|
1 | | -import { UIPanel, UIText, UIButton } from './libs/ui.js'; |
| 1 | +import { UIPanel, UIText, UIButton, UINumber } from './libs/ui.js'; |
2 | 2 |
|
3 | 3 | import { AnimationPathHelper } from 'three/addons/helpers/AnimationPathHelper.js'; |
4 | 4 |
|
5 | 5 | function Animation( editor ) { |
6 | 6 |
|
7 | 7 | const signals = editor.signals; |
| 8 | + const strings = editor.strings; |
| 9 | + const mixer = editor.mixer; |
8 | 10 |
|
9 | 11 | const container = new UIPanel(); |
10 | 12 | container.setId( 'animation' ); |
@@ -33,9 +35,9 @@ function Animation( editor ) { |
33 | 35 | container.add( controlsPanel ); |
34 | 36 |
|
35 | 37 | // SVG icons |
36 | | - const playIcon = `<svg width="12" height="12" viewBox="0 0 12 12"><path d="M3 1.5v9l7-4.5z" fill="currentColor"/></svg>`; |
37 | | - const pauseIcon = `<svg width="12" height="12" viewBox="0 0 12 12"><path d="M2 1h3v10H2zM7 1h3v10H7z" fill="currentColor"/></svg>`; |
38 | | - const stopIcon = `<svg width="12" height="12" viewBox="0 0 12 12"><rect x="2" y="2" width="8" height="8" fill="currentColor"/></svg>`; |
| 38 | + const playIcon = '<svg width="12" height="12" viewBox="0 0 12 12"><path d="M3 1.5v9l7-4.5z" fill="currentColor"/></svg>'; |
| 39 | + const pauseIcon = '<svg width="12" height="12" viewBox="0 0 12 12"><path d="M2 1h3v10H2zM7 1h3v10H7z" fill="currentColor"/></svg>'; |
| 40 | + const stopIcon = '<svg width="12" height="12" viewBox="0 0 12 12"><rect x="2" y="2" width="8" height="8" fill="currentColor"/></svg>'; |
39 | 41 |
|
40 | 42 | const playButton = new UIButton(); |
41 | 43 | playButton.dom.innerHTML = playIcon; |
@@ -130,6 +132,17 @@ function Animation( editor ) { |
130 | 132 | const durationText = new UIText( '0.00' ).setWidth( '36px' ); |
131 | 133 | timeDisplay.appendChild( durationText.dom ); |
132 | 134 |
|
| 135 | + // Time Scale |
| 136 | + const mixerTimeScaleNumber = new UINumber( 1 ).setWidth( '60px' ).setRange( - 10, 10 ); |
| 137 | + mixerTimeScaleNumber.onChange( function () { |
| 138 | + |
| 139 | + mixer.timeScale = mixerTimeScaleNumber.getValue(); |
| 140 | + |
| 141 | + } ); |
| 142 | + |
| 143 | + controlsPanel.add( new UIText( strings.getKey( 'sidebar/animations/timescale' ) ).setClass( 'Label' ) ); |
| 144 | + controlsPanel.add( mixerTimeScaleNumber ); |
| 145 | + |
133 | 146 | // Timeline area with track rows |
134 | 147 | const timelineArea = document.createElement( 'div' ); |
135 | 148 | timelineArea.style.flex = '1'; |
@@ -440,6 +453,7 @@ function Animation( editor ) { |
440 | 453 | trackTimeline.appendChild( keyframe ); |
441 | 454 |
|
442 | 455 | } |
| 456 | + |
443 | 457 | trackRow.appendChild( trackTimeline ); |
444 | 458 |
|
445 | 459 | // Hover on position tracks to show path helper |
@@ -485,13 +499,27 @@ function Animation( editor ) { |
485 | 499 |
|
486 | 500 | } |
487 | 501 |
|
488 | | - // Select clip without playing |
489 | | - currentClip = clip; |
490 | | - currentRoot = root; |
491 | | - currentAction = editor.mixer.clipAction( clip, root ); |
| 502 | + if ( currentClip === clip ) { |
492 | 503 |
|
493 | | - // Update duration display |
494 | | - durationText.setValue( clip.duration.toFixed( 2 ) ); |
| 504 | + // Unselect clip |
| 505 | + currentAction = null; |
| 506 | + currentClip = null; |
| 507 | + currentRoot = null; |
| 508 | + |
| 509 | + timeText.setValue( '0.00' ); |
| 510 | + durationText.setValue( '0.00' ); |
| 511 | + |
| 512 | + } else { |
| 513 | + |
| 514 | + // Select clip without playing |
| 515 | + currentClip = clip; |
| 516 | + currentRoot = root; |
| 517 | + currentAction = editor.mixer.clipAction( clip, root ); |
| 518 | + |
| 519 | + // Update duration display |
| 520 | + durationText.setValue( clip.duration.toFixed( 2 ) ); |
| 521 | + |
| 522 | + } |
495 | 523 |
|
496 | 524 | } |
497 | 525 |
|
|
0 commit comments