From 5a5dbea05b5be3ed0a1de7d0ea120ce865d97ea3 Mon Sep 17 00:00:00 2001 From: Haowei Wen Date: Thu, 9 Feb 2023 22:40:51 +0000 Subject: [PATCH] Upgrade skinview3d --- package.json | 2 +- resources/assets/src/components/Viewer.tsx | 129 +++++++++--------- .../assets/tests/__mocks__/skinview3d.ts | 63 ++++----- .../assets/tests/components/Viewer.test.tsx | 26 ++-- resources/lang/en/front-end.yml | 12 +- resources/lang/zh_CN/front-end.yml | 10 +- yarn.lock | 45 +++--- 7 files changed, 139 insertions(+), 148 deletions(-) diff --git a/package.json b/package.json index 1754df06..c150b3db 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "reaptcha": "^1.7.2", "rxjs": "^6.5.5", "skinview-utils": "^0.5.5", - "skinview3d": "^2.2.1", + "skinview3d": "^3.0.0-alpha.1", "spectre.css": "^0.5.8", "use-immer": "^0.4.2", "xterm": "^4.6.0", diff --git a/resources/assets/src/components/Viewer.tsx b/resources/assets/src/components/Viewer.tsx index 2cfcc100..6ed0e216 100644 --- a/resources/assets/src/components/Viewer.tsx +++ b/resources/assets/src/components/Viewer.tsx @@ -26,17 +26,12 @@ interface Props { initPositionZ?: number } -type AnimationHandles = { - walk: skinview3d.SubAnimationHandle | null - run: skinview3d.SubAnimationHandle | null - rotate: skinview3d.SubAnimationHandle | null -} - -const animationHandles: AnimationHandles = { - walk: null, - run: null, - rotate: null, -} +const animationFactories = [ + () => new skinview3d.WalkingAnimation(), + () => new skinview3d.RunningAnimation(), + () => new skinview3d.FlyingAnimation(), + () => new skinview3d.IdleAnimation(), +] const ActionButton = styled.i` display: inline; @@ -63,10 +58,9 @@ const Viewer: React.FC = (props) => { const viewRef: React.MutableRefObject = useRef(null!) const containerRef = useRef(null) - const animationHandlesRef = useRef(animationHandles) const [paused, setPaused] = useState(false) - const [running, setRunning] = useState(false) + const [animation, setAnimation] = useState(0) const [bgPicture, setBgPicture] = useState(-1) const indicator = (() => { @@ -83,36 +77,34 @@ const Viewer: React.FC = (props) => { useEffect(() => { const container = containerRef.current! - const viewer = new skinview3d.FXAASkinViewer({ + const viewer = new skinview3d.SkinViewer({ canvas: container, width: container.clientWidth, height: container.clientHeight, skin: props.skin || SkinSteve, - cape: props.cape || '', + cape: props.cape || undefined, model: props.isAlex ? 'slim' : 'default', zoom: initPositionZ / 100, }) + viewer.autoRotate = true if (document.body.classList.contains('dark-mode')) { viewer.background = '#6c757d' } - const rotate = viewer.animations.add(skinview3d.RotatingAnimation) - animationHandlesRef.current.rotate = rotate - - const control = skinview3d.createOrbitControls(viewer) - viewRef.current = viewer return () => { - control.dispose() viewer.dispose() } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []) useEffect(() => { const viewer = viewRef.current - viewer.loadSkin(props.skin || SkinSteve, props.isAlex ? 'slim' : 'default') + viewer.loadSkin(props.skin || SkinSteve, { + model: props.isAlex ? 'slim' : 'default', + }) }, [props.skin, props.isAlex]) useEffect(() => { @@ -125,52 +117,63 @@ const Viewer: React.FC = (props) => { }, [props.cape]) useEffect(() => { - const handles = animationHandlesRef.current - if (running) { - handles.walk?.resetAndRemove() - handles.walk = null - - const run = viewRef.current.animations.add(skinview3d.RunningAnimation) - run.speed = 0.6 - handles.run = run + const viewer = viewRef.current + const factory = animationFactories[animation] + if (factory === undefined) { + viewer.animation = null } else { - handles.run?.resetAndRemove() - handles.run = null - - const walk = viewRef.current.animations.add(skinview3d.WalkingAnimation) - handles.walk = walk + const newAnimation = factory() + newAnimation.paused = paused // Perseve `paused` state + viewer.animation = newAnimation } - }, [running]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [animation]) useEffect(() => { - viewRef.current.animations.paused = paused + const currentAnimation = viewRef.current.animation + if (currentAnimation !== null) { + currentAnimation.paused = paused + } }, [paused]) useEffect(() => { - viewRef.current.loadBackground(backgrounds[bgPicture]!) + const viewer = viewRef.current + const backgroundUrl = backgrounds[bgPicture] + if (backgroundUrl === undefined) { + viewer.background = null + } else { + viewer.loadBackground(backgroundUrl) + } }, [bgPicture]) const togglePause = () => { - setPaused((paused) => !paused) + setPaused((paused) => { + if (paused) { + return false + } else { + viewRef.current.autoRotate = false + return true + } + }) } - const toggleRun = () => { - setRunning((running) => !running) + const toggleAnimation = () => { + setAnimation((index) => (index + 1) % animationFactories.length) + setPaused(false) } const toggleRotate = () => { - const handles = animationHandlesRef.current - if (handles.rotate) { - handles.rotate.paused = !handles.rotate.paused - } + const viewer = viewRef.current + viewer.autoRotate = !viewer.autoRotate } - const handleReset = () => { - const handles = animationHandlesRef.current - handles.walk?.resetAndRemove() - handles.run?.resetAndRemove() - handles.rotate?.resetAndRemove() - viewRef.current.animations.paused = true + const toggleBackEquippment = () => { + const player = viewRef.current.playerObject + if (player.backEquipment === 'cape') { + player.backEquipment = 'elytra' + } else { + player.backEquipment = 'cape' + } } const setWhite = () => { @@ -213,32 +216,36 @@ const Viewer: React.FC = (props) => {
diff --git a/resources/assets/tests/__mocks__/skinview3d.ts b/resources/assets/tests/__mocks__/skinview3d.ts index 8054e86b..ba7e074b 100644 --- a/resources/assets/tests/__mocks__/skinview3d.ts +++ b/resources/assets/tests/__mocks__/skinview3d.ts @@ -1,24 +1,32 @@ /* eslint-disable max-params */ /* eslint-disable max-classes-per-file */ -import type { PlayerObject, SkinObject, CapeObject } from 'skinview3d' +import type { + PlayerObject, + SkinObject, + CapeObject, + EarsObject, +} from 'skinview3d' -export class FXAASkinViewer { +export class SkinViewer { disposed = false - background = '' - animations = new RootAnimation() - animationPaused = false + background = null + animation = null + autoRotate = false + autoRotateSpeed = 1.0 playerObject: PlayerObject constructor() { - this.animationPaused = false this.playerObject = { skin: {} as SkinObject, cape: {} as CapeObject, + ears: {} as EarsObject, + backEquipment: 'cape', } as PlayerObject } loadSkin() {} + resetSkin() {} loadCape() {} resetCape() {} loadBackground() {} @@ -28,44 +36,19 @@ export class FXAASkinViewer { } } -export class RootAnimation { +export class PlayerAnimation { + speed = 1.0 paused = false - - add(animation: unknown) { - return animation - } + progress = 0 } -export function createOrbitControls() { - return { - dispose() {}, - } -} +export class IdleAnimation extends PlayerAnimation {} -export const WalkingAnimation = new Proxy( - {}, - { - get() { - return jest.fn() - }, - }, -) -export const RunningAnimation = new Proxy( - {}, - { - get() { - return jest.fn() - }, - }, -) -export const RotatingAnimation = new Proxy( - {}, - { - get() { - return jest.fn() - }, - }, -) +export class WalkingAnimation extends PlayerAnimation {} + +export class RunningAnimation extends PlayerAnimation {} + +export class FlyingAnimation extends PlayerAnimation {} export function isSlimSkin() { return false diff --git a/resources/assets/tests/components/Viewer.test.tsx b/resources/assets/tests/components/Viewer.test.tsx index 1274a6e7..33894900 100644 --- a/resources/assets/tests/components/Viewer.test.tsx +++ b/resources/assets/tests/components/Viewer.test.tsx @@ -49,33 +49,27 @@ describe('indicator', () => { }) describe('actions', () => { - it('toggle run', () => { - const { getByTitle } = render() - fireEvent.click(getByTitle(`${t('general.walk')} / ${t('general.run')}`)) + it('toggle animation', () => { + const component = + const { getByTitle } = render(component) + fireEvent.click(getByTitle(`${t('general.switchAnimation')}`)) // should start running + fireEvent.click(getByTitle(`${t('general.switchAnimation')}`)) // should start flying + fireEvent.click(getByTitle(`${t('general.switchAnimation')}`)) // should be idle + fireEvent.click(getByTitle(`${t('general.switchAnimation')}`)) // should start walking }) it('toggle rotation', () => { const { getByTitle } = render() - fireEvent.click(getByTitle(t('general.rotation'))) + fireEvent.click(getByTitle(t('general.rotation'))) // should stop rotation + fireEvent.click(getByTitle(t('general.rotation'))) // should start rotation }) it('toggle pause', () => { const { getByTitle } = render() - const icon = getByTitle(t('general.pause')) + const icon = getByTitle(t('general.pauseAnimation')) fireEvent.click(icon) expect(icon).toHaveClass('fa-play') }) - - it('reset', () => { - const { getByTitle } = render() - fireEvent.click(getByTitle(t('general.reset'))) - }) - - it('reset when running', () => { - const { getByTitle } = render() - fireEvent.click(getByTitle(`${t('general.walk')} / ${t('general.run')}`)) - fireEvent.click(getByTitle(t('general.reset'))) - }) }) describe('background', () => { diff --git a/resources/lang/en/front-end.yml b/resources/lang/en/front-end.yml index 9808ea70..f7358552 100644 --- a/resources/lang/en/front-end.yml +++ b/resources/lang/en/front-end.yml @@ -62,7 +62,7 @@ skinlib: setPublicNotice: Sure to set this as public texture? setPrivateNotice: Sure to set this as private texture? deleteNotice: Are you sure to delete this texture? - setNewTextureModel: "Please select a new texture model:" + setNewTextureModel: 'Please select a new texture model:' upload: texture-name: Texture Name texture-type: Texture Type @@ -248,11 +248,11 @@ general: tip: Tip noResult: No result. texturePreview: Texture Preview - walk: Walk - run: Run - rotation: Rotation - pause: Pause - reset: Reset + rotation: Toggle rotation + playAnimation: Play animation + pauseAnimation: Pause animation + switchAnimation: Switch animation + switchCapeElytra: Switch Cape / Elytra skinlib: Skin Library wait: Please wait... csrf: This page is out-dated. Please refresh it. diff --git a/resources/lang/zh_CN/front-end.yml b/resources/lang/zh_CN/front-end.yml index f00bd749..331700d6 100644 --- a/resources/lang/zh_CN/front-end.yml +++ b/resources/lang/zh_CN/front-end.yml @@ -62,7 +62,7 @@ skinlib: setPublicNotice: 要将此材质设置为公开吗? setPrivateNotice: 要将此材质设置为私有吗? deleteNotice: 真的要删除此材质吗? - setNewTextureModel: "请选择新的材质适用模型:" + setNewTextureModel: '请选择新的材质适用模型:' upload: texture-name: 材质名称 texture-type: 材质类型 @@ -242,11 +242,11 @@ general: tip: 提示 noResult: 无结果 texturePreview: 材质预览 - walk: 行走 - run: 奔跑 rotation: 旋转 - pause: 暂停 - reset: 重置 + playAnimation: 播放动画 + pauseAnimation: 暂停动画 + switchAnimation: 切换动画 + switchCapeElytra: 切换披风 / 鞘翅 skinlib: 皮肤库 wait: 请稍等... csrf: 页面已过期,请刷新页面。 diff --git a/yarn.lock b/yarn.lock index 3c1f02d9..90a9893c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1744,10 +1744,12 @@ dependencies: "@types/jest" "*" -"@types/three@^0.136.1": - version "0.136.1" - resolved "https://registry.npmjs.org/@types/three/-/three-0.136.1.tgz#030fc01cdc5ce82cad93db17b94a24515cb4b50e" - integrity sha512-gzTw6RR4dU8sGf+RpLBWWKHRVIJ4gwKVQPk+IFCgha04Efg/itXYUalX6iBcYeSmaDu0qE5M7uTq0XLQpU4FAg== +"@types/three@^0.142.0": + version "0.142.0" + resolved "https://registry.yarnpkg.com/@types/three/-/three-0.142.0.tgz#32ca897afc925057174a398416777fcb141666d6" + integrity sha512-YNVnlqthfgqsxKuSNbT/G2r+ha/6x+aJm7kvjT+sxa4jUROw/KOPm5xzGj33sk1+Librg6NwH/wJ6LCDX33UZg== + dependencies: + "@types/webxr" "*" "@types/tween.js@^18.5.0": version "18.5.0" @@ -1793,6 +1795,11 @@ "@types/webpack-sources" "*" source-map "^0.6.0" +"@types/webxr@*": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.1.tgz#4908349419104bd476a4252d04e4c3abb496748d" + integrity sha512-xlFXPfgJR5vIuDefhaHuUM9uUgvPaXB6GKdXy2gdEh8gBWQZ2ul24AJz3foUd8NNKlSTQuWYJpCb1/pL81m1KQ== + "@types/yargs-parser@*": version "13.1.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228" @@ -8815,21 +8822,21 @@ skinview-utils@^0.5.5: resolved "https://registry.npmjs.org/skinview-utils/-/skinview-utils-0.5.5.tgz#55671db8db97f82df0f87e1c8ec95bd58e861bc8" integrity sha512-lApkcJuGQhK9j4oGeaPTjAx/WiRMduunho7RUoBbP+ceOCbncHogQNkfwbil7pjgvyy8S82pBdfzOQggjbZMdg== -skinview-utils@^0.6.2: - version "0.6.2" - resolved "https://registry.npmjs.org/skinview-utils/-/skinview-utils-0.6.2.tgz#1247aef71c472e963fd3f0db3494f18d63e31e51" - integrity sha512-UdjWwXCVZobtG+dc7ilvMRbtXYSqPJtWKPFdgWc44Gs4aoOmZML2lErr77h7uussXo9zrcR+fPizGshGpdETvQ== +skinview-utils@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/skinview-utils/-/skinview-utils-0.7.0.tgz#84164d06ff4666c731288ebb7783b7be964bda0c" + integrity sha512-ecbbUp0AuvZX5fCIOOwbNPxr/JIbrAGhCcdLcww0Ov9PbvAbeYjNDSIu1ebCJBe4CWc6ZYYn8MFNp68DDta0iQ== dependencies: "@types/offscreencanvas" "^2019.6.4" -skinview3d@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/skinview3d/-/skinview3d-2.2.1.tgz#f66f29a35055d378a5949c72512e2c6eddaad02a" - integrity sha512-VOzB2jcsXWTmyEqE6nIXhECg1+29ZcZjP0tw9mFIGN9Y4P/hDlAt2ev7NT7J0Sqgei5UJenkorW24VDZHCepKw== +skinview3d@^3.0.0-alpha.1: + version "3.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/skinview3d/-/skinview3d-3.0.0-alpha.1.tgz#48f43243a96ddebba9b3657e1ebcc5bd45e20638" + integrity sha512-Y5v5yxQIVZZwpQlnRHeVZ9pgDOETLkPrkeMcwJN92OFAYQ0+WTHy6WlZOu0M1DRys+h8iyBdxRb/9976ftt72Q== dependencies: - "@types/three" "^0.136.1" - skinview-utils "^0.6.2" - three "^0.136.0" + "@types/three" "^0.142.0" + skinview-utils "^0.7.0" + three "^0.142.0" slash@^2.0.0: version "2.0.0" @@ -9404,10 +9411,10 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -three@^0.136.0: - version "0.136.0" - resolved "https://registry.npmjs.org/three/-/three-0.136.0.tgz#b1504db021b46398ef468aa7849f3dcabb814f50" - integrity sha512-+fEMX7nYLz2ZesVP/dyifli5Jf8gR3XPAnFJveQ80aMhibFduzrADnjMbARXh8+W9qLK7rshJCjAIL/6cDxC+A== +three@^0.142.0: + version "0.142.0" + resolved "https://registry.yarnpkg.com/three/-/three-0.142.0.tgz#89e226a16221f212eb1d40f0786604b711f28aed" + integrity sha512-ESjPO+3geFr+ZUfVMpMnF/eVU2uJPOh0e2ZpMFqjNca1wApS9lJb7E4MjwGIczgt9iuKd8PEm6Pfgp2bJ92Xtg== throat@^6.0.1: version "6.0.1"