ne minor, a ebat fix
This commit is contained in:
@ -9,6 +9,9 @@ interface SkinViewerProps {
|
||||
autoRotate?: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_SKIN =
|
||||
'https://static.planetminecraft.com/files/resource_media/skin/original-steve-15053860.png';
|
||||
|
||||
export default function SkinViewer({
|
||||
width = 300,
|
||||
height = 400,
|
||||
@ -19,57 +22,111 @@ export default function SkinViewer({
|
||||
}: SkinViewerProps) {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const viewerRef = useRef<any>(null);
|
||||
const animRef = useRef<any>(null);
|
||||
|
||||
// 1) Инициализируем viewer ОДИН РАЗ
|
||||
useEffect(() => {
|
||||
if (!canvasRef.current) return;
|
||||
let disposed = false;
|
||||
|
||||
const init = async () => {
|
||||
if (!canvasRef.current || viewerRef.current) return;
|
||||
|
||||
// Используем динамический импорт для обхода проблемы ESM/CommonJS
|
||||
const initSkinViewer = async () => {
|
||||
try {
|
||||
const skinview3d = await import('skinview3d');
|
||||
if (disposed) return;
|
||||
|
||||
// Создаем просмотрщик скина по документации
|
||||
const viewer = new skinview3d.SkinViewer({
|
||||
canvas: canvasRef.current,
|
||||
width,
|
||||
height,
|
||||
skin:
|
||||
skinUrl ||
|
||||
'https://static.planetminecraft.com/files/resource_media/skin/original-steve-15053860.png',
|
||||
model: 'auto-detect',
|
||||
cape: capeUrl || undefined,
|
||||
});
|
||||
|
||||
// Настраиваем вращение
|
||||
// базовая настройка
|
||||
viewer.autoRotate = autoRotate;
|
||||
|
||||
// Настраиваем анимацию ходьбы
|
||||
viewer.animation = new skinview3d.WalkingAnimation();
|
||||
viewer.animation.speed = walkingSpeed;
|
||||
// анимация ходьбы
|
||||
const walking = new skinview3d.WalkingAnimation();
|
||||
walking.speed = walkingSpeed;
|
||||
viewer.animation = walking;
|
||||
|
||||
// Сохраняем экземпляр для очистки
|
||||
viewerRef.current = viewer;
|
||||
} catch (error) {
|
||||
console.error('Ошибка при инициализации skinview3d:', error);
|
||||
animRef.current = walking;
|
||||
|
||||
// выставляем ресурсы сразу
|
||||
const finalSkin = skinUrl?.trim() ? skinUrl : DEFAULT_SKIN;
|
||||
await viewer.loadSkin(finalSkin);
|
||||
|
||||
if (capeUrl?.trim()) {
|
||||
await viewer.loadCape(capeUrl);
|
||||
} else {
|
||||
viewer.cape = null;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Ошибка при инициализации skinview3d:', e);
|
||||
}
|
||||
};
|
||||
|
||||
initSkinViewer();
|
||||
init();
|
||||
|
||||
// Очистка при размонтировании
|
||||
return () => {
|
||||
disposed = true;
|
||||
if (viewerRef.current) {
|
||||
viewerRef.current.dispose();
|
||||
viewerRef.current = null;
|
||||
animRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [width, height, skinUrl, capeUrl, walkingSpeed, autoRotate]);
|
||||
// ⚠️ пустой deps — создаём один раз
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
width={width}
|
||||
height={height}
|
||||
style={{ display: 'block' }}
|
||||
/>
|
||||
);
|
||||
// 2) Обновляем размеры (не пересоздаём viewer)
|
||||
useEffect(() => {
|
||||
const viewer = viewerRef.current;
|
||||
if (!viewer) return;
|
||||
viewer.width = width;
|
||||
viewer.height = height;
|
||||
}, [width, height]);
|
||||
|
||||
// 3) Обновляем автоповорот
|
||||
useEffect(() => {
|
||||
const viewer = viewerRef.current;
|
||||
if (!viewer) return;
|
||||
viewer.autoRotate = autoRotate;
|
||||
}, [autoRotate]);
|
||||
|
||||
// 4) Обновляем скорость анимации
|
||||
useEffect(() => {
|
||||
const walking = animRef.current;
|
||||
if (!walking) return;
|
||||
walking.speed = walkingSpeed;
|
||||
}, [walkingSpeed]);
|
||||
|
||||
// 5) Обновляем скин
|
||||
useEffect(() => {
|
||||
const viewer = viewerRef.current;
|
||||
if (!viewer) return;
|
||||
|
||||
const finalSkin = skinUrl?.trim() ? skinUrl : DEFAULT_SKIN;
|
||||
|
||||
// защита от кеша: добавим “bust” только если URL уже имеет query — не обязательно, но помогает
|
||||
const url = finalSkin.includes('?') ? `${finalSkin}&t=${Date.now()}` : `${finalSkin}?t=${Date.now()}`;
|
||||
|
||||
viewer.loadSkin(url).catch((e: any) => console.error('loadSkin error:', e));
|
||||
}, [skinUrl]);
|
||||
|
||||
// 6) Обновляем плащ
|
||||
useEffect(() => {
|
||||
const viewer = viewerRef.current;
|
||||
if (!viewer) return;
|
||||
|
||||
if (capeUrl?.trim()) {
|
||||
const url = capeUrl.includes('?') ? `${capeUrl}&t=${Date.now()}` : `${capeUrl}?t=${Date.now()}`;
|
||||
viewer.loadCape(url).catch((e: any) => console.error('loadCape error:', e));
|
||||
} else {
|
||||
viewer.cape = null;
|
||||
}
|
||||
}, [capeUrl]);
|
||||
|
||||
return <canvas ref={canvasRef} width={width} height={height} style={{ display: 'block' }} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user