Upgrade skinview3d

This commit is contained in:
Haowei Wen 2023-02-09 22:40:51 +00:00
parent 950f2e6c7a
commit 5a5dbea05b
7 changed files with 139 additions and 148 deletions

View File

@ -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",

View File

@ -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> = (props) => {
const viewRef: React.MutableRefObject<skinview3d.SkinViewer> = useRef(null!)
const containerRef = useRef<HTMLCanvasElement>(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> = (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) => {
}, [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> = (props) => {
</h3>
<div>
<ActionButton
className={`fas fa-${running ? 'walking' : 'running'}`}
className={`fas fa-tablet ${props.cape ? '' : 'd-none'}`}
data-toggle="tooltip"
data-placement="bottom"
title={`${t('general.walk')} / ${t('general.run')}`}
onClick={toggleRun}
title={t('general.switchCapeElytra')}
onClick={toggleBackEquippment}
></ActionButton>
<ActionButton
className="fas fa-redo-alt"
className={`fas fa-person-running`}
data-toggle="tooltip"
data-placement="bottom"
title={t('general.rotation')}
onClick={toggleRotate}
title={t('general.switchAnimation')}
onClick={toggleAnimation}
></ActionButton>
<ActionButton
className={`fas fa-${paused ? 'play' : 'pause'}`}
data-toggle="tooltip"
data-placement="bottom"
title={t('general.pause')}
title={
paused
? t('general.playAnimation')
: t('general.pauseAnimation')
}
onClick={togglePause}
></ActionButton>
<ActionButton
className="fas fa-stop"
className="fas fa-rotate-right"
data-toggle="tooltip"
data-placement="bottom"
title={t('general.reset')}
onClick={handleReset}
title={t('general.rotation')}
onClick={toggleRotate}
></ActionButton>
</div>
</div>

View File

@ -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

View File

@ -49,33 +49,27 @@ describe('indicator', () => {
})
describe('actions', () => {
it('toggle run', () => {
const { getByTitle } = render(<Viewer isAlex={false} />)
fireEvent.click(getByTitle(`${t('general.walk')} / ${t('general.run')}`))
it('toggle animation', () => {
const component = <Viewer isAlex={false} />
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(<Viewer isAlex={false} />)
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(<Viewer isAlex={false} />)
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(<Viewer isAlex={false} />)
fireEvent.click(getByTitle(t('general.reset')))
})
it('reset when running', () => {
const { getByTitle } = render(<Viewer isAlex={false} />)
fireEvent.click(getByTitle(`${t('general.walk')} / ${t('general.run')}`))
fireEvent.click(getByTitle(t('general.reset')))
})
})
describe('background', () => {

View File

@ -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.

View File

@ -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: 页面已过期,请刷新页面。

View File

@ -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"