feat: 完成navbar隐藏优化和侧边栏冻结功能

- 优化navbar滚动隐藏逻辑,更敏感响应
- 添加返回顶部按钮,固定在右下角
- 实现profile页面侧边栏真正冻结效果
- 修复首页滑动指示器位置
- 优化整体布局确保首屏内容完整显示
This commit is contained in:
Wuying Created Local Users
2025-12-04 20:05:13 +08:00
parent 570e864e06
commit 5f90f48a1c
25 changed files with 7493 additions and 118 deletions

View File

@@ -0,0 +1,218 @@
'use client';
import { useEffect, useRef, useState } from 'react';
import { SkinViewer as SkinViewer3D, WalkingAnimation, FunctionAnimation } from 'skinview3d';
interface SkinViewerProps {
skinUrl: string;
capeUrl?: string;
isSlim?: boolean;
width?: number;
height?: number;
className?: string;
autoRotate?: boolean;
walking?: boolean;
}
export default function SkinViewer({
skinUrl,
capeUrl,
isSlim = false,
width = 300,
height = 300,
className = '',
autoRotate = true,
walking = false,
}: SkinViewerProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const viewerRef = useRef<SkinViewer3D | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
const [imageLoaded, setImageLoaded] = useState(false);
// 预加载皮肤图片以检查是否可访问
useEffect(() => {
if (!skinUrl) return;
setIsLoading(true);
setHasError(false);
setImageLoaded(false);
const img = new Image();
img.crossOrigin = 'anonymous'; // 尝试跨域访问
img.onload = () => {
console.log('皮肤图片加载成功:', skinUrl);
setImageLoaded(true);
setIsLoading(false);
};
img.onerror = (error) => {
console.error('皮肤图片加载失败:', skinUrl, error);
setHasError(true);
setIsLoading(false);
};
// 开始加载图片
img.src = skinUrl;
return () => {
// 清理
img.onload = null;
img.onerror = null;
};
}, [skinUrl]);
// 初始化3D查看器
useEffect(() => {
if (!canvasRef.current || !imageLoaded || hasError) return;
try {
console.log('初始化3D皮肤查看器:', { skinUrl, isSlim, width, height });
// 使用canvas的实际尺寸参考blessingskin
const canvas = canvasRef.current;
const viewer = new SkinViewer3D({
canvas: canvas,
width: canvas.clientWidth || width,
height: canvas.clientHeight || height,
skin: skinUrl,
cape: capeUrl,
model: isSlim ? 'slim' : 'default',
zoom: 1.0, // 使用blessingskin的zoom方式
});
viewerRef.current = viewer;
// 设置背景和控制选项 - 参考blessingskin
viewer.background = null; // 透明背景
viewer.autoRotate = false; // 禁用自动旋转
// 禁用所有交互控制
viewer.controls.enableRotate = false; // 禁用旋转控制
viewer.controls.enableZoom = false; // 禁用缩放
viewer.controls.enablePan = false; // 禁用平移
console.log('3D皮肤查看器初始化成功');
} catch (error) {
console.error('3D皮肤查看器初始化失败:', error);
setHasError(true);
}
// 清理函数
return () => {
if (viewerRef.current) {
try {
viewerRef.current.dispose();
viewerRef.current = null;
console.log('3D皮肤查看器已清理');
} catch (error) {
console.error('清理3D皮肤查看器失败:', error);
}
}
};
}, [skinUrl, capeUrl, isSlim, width, height, autoRotate, walking, imageLoaded, hasError]);
// 当皮肤URL改变时更新
useEffect(() => {
if (viewerRef.current && skinUrl && imageLoaded) {
try {
console.log('更新皮肤URL:', skinUrl);
viewerRef.current.loadSkin(skinUrl);
} catch (error) {
console.error('更新皮肤失败:', error);
}
}
}, [skinUrl, imageLoaded]);
// 监听容器尺寸变化并调整viewer大小 - 参考blessingskin
useEffect(() => {
if (viewerRef.current && canvasRef.current) {
const rect = canvasRef.current.getBoundingClientRect();
viewerRef.current.setSize(rect.width, rect.height);
}
}, [imageLoaded]); // 当图片加载完成后调整尺寸
// 当披风URL改变时更新
useEffect(() => {
if (viewerRef.current && capeUrl && imageLoaded) {
try {
console.log('更新披风URL:', capeUrl);
viewerRef.current.loadCape(capeUrl);
} catch (error) {
console.error('更新披风失败:', error);
}
} else if (viewerRef.current && !capeUrl && imageLoaded) {
try {
viewerRef.current.loadCape(null);
} catch (error) {
console.error('移除披风失败:', error);
}
}
}, [capeUrl, imageLoaded]);
// 当模型类型改变时更新
useEffect(() => {
if (viewerRef.current && skinUrl && imageLoaded) {
try {
console.log('更新模型类型:', isSlim ? 'slim' : 'default');
viewerRef.current.loadSkin(skinUrl, { model: isSlim ? 'slim' : 'default' });
} catch (error) {
console.error('更新模型失败:', error);
}
}
}, [isSlim, skinUrl, imageLoaded]);
// 错误状态显示
if (hasError) {
return (
<div
className={`${className} flex items-center justify-center bg-gradient-to-br from-red-50 to-orange-50 dark:from-red-900/20 dark:to-orange-900/20 rounded-xl border-2 border-dashed border-red-300 dark:border-red-700`}
style={{ width: width, height: height }}
>
<div className="text-center p-4">
<div className="text-4xl mb-2"></div>
<div className="text-sm text-red-600 dark:text-red-400 font-medium mb-1">
</div>
<div className="text-xs text-red-500 dark:text-red-500">
访
</div>
</div>
</div>
);
}
// 加载状态显示
if (isLoading) {
return (
<div
className={`${className} flex items-center justify-center bg-gradient-to-br from-orange-50 to-amber-50 dark:from-gray-700 dark:to-gray-600 rounded-xl`}
style={{ width: width, height: height }}
>
<div className="text-center">
<div className="animate-spin w-8 h-8 border-3 border-orange-500 border-t-transparent rounded-full mx-auto mb-3"></div>
<div className="text-sm text-orange-600 dark:text-orange-400 font-medium">
...
</div>
</div>
</div>
);
}
return (
<canvas
ref={canvasRef}
className={className}
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '100%',
height: '100%'
}}
/>
);
}