diff --git a/src/extensions/core/load3d.ts b/src/extensions/core/load3d.ts
index ff6240f68..9e3dbfa19 100644
--- a/src/extensions/core/load3d.ts
+++ b/src/extensions/core/load3d.ts
@@ -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,
diff --git a/src/extensions/core/load3d/Load3dAnimation.ts b/src/extensions/core/load3d/Load3dAnimation.ts
index e472d31f4..a5f264dc5 100644
--- a/src/extensions/core/load3d/Load3dAnimation.ts
+++ b/src/extensions/core/load3d/Load3dAnimation.ts
@@ -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 = `
+
+ `
+ this.playPauseContainer.title = 'Pause Animation'
+ } else {
+ icon.innerHTML = `
+
+ `
+ this.playPauseContainer.title = 'Play Animation'
+ }
+ }
+ }
+
+ const playIcon = document.createElement('div')
+ playIcon.innerHTML = `
+
+ `
+
+ 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 = ''
+ this.playPauseContainer.title = 'Pause Animation'
+ } else {
+ icon.innerHTML = ''
+ this.playPauseContainer.title = 'Play Animation'
+ }
+ }
+
this.animationActions.forEach((action) => {
if (this.isAnimationPlaying) {
action.paused = false