@preference-sl/pref-viewer 2.11.0 → 2.12.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -113,18 +113,18 @@ export default class BabylonJSAnimationController {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
|
-
*
|
|
116
|
+
* Finds all animated node IDs associated with a given mesh by traversing its parent hierarchy.
|
|
117
117
|
* @private
|
|
118
118
|
* @param {Mesh} mesh - The mesh to check.
|
|
119
|
-
* @returns {string
|
|
119
|
+
* @returns {Array<string>} Array of animated node IDs associated with the mesh.
|
|
120
120
|
*/
|
|
121
|
-
#
|
|
122
|
-
let nodeId =
|
|
121
|
+
#getNodesAnimatedByMesh = function (mesh) {
|
|
122
|
+
let nodeId = [];
|
|
123
123
|
let node = mesh;
|
|
124
|
-
while (node.parent !== null
|
|
124
|
+
while (node.parent !== null) {
|
|
125
125
|
node = node.parent;
|
|
126
|
-
if (this.#animatedNodes.includes(node.id)) {
|
|
127
|
-
nodeId
|
|
126
|
+
if (this.#animatedNodes.includes(node.id) && !nodeId.includes(node.id)) {
|
|
127
|
+
nodeId.push(node.id);
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
return nodeId;
|
|
@@ -169,16 +169,29 @@ export default class BabylonJSAnimationController {
|
|
|
169
169
|
return;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
const
|
|
173
|
-
if (!
|
|
172
|
+
const nodeIds = this.#getNodesAnimatedByMesh(pickingInfo.pickedMesh);
|
|
173
|
+
if (!nodeIds.length) {
|
|
174
174
|
return;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
const transformNodes = [];
|
|
178
|
+
nodeIds.forEach((nodeId) => {
|
|
179
|
+
const transformNode = this.#scene.getTransformNodeByID(nodeId);
|
|
180
|
+
if (transformNode) {
|
|
181
|
+
transformNodes.push(transformNode);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
transformNodes.forEach((transformNode) => {
|
|
186
|
+
const nodeMeshes = transformNode.getChildMeshes();
|
|
187
|
+
if (nodeMeshes.length) {
|
|
188
|
+
nodeMeshes.forEach((mesh) => {
|
|
189
|
+
if (!this.#highlightLayer.hasMesh(mesh)) {
|
|
190
|
+
this.#highlightLayer.addMesh(mesh, this.#highlightColor);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
});
|
|
182
195
|
}
|
|
183
196
|
|
|
184
197
|
/**
|
|
@@ -203,15 +216,20 @@ export default class BabylonJSAnimationController {
|
|
|
203
216
|
|
|
204
217
|
this.hideMenu();
|
|
205
218
|
|
|
206
|
-
const
|
|
207
|
-
if (!
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
const openingAnimation = this.#getOpeningAnimationByNode(nodeId);
|
|
211
|
-
if (!openingAnimation) {
|
|
219
|
+
const nodeIds = this.#getNodesAnimatedByMesh(pickingInfo.pickedMesh);
|
|
220
|
+
if (!nodeIds.length) {
|
|
212
221
|
return;
|
|
213
222
|
}
|
|
223
|
+
const openingAnimations = [];
|
|
224
|
+
nodeIds.forEach((nodeId) => {
|
|
225
|
+
const openingAnimation = this.#getOpeningAnimationByNode(nodeId);
|
|
226
|
+
if (!openingAnimation) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
openingAnimations.push(openingAnimation);
|
|
230
|
+
});
|
|
214
231
|
|
|
215
|
-
|
|
232
|
+
const startedAnimation = openingAnimations.find((animation) => animation.state !== OpeningAnimation.states.closed);
|
|
233
|
+
startedAnimation ? startedAnimation.showControls(this.#canvas, openingAnimations) : openingAnimations[0].showControls(this.#canvas, openingAnimations);
|
|
216
234
|
}
|
|
217
235
|
}
|
|
@@ -5,17 +5,19 @@ import { PrefViewer3DAnimationMenuStyles } from "./styles.js";
|
|
|
5
5
|
* OpeningAnimationMenu - Manages and renders the interactive animation control menu for opening/closing animations in a Babylon.js scene.
|
|
6
6
|
*
|
|
7
7
|
* Summary:
|
|
8
|
-
* Provides a floating HTML-based GUI menu with playback and loop controls for animated nodes, including buttons and a
|
|
8
|
+
* Provides a floating HTML-based GUI menu with playback and loop controls for animated nodes, including buttons, a progress slider, and a selector for switching between multiple animations.
|
|
9
9
|
* Handles user interaction, updates UI state based on animation state, and invokes external callbacks for animation actions.
|
|
10
10
|
* Designed for integration with Babylon.js product configurators and interactive 3D applications.
|
|
11
11
|
*
|
|
12
12
|
* Key Features:
|
|
13
13
|
* - Renders a menu with buttons for controlling animation playback (open, close, pause, go to opened/closed, loop).
|
|
14
|
+
* - Displays a selector UI for switching between multiple opening animations, showing only the animation name without prefix.
|
|
14
15
|
* - Updates button states and slider position based on animation state, progress, and loop mode.
|
|
15
16
|
* - Handles user interactions and invokes provided callbacks for animation actions.
|
|
16
17
|
* - Synchronizes the slider value with animation progress, avoiding callback loops.
|
|
17
18
|
* - Provides setters to update animation state, progress, and loop mode from external controllers.
|
|
18
19
|
* - Disposes and cleans up all DOM resources when no longer needed.
|
|
20
|
+
* - Resets other animation menus when switching between animations.
|
|
19
21
|
*
|
|
20
22
|
* Public Setters:
|
|
21
23
|
* - set animationState(state): Updates the animation state and button states.
|
|
@@ -29,9 +31,10 @@ import { PrefViewer3DAnimationMenuStyles } from "./styles.js";
|
|
|
29
31
|
* - isVisible: Returns whether the animation menu is currently visible in the DOM.
|
|
30
32
|
*
|
|
31
33
|
* Private Methods:
|
|
32
|
-
* - #createMenu(): Initializes and renders the menu UI.
|
|
34
|
+
* - #createMenu(): Initializes and renders the menu UI, including the selector, buttons, and slider.
|
|
33
35
|
* - #addButton(name, imageSVG, enabled, active, visible, callback): Adds a button to the menu with specified properties.
|
|
34
36
|
* - #createButtons(): Creates all control buttons and sets their initial states.
|
|
37
|
+
* - #createSelector(): Creates a selector UI element for switching between available opening animations, omitting the prefix in the display name.
|
|
35
38
|
* - #getButtonByName(name): Retrieves a button control by its name.
|
|
36
39
|
* - #setButtonState(name, enabled, active, visible): Updates the visual state of a button.
|
|
37
40
|
* - #getPlayerButtonsState(): Returns the state (enabled, active, visible) for playback control buttons.
|
|
@@ -41,16 +44,18 @@ import { PrefViewer3DAnimationMenuStyles } from "./styles.js";
|
|
|
41
44
|
* - #setLoopButtonsState(): Updates all loop control buttons.
|
|
42
45
|
* - #createSlider(): Creates and configures the animation progress slider.
|
|
43
46
|
* - #onSliderInput(event): Handles the slider input event for animation progress.
|
|
47
|
+
* - #resetOtherAnimations(): Resets all other opening animations except the current one.
|
|
44
48
|
*
|
|
45
49
|
* Usage Example:
|
|
46
|
-
* const menu = new OpeningAnimationMenu(name, canvas, state, progress, loop, {
|
|
50
|
+
* const menu = new OpeningAnimationMenu(name, canvas, state, progress, loop, openingAnimations, {
|
|
47
51
|
* onOpen: () => { ... },
|
|
48
52
|
* onClose: () => { ... },
|
|
49
53
|
* onPause: () => { ... },
|
|
50
54
|
* onGoToOpened: () => { ... },
|
|
51
55
|
* onGoToClosed: () => { ... },
|
|
52
56
|
* onToggleLoop: () => { ... },
|
|
53
|
-
* onSetAnimationProgress: (progress) => { ... }
|
|
57
|
+
* onSetAnimationProgress: (progress) => { ... },
|
|
58
|
+
* onChangeAnimation: () => { ... }
|
|
54
59
|
* });
|
|
55
60
|
* menu.animationState = OpeningAnimation.states.opening;
|
|
56
61
|
* menu.animationProgress = 0.5;
|
|
@@ -60,6 +65,7 @@ export default class OpeningAnimationMenu {
|
|
|
60
65
|
#animationState = OpeningAnimation.states.closed;
|
|
61
66
|
#animationProgress = 0;
|
|
62
67
|
#animationLoop = false;
|
|
68
|
+
#openingAnimations = [];
|
|
63
69
|
#callbacks = null;
|
|
64
70
|
|
|
65
71
|
#name = "";
|
|
@@ -89,17 +95,20 @@ export default class OpeningAnimationMenu {
|
|
|
89
95
|
* @param {number} animationState - Current animation state (enum).
|
|
90
96
|
* @param {number} animationProgress - Current animation progress (0 to 1).
|
|
91
97
|
* @param {boolean} animationLoop - Whether the animation is set to loop.
|
|
98
|
+
* @param {Array<OpeningAnimation>} openingAnimations - Array of OpeningAnimation instances to manage.
|
|
92
99
|
* @param {object} callbacks - Callback functions for menu actions (play, pause, open, close, etc.).
|
|
93
100
|
* @public
|
|
94
101
|
*/
|
|
95
|
-
constructor(name, canvas, animationState, animationProgress, animationLoop, callbacks) {
|
|
102
|
+
constructor(name, canvas, animationState, animationProgress, animationLoop, openingAnimations, callbacks) {
|
|
96
103
|
this.#name = name;
|
|
97
104
|
this.#canvas = canvas;
|
|
98
105
|
this.#animationState = animationState;
|
|
99
106
|
this.#animationProgress = animationProgress;
|
|
100
107
|
this.#animationLoop = animationLoop;
|
|
108
|
+
this.#openingAnimations = openingAnimations;
|
|
101
109
|
this.#callbacks = callbacks;
|
|
102
110
|
|
|
111
|
+
this.#resetOtherAnimations();
|
|
103
112
|
this.#createMenu(animationState);
|
|
104
113
|
}
|
|
105
114
|
|
|
@@ -131,6 +140,46 @@ export default class OpeningAnimationMenu {
|
|
|
131
140
|
this.#containerButtons.appendChild(button);
|
|
132
141
|
}
|
|
133
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Creates a selector UI element for switching between available opening animations.
|
|
145
|
+
* @private
|
|
146
|
+
* @returns {void}
|
|
147
|
+
*/
|
|
148
|
+
#createSelector() {
|
|
149
|
+
if (!Array.isArray(this.#openingAnimations) || this.#openingAnimations.length < 2) {
|
|
150
|
+
return; // No selector needed if only one animation
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const selector = document.createElement("div");
|
|
154
|
+
selector.classList.add("animation-menu-selector");
|
|
155
|
+
|
|
156
|
+
this.#openingAnimations.forEach((animation) => {
|
|
157
|
+
const button = document.createElement("button");
|
|
158
|
+
button.classList.add("button-selector");
|
|
159
|
+
// Remove prefix before "_" for display
|
|
160
|
+
const nameParts = animation.name.split("_");
|
|
161
|
+
button.textContent = nameParts.length > 1 ? nameParts.slice(1).join("_") : animation.name;
|
|
162
|
+
|
|
163
|
+
// Highlight the current animation
|
|
164
|
+
if (animation.name === this.#name) {
|
|
165
|
+
button.classList.add("active");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
button.addEventListener("click", (event) => {
|
|
169
|
+
if (animation.name !== this.#name) {
|
|
170
|
+
if (this.#callbacks.onChangeAnimation && typeof this.#callbacks.onChangeAnimation === "function") {
|
|
171
|
+
this.#callbacks.onChangeAnimation();
|
|
172
|
+
}
|
|
173
|
+
animation.showControls(this.#canvas, this.#openingAnimations);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
selector.appendChild(button);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
this.#containerMain.prepend(selector);
|
|
181
|
+
}
|
|
182
|
+
|
|
134
183
|
/**
|
|
135
184
|
* Creates all control buttons and sets their initial states.
|
|
136
185
|
* @private
|
|
@@ -183,6 +232,7 @@ export default class OpeningAnimationMenu {
|
|
|
183
232
|
this.#containerButtons.classList.add("animation-menu-buttons");
|
|
184
233
|
this.#containerMain.appendChild(this.#containerButtons);
|
|
185
234
|
|
|
235
|
+
this.#createSelector();
|
|
186
236
|
this.#createButtons();
|
|
187
237
|
this.#createSlider();
|
|
188
238
|
}
|
|
@@ -263,6 +313,20 @@ export default class OpeningAnimationMenu {
|
|
|
263
313
|
return buttonsState;
|
|
264
314
|
}
|
|
265
315
|
|
|
316
|
+
/**
|
|
317
|
+
* Resets all other opening animations except the current one.
|
|
318
|
+
* @private
|
|
319
|
+
* @returns {void}
|
|
320
|
+
*/
|
|
321
|
+
#resetOtherAnimations() {
|
|
322
|
+
this.#openingAnimations.forEach((animation) => {
|
|
323
|
+
if (animation.name !== this.#name) {
|
|
324
|
+
animation.hideControls();
|
|
325
|
+
animation.goToClosed();
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
266
330
|
/**
|
|
267
331
|
* Updates the visual state of a button (enabled, active, visible).
|
|
268
332
|
* @private
|
|
@@ -19,7 +19,7 @@ import OpeningAnimationMenu from "./babylonjs-animation-opening-menu.js";
|
|
|
19
19
|
* - pause(): Pauses the current animation.
|
|
20
20
|
* - goToOpened(): Moves animation to the fully opened state.
|
|
21
21
|
* - goToClosed(): Moves animation to the fully closed state.
|
|
22
|
-
* - showControls(canvas): Displays the animation control menu.
|
|
22
|
+
* - showControls(canvas, openingAnimations): Displays the animation control menu and sets up callbacks.
|
|
23
23
|
* - hideControls(): Hides the animation control menu.
|
|
24
24
|
* - isControlsVisible(): Returns true if the control menu is visible for this animation.
|
|
25
25
|
*
|
|
@@ -433,8 +433,9 @@ export default class OpeningAnimation {
|
|
|
433
433
|
* Synchronizes slider and button states with animation.
|
|
434
434
|
* @public
|
|
435
435
|
* @param {HTMLCanvasElement} canvas - The canvas element for rendering.
|
|
436
|
+
* @param {Array<OpeningAnimation>} openingAnimations - Array of OpeningAnimation instances to manage.
|
|
436
437
|
*/
|
|
437
|
-
showControls(canvas) {
|
|
438
|
+
showControls(canvas, openingAnimations = []) {
|
|
438
439
|
const controlCallbacks = {
|
|
439
440
|
onGoToOpened: () => {
|
|
440
441
|
if (this.#state === OpeningAnimation.states.opened) {
|
|
@@ -481,8 +482,14 @@ export default class OpeningAnimation {
|
|
|
481
482
|
this.#loop = !this.#loop;
|
|
482
483
|
this.#menu.animationLoop = this.#loop;
|
|
483
484
|
},
|
|
485
|
+
onChangeAnimation: () => {
|
|
486
|
+
if (this.#state !== OpeningAnimation.states.closed) {
|
|
487
|
+
this.goToClosed();
|
|
488
|
+
}
|
|
489
|
+
this.hideControls();
|
|
490
|
+
},
|
|
484
491
|
};
|
|
485
|
-
this.#menu = new OpeningAnimationMenu(this.name, canvas, this.#state, this.#getProgress(), this.#loop, controlCallbacks);
|
|
492
|
+
this.#menu = new OpeningAnimationMenu(this.name, canvas, this.#state, this.#getProgress(), this.#loop, openingAnimations, controlCallbacks);
|
|
486
493
|
|
|
487
494
|
// Attach to Babylon.js scene render loop for real-time updates
|
|
488
495
|
this.#openAnimation._scene.onBeforeRenderObservable.add(this.#handlers.updateControlsSlider);
|
package/src/styles.js
CHANGED
|
@@ -111,15 +111,62 @@ export const PrefViewer3DAnimationMenuStyles = `
|
|
|
111
111
|
--slider-thumb-width: 20px;
|
|
112
112
|
--slider-bar-height: 8px;
|
|
113
113
|
--slider-bar-offset: 10px;
|
|
114
|
+
--selector-button-radius: 6px;
|
|
115
|
+
--selector-button-padding-horizontal: 8px;
|
|
116
|
+
--selector-button-padding-vertical: 4px;
|
|
117
|
+
--selector-button-font-size: 14px;
|
|
118
|
+
--selector-button-font-weight: 500;
|
|
119
|
+
--width: calc(var(--button-size) * 6 + var(--button-spacing) * 5 + var(--button-loop-margin-left));
|
|
114
120
|
|
|
115
121
|
display: block;
|
|
116
122
|
position: absolute;
|
|
117
123
|
bottom: 10px;
|
|
118
124
|
|
|
119
|
-
right: calc(50% - (
|
|
125
|
+
right: calc(50% - (var(--width) / 2));
|
|
120
126
|
z-index: 1000;
|
|
121
127
|
}
|
|
122
128
|
|
|
129
|
+
div.pref-viewer-3d.animation-menu>div.animation-menu-selector {
|
|
130
|
+
fore
|
|
131
|
+
padding: 0;
|
|
132
|
+
margin: 0;
|
|
133
|
+
display: flex;
|
|
134
|
+
flex-wrap: wrap;
|
|
135
|
+
gap: var(--button-spacing);
|
|
136
|
+
max-width: var(--width);
|
|
137
|
+
margin-bottom: var(--button-spacing);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
div.pref-viewer-3d.animation-menu>div.animation-menu-selector>button {
|
|
141
|
+
display: block;
|
|
142
|
+
padding: var(--selector-button-padding-vertical) var(--selector-button-padding-horizontal);
|
|
143
|
+
margin: 0;
|
|
144
|
+
border: 1px solid var(--color-border);
|
|
145
|
+
border-radius: var(--selector-button-radius);
|
|
146
|
+
background: var(--color-enabled);
|
|
147
|
+
color: var(--color-icon);
|
|
148
|
+
font-size: var(--selector-button-font-size);
|
|
149
|
+
font-weight: var(--selector-button-font-weight);
|
|
150
|
+
line-height: 1;
|
|
151
|
+
flex: 1;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
div.pref-viewer-3d.animation-menu>div.animation-menu-selector>button.active {
|
|
155
|
+
background: var(--color-active);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
div.pref-viewer-3d.animation-menu>div.animation-menu-selector>button.active:hover {
|
|
159
|
+
background: var(--color-active-hover);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
div.pref-viewer-3d.animation-menu>div.animation-menu-selector>button:not(.active) {
|
|
163
|
+
cursor: pointer;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
div.pref-viewer-3d.animation-menu>div.animation-menu-buttons>button:not(.active):hover {
|
|
167
|
+
background: var(--color-enabled-hover);
|
|
168
|
+
}
|
|
169
|
+
|
|
123
170
|
div.pref-viewer-3d.animation-menu>div.animation-menu-buttons {
|
|
124
171
|
padding: 0;
|
|
125
172
|
margin: 0;
|