mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-21 23:09:39 +00:00
feat: add animation progress bar for 3D nodes and viewer (#7839)
## Summary - Add draggable progress bar to AnimationControls component - Display current time / total duration - Allow seeking through animations when paused or playing - Add animation controls to 3D Viewer fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/7830 and https://github.com/Comfy-Org/ComfyUI_frontend/issues/7831 ## Screenshots (if applicable) https://github.com/user-attachments/assets/f6d0668c-c7a4-497e-8345-9ef6e47a41c6 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7839-feat-add-animation-progress-bar-for-3D-nodes-and-viewer-2de6d73d36508101ac98f673206b30d9) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -60,6 +60,8 @@ export const useLoad3d = (nodeOrRef: MaybeRef<LGraphNode | null>) => {
|
||||
const playing = ref(false)
|
||||
const selectedSpeed = ref(1)
|
||||
const selectedAnimation = ref(0)
|
||||
const animationProgress = ref(0)
|
||||
const animationDuration = ref(0)
|
||||
const loading = ref(false)
|
||||
const loadingMessage = ref('')
|
||||
const isPreview = ref(false)
|
||||
@@ -357,6 +359,13 @@ export const useLoad3d = (nodeOrRef: MaybeRef<LGraphNode | null>) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleSeek = (progress: number) => {
|
||||
if (load3d && animationDuration.value > 0) {
|
||||
const time = (progress / 100) * animationDuration.value
|
||||
load3d.setAnimationTime(time)
|
||||
}
|
||||
}
|
||||
|
||||
const handleBackgroundImageUpdate = async (file: File | null) => {
|
||||
if (!file) {
|
||||
sceneConfig.value.backgroundImage = ''
|
||||
@@ -514,6 +523,14 @@ export const useLoad3d = (nodeOrRef: MaybeRef<LGraphNode | null>) => {
|
||||
animationListChange: (newValue: AnimationItem[]) => {
|
||||
animations.value = newValue
|
||||
},
|
||||
animationProgressChange: (data: {
|
||||
progress: number
|
||||
currentTime: number
|
||||
duration: number
|
||||
}) => {
|
||||
animationProgress.value = data.progress
|
||||
animationDuration.value = data.duration
|
||||
},
|
||||
cameraChanged: (cameraState: CameraState) => {
|
||||
const rawNode = toRaw(nodeRef.value)
|
||||
if (rawNode) {
|
||||
@@ -573,6 +590,8 @@ export const useLoad3d = (nodeOrRef: MaybeRef<LGraphNode | null>) => {
|
||||
playing,
|
||||
selectedSpeed,
|
||||
selectedAnimation,
|
||||
animationProgress,
|
||||
animationDuration,
|
||||
loading,
|
||||
loadingMessage,
|
||||
|
||||
@@ -585,6 +604,7 @@ export const useLoad3d = (nodeOrRef: MaybeRef<LGraphNode | null>) => {
|
||||
handleStopRecording,
|
||||
handleExportRecording,
|
||||
handleClearRecording,
|
||||
handleSeek,
|
||||
handleBackgroundImageUpdate,
|
||||
handleExportModel,
|
||||
handleModelDrop,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ref, toRaw, watch } from 'vue'
|
||||
import Load3d from '@/extensions/core/load3d/Load3d'
|
||||
import Load3dUtils from '@/extensions/core/load3d/Load3dUtils'
|
||||
import type {
|
||||
AnimationItem,
|
||||
BackgroundRenderModeType,
|
||||
CameraState,
|
||||
CameraType,
|
||||
@@ -49,6 +50,14 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
|
||||
const isSplatModel = ref(false)
|
||||
const isPlyModel = ref(false)
|
||||
|
||||
// Animation state
|
||||
const animations = ref<AnimationItem[]>([])
|
||||
const playing = ref(false)
|
||||
const selectedSpeed = ref(1)
|
||||
const selectedAnimation = ref(0)
|
||||
const animationProgress = ref(0)
|
||||
const animationDuration = ref(0)
|
||||
|
||||
let load3d: Load3d | null = null
|
||||
let sourceLoad3d: Load3d | null = null
|
||||
|
||||
@@ -174,6 +183,61 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
|
||||
}
|
||||
})
|
||||
|
||||
// Animation watches
|
||||
watch(playing, (newValue) => {
|
||||
if (load3d) {
|
||||
load3d.toggleAnimation(newValue)
|
||||
}
|
||||
})
|
||||
|
||||
watch(selectedSpeed, (newValue) => {
|
||||
if (load3d && newValue) {
|
||||
load3d.setAnimationSpeed(newValue)
|
||||
}
|
||||
})
|
||||
|
||||
watch(selectedAnimation, (newValue) => {
|
||||
if (load3d && newValue !== undefined) {
|
||||
load3d.updateSelectedAnimation(newValue)
|
||||
}
|
||||
})
|
||||
|
||||
const handleSeek = (progress: number) => {
|
||||
if (load3d && animationDuration.value > 0) {
|
||||
const time = (progress / 100) * animationDuration.value
|
||||
load3d.setAnimationTime(time)
|
||||
}
|
||||
}
|
||||
|
||||
const setupAnimationEvents = () => {
|
||||
if (!load3d) return
|
||||
|
||||
load3d.addEventListener(
|
||||
'animationListChange',
|
||||
(newValue: AnimationItem[]) => {
|
||||
animations.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
load3d.addEventListener(
|
||||
'animationProgressChange',
|
||||
(data: { progress: number; currentTime: number; duration: number }) => {
|
||||
animationProgress.value = data.progress
|
||||
animationDuration.value = data.duration
|
||||
}
|
||||
)
|
||||
|
||||
// Initialize animation list if animations already exist
|
||||
if (load3d.hasAnimations()) {
|
||||
const clips = load3d.animationManager.animationClips
|
||||
animations.value = clips.map((clip, index) => ({
|
||||
name: clip.name || `Animation ${index + 1}`,
|
||||
index
|
||||
}))
|
||||
animationDuration.value = load3d.getAnimationDuration()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize viewer in node mode (with source Load3d)
|
||||
*/
|
||||
@@ -270,6 +334,8 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
|
||||
upDirection: upDirection.value,
|
||||
materialMode: materialMode.value
|
||||
}
|
||||
|
||||
setupAnimationEvents()
|
||||
} catch (error) {
|
||||
console.error('Error initializing Load3d viewer:', error)
|
||||
useToastStore().addAlert(
|
||||
@@ -310,6 +376,8 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
|
||||
isPlyModel.value = load3d.isPlyModel()
|
||||
|
||||
isPreview.value = true
|
||||
|
||||
setupAnimationEvents()
|
||||
} catch (error) {
|
||||
console.error('Error initializing standalone 3D viewer:', error)
|
||||
useToastStore().addAlert('Failed to load 3D model')
|
||||
@@ -527,6 +595,14 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
|
||||
isSplatModel,
|
||||
isPlyModel,
|
||||
|
||||
// Animation state
|
||||
animations,
|
||||
playing,
|
||||
selectedSpeed,
|
||||
selectedAnimation,
|
||||
animationProgress,
|
||||
animationDuration,
|
||||
|
||||
// Methods
|
||||
initializeViewer,
|
||||
initializeStandaloneViewer,
|
||||
@@ -539,6 +615,7 @@ export const useLoad3dViewer = (node?: LGraphNode) => {
|
||||
refreshViewport,
|
||||
handleBackgroundImageUpdate,
|
||||
handleModelDrop,
|
||||
handleSeek,
|
||||
cleanup
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user