[3d] animation node UI change (#2347)

This commit is contained in:
Terry Jia
2025-01-25 11:41:05 -05:00
committed by GitHub
parent 652125de1f
commit 28b163cdd5
2 changed files with 141 additions and 99 deletions

View File

@@ -258,15 +258,6 @@ app.registerExtension({
modelWidget.value = ''
}
const animationSelect = node.widgets?.find(
(w: IWidget) => w.name === 'animation'
)
if (animationSelect) {
animationSelect.options.values = []
animationSelect.value = ''
}
const speedSelect = node.widgets?.find(
(w: IWidget) => w.name === 'animation_speed'
)
@@ -276,44 +267,6 @@ app.registerExtension({
}
})
node.addWidget(
'button',
'Play/Pause Animation',
'toggle_animation',
() => {
load3d.toggleAnimation()
}
)
const animationSelect = node.addWidget(
'combo',
'animation',
'',
() => '',
{
values: []
}
) as IWidget
animationSelect.callback = (value: string) => {
const names = load3d.getAnimationNames()
const index = names.indexOf(value)
if (index !== -1) {
const wasPlaying = load3d.isAnimationPlaying
if (wasPlaying) {
load3d.toggleAnimation(false)
}
load3d.updateSelectedAnimation(index)
if (wasPlaying) {
load3d.toggleAnimation(true)
}
}
}
return {
widget: node.addDOMWidget(inputName, 'LOAD_3D_ANIMATION', container)
}
@@ -379,20 +332,7 @@ app.registerExtension({
lightIntensity,
upDirection,
fov,
cameraState,
(load3d: Load3d) => {
const animationLoad3d = load3d as Load3dAnimation
const names = animationLoad3d.getAnimationNames()
const animationSelect = node.widgets.find(
(w: IWidget) => w.name === 'animation'
)
animationSelect.options.values = names
if (names.length) {
animationSelect.value = names[0]
}
}
cameraState
)
const w = node.widgets.find((w: IWidget) => w.name === 'width')
@@ -592,44 +532,6 @@ app.registerExtension({
load3d.renderer.domElement.hidden = this.flags.collapsed ?? false
}
node.addWidget(
'button',
'Play/Pause Animation',
'toggle_animation',
() => {
load3d.toggleAnimation()
}
)
const animationSelect = node.addWidget(
'combo',
'animation',
'',
() => '',
{
values: []
}
) as IWidget
animationSelect.callback = (value: string) => {
const names = load3d.getAnimationNames()
const index = names.indexOf(value)
if (index !== -1) {
const wasPlaying = load3d.isAnimationPlaying
if (wasPlaying) {
load3d.toggleAnimation(false)
}
load3d.updateSelectedAnimation(index)
if (wasPlaying) {
load3d.toggleAnimation(true)
}
}
}
return {
widget: node.addDOMWidget(
inputName,

View File

@@ -10,9 +10,116 @@ class Load3dAnimation extends Load3d {
isAnimationPlaying: boolean = false
animationSpeed: number = 1.0
playPauseContainer: HTMLDivElement = {} as HTMLDivElement
animationSelect: HTMLSelectElement = {} as HTMLSelectElement
constructor(container: Element | HTMLElement) {
super(container)
this.createPlayPauseButton(container)
this.createAnimationList(container)
}
createAnimationList(container: Element | HTMLElement) {
this.animationSelect = document.createElement('select')
Object.assign(this.animationSelect.style, {
position: 'absolute',
top: '3px',
left: '50%',
transform: 'translateX(15px)',
width: '90px',
height: '20px',
backgroundColor: 'rgba(0, 0, 0, 0.3)',
color: 'white',
border: 'none',
borderRadius: '4px',
fontSize: '12px',
padding: '0 8px',
cursor: 'pointer',
display: 'none',
outline: 'none'
})
this.animationSelect.addEventListener('mouseenter', () => {
this.animationSelect.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'
})
this.animationSelect.addEventListener('mouseleave', () => {
this.animationSelect.style.backgroundColor = 'rgba(0, 0, 0, 0.3)'
})
this.animationSelect.addEventListener('change', (event) => {
const select = event.target as HTMLSelectElement
this.updateSelectedAnimation(select.selectedIndex)
})
container.appendChild(this.animationSelect)
}
updateAnimationList() {
this.animationSelect.innerHTML = ''
this.animationClips.forEach((clip, index) => {
const option = document.createElement('option')
option.value = index.toString()
option.text = clip.name || `Animation ${index + 1}`
option.selected = index === this.selectedAnimationIndex
this.animationSelect.appendChild(option)
})
}
createPlayPauseButton(container: Element | HTMLElement) {
this.playPauseContainer = document.createElement('div')
this.playPauseContainer.style.position = 'absolute'
this.playPauseContainer.style.top = '3px'
this.playPauseContainer.style.left = '50%'
this.playPauseContainer.style.transform = 'translateX(-50%)'
this.playPauseContainer.style.width = '20px'
this.playPauseContainer.style.height = '20px'
this.playPauseContainer.style.cursor = 'pointer'
this.playPauseContainer.style.alignItems = 'center'
this.playPauseContainer.style.justifyContent = 'center'
const updateButtonState = () => {
const icon = this.playPauseContainer.querySelector('svg')
if (icon) {
if (this.isAnimationPlaying) {
icon.innerHTML = `
<path d="M6 4h4v16H6zM14 4h4v16h-4z"/>
`
this.playPauseContainer.title = 'Pause Animation'
} else {
icon.innerHTML = `
<path d="M8 5v14l11-7z"/>
`
this.playPauseContainer.title = 'Play Animation'
}
}
}
const playIcon = document.createElement('div')
playIcon.innerHTML = `
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
<path d="M8 5v14l11-7z"/>
</svg>
`
this.playPauseContainer.addEventListener('mouseenter', () => {
this.playPauseContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'
})
this.playPauseContainer.addEventListener('mouseleave', () => {
this.playPauseContainer.style.backgroundColor = 'transparent'
})
this.playPauseContainer.addEventListener('click', (event) => {
event.stopPropagation()
this.toggleAnimation()
updateButtonState()
})
this.playPauseContainer.appendChild(playIcon)
container.appendChild(this.playPauseContainer)
this.playPauseContainer.style.display = 'none'
}
protected async setupModel(model: THREE.Object3D) {
@@ -44,6 +151,21 @@ class Load3dAnimation extends Load3d {
this.updateSelectedAnimation(0)
}
}
if (this.animationClips.length > 0) {
this.playPauseContainer.style.display = 'block'
} else {
this.playPauseContainer.style.display = 'none'
}
if (this.animationClips.length > 0) {
this.playPauseContainer.style.display = 'block'
this.animationSelect.style.display = 'block'
this.updateAnimationList()
} else {
this.playPauseContainer.style.display = 'none'
this.animationSelect.style.display = 'none'
}
}
setAnimationSpeed(speed: number) {
@@ -88,6 +210,8 @@ class Load3dAnimation extends Load3d {
}
this.animationActions = [action]
this.updateAnimationList()
}
clearModel() {
@@ -104,6 +228,11 @@ class Load3dAnimation extends Load3d {
this.animationSpeed = 1.0
super.clearModel()
if (this.animationSelect) {
this.animationSelect.style.display = 'none'
this.animationSelect.innerHTML = ''
}
}
getAnimationNames(): string[] {
@@ -120,6 +249,17 @@ class Load3dAnimation extends Load3d {
this.isAnimationPlaying = play ?? !this.isAnimationPlaying
const icon = this.playPauseContainer.querySelector('svg')
if (icon) {
if (this.isAnimationPlaying) {
icon.innerHTML = '<path d="M6 4h4v16H6zM14 4h4v16h-4z"/>'
this.playPauseContainer.title = 'Pause Animation'
} else {
icon.innerHTML = '<path d="M8 5v14l11-7z"/>'
this.playPauseContainer.title = 'Play Animation'
}
}
this.animationActions.forEach((action) => {
if (this.isAnimationPlaying) {
action.paused = false