'use client'; import { useEffect, useRef, useState } from 'react'; import { SkinViewer as SkinViewer3D, WalkingAnimation, RunningAnimation, FlyingAnimation, IdleAnimation } from 'skinview3d'; interface SkinViewerProps { skinUrl: string; capeUrl?: string; isSlim?: boolean; width?: number; height?: number; className?: string; autoRotate?: boolean; walking?: boolean; running?: boolean; // 新增:跑步动画 jumping?: boolean; // 新增:跳跃动画 rotation?: boolean; // 新增:旋转控制 isExternalPreview?: boolean; // 新增:是否为外部预览 } export default function SkinViewer({ skinUrl, capeUrl, isSlim = false, width = 300, height = 300, className = '', autoRotate = true, walking = false, running = false, jumping = false, rotation = true, isExternalPreview = false, // 新增:默认为false }: SkinViewerProps) { const canvasRef = useRef(null); const viewerRef = useRef(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; // 完全禁用自动旋转 // 外部预览时禁用所有动画和旋转 if (isExternalPreview) { viewer.autoRotate = false; viewer.controls.enableRotate = false; // 禁用旋转控制 viewer.controls.enableZoom = false; // 禁用缩放 } else { viewer.autoRotate = autoRotate && !walking && !running && !jumping; viewer.controls.enableRotate = rotation; // 根据参数控制旋转 viewer.controls.enableZoom = true; // 启用缩放 } 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, running, jumping, rotation, imageLoaded, hasError, isExternalPreview]); // 控制动画效果 - 参考 Blessing Skin 的实现 useEffect(() => { if (!viewerRef.current) return; const viewer = viewerRef.current; // 外部预览时只使用静止动画,禁用所有其他动画 if (isExternalPreview) { viewer.animation = new IdleAnimation(); viewer.autoRotate = false; console.log('外部预览模式:启用静止动画,禁用旋转'); } else { // 根据优先级设置动画 - 参考 Blessing Skin 的 animationFactories if (running) { // 跑步动画 viewer.animation = new RunningAnimation(); console.log('启用跑步动画'); } else if (walking) { // 普通步行动画 viewer.animation = new WalkingAnimation(); console.log('启用步行动画'); } else if (jumping) { // 飞行动画作为跳跃 viewer.animation = new FlyingAnimation(); console.log('启用跳跃动画'); } else { // 静止动画 viewer.animation = new IdleAnimation(); console.log('启用静止动画'); } // 更新自动旋转状态 viewer.autoRotate = autoRotate && !walking && !running && !jumping; } }, [walking, running, jumping, autoRotate, isExternalPreview]); // 当皮肤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.resetCape(); } 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 (
⚠️
皮肤加载失败
图片链接可能无效或存在访问限制
); } // 加载状态显示 if (isLoading) { return (
加载中...
); } return ( ); }