@needle-tools/engine 4.10.2 → 4.10.3
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/CHANGELOG.md +5 -0
- package/dist/{needle-engine.bundle-TQLdfpdM.min.js → needle-engine.bundle-B89CYbPy.min.js} +120 -120
- package/dist/{needle-engine.bundle-DJeQHQFo.js → needle-engine.bundle-CyS6tKe3.js} +4009 -4005
- package/dist/{needle-engine.bundle-y_36uvNP.umd.cjs → needle-engine.bundle-lvbiYdYt.umd.cjs} +121 -121
- package/dist/needle-engine.js +2 -2
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/rapier-BIeDFw5K.umd.cjs +1 -0
- package/dist/rapier-DQM98oaj.min.js +1 -0
- package/dist/rapier-DpcIWXum.js +5217 -0
- package/lib/engine/engine_three_utils.d.ts +7 -1
- package/lib/engine/engine_three_utils.js +9 -7
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine-components/timeline/TimelineModels.d.ts +1 -1
- package/lib/engine-components/web/ScrollFollow.d.ts +6 -0
- package/lib/engine-components/web/ScrollFollow.js +70 -100
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/engine_three_utils.ts +11 -1
- package/src/engine-components/timeline/TimelineModels.ts +1 -1
- package/src/engine-components/web/ScrollFollow.ts +81 -113
- package/dist/rapier-BJaux8TQ.js +0 -5217
- package/dist/rapier-Bd0qRV1r.umd.cjs +0 -1
- package/dist/rapier-CnHGx3sO.min.js +0 -1
|
@@ -53,6 +53,12 @@ type ScrollFollowEvent = {
|
|
|
53
53
|
* 2. Add a ScrollFollow component to the same GameObject or another GameObject in the scene.
|
|
54
54
|
* 3. Assign the PlayableDirector component to the ScrollFollow's target property.
|
|
55
55
|
* 4. The timeline will now scrub based on the scroll position of the page.
|
|
56
|
+
* 5. (Optional) Add ScrollMarker markers to your HTML to define specific points in the timeline that correspond to elements on the page. For example:
|
|
57
|
+
* ```html
|
|
58
|
+
* <div data-timeline-marker="0.0">Start of Timeline</div>
|
|
59
|
+
* <div data-timeline-marker="0.5">Middle of Timeline</div>
|
|
60
|
+
* <div data-timeline-marker="1.0">End of Timeline</div>
|
|
61
|
+
* ```
|
|
56
62
|
*
|
|
57
63
|
* @category Web
|
|
58
64
|
* @group Components
|
|
@@ -291,67 +297,75 @@ export class ScrollFollow extends Behaviour {
|
|
|
291
297
|
private handleTimelineTarget(director: PlayableDirector, value: number) {
|
|
292
298
|
|
|
293
299
|
const duration = director.duration;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
continue;
|
|
300
|
+
let markersArray = timelineMarkerArrays.get(director);
|
|
301
|
+
|
|
302
|
+
// Create markers array
|
|
303
|
+
if (!markersArray) {
|
|
304
|
+
markersArray = [];
|
|
305
|
+
timelineMarkerArrays.set(director, markersArray);
|
|
306
|
+
|
|
307
|
+
let markerIndex = 0;
|
|
308
|
+
|
|
309
|
+
for (const marker of director.foreachMarker<ScrollMarkerModel & { element?: HTMLElement | null, needsUpdate?: boolean, timeline?: ViewTimeline }>("ScrollMarker")) {
|
|
310
|
+
|
|
311
|
+
const index = markerIndex++;
|
|
312
|
+
|
|
313
|
+
// Get marker elements from DOM
|
|
314
|
+
if ((marker.element === undefined || marker.needsUpdate === true || /** element is not in DOM anymore? */ (marker.element && !marker.element?.parentNode))) {
|
|
315
|
+
marker.needsUpdate = false;
|
|
316
|
+
try {
|
|
317
|
+
// TODO: with this it's currently not possible to remap markers from HTML. For example if I have two sections and I want to now use the marker["center"] multiple times to stay at that marker for a longer time
|
|
318
|
+
marker.element = tryGetElementsForSelector(index) as HTMLElement | null;
|
|
319
|
+
if (debug) console.debug(`ScrollMarker #${index} (${marker.time.toFixed(2)}) found`, marker.element);
|
|
320
|
+
if (!marker.element) {
|
|
321
|
+
if (debug || isDevEnvironment()) console.warn(`No HTML element found for ScrollMarker: ${marker.name} (index ${index})`);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
319
324
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
marker.
|
|
323
|
-
subject: marker.element,
|
|
324
|
-
axis: 'block', // https://drafts.csswg.org/scroll-animations/#scroll-notation
|
|
325
|
-
});
|
|
325
|
+
catch (error) {
|
|
326
|
+
marker.element = null;
|
|
327
|
+
console.error("ScrollMarker selector is not valid: " + marker.name + "\n", error);
|
|
326
328
|
}
|
|
327
329
|
}
|
|
328
|
-
catch (error) {
|
|
329
|
-
marker.element = null;
|
|
330
|
-
console.error("ScrollMarker selector is not valid: " + marker.name + "\n", error);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
330
|
|
|
334
|
-
|
|
335
|
-
|
|
331
|
+
// skip markers without element (e.g. if the selector didn't return any element)
|
|
332
|
+
if (!marker.element) continue;
|
|
336
333
|
|
|
337
|
-
|
|
334
|
+
markersArray.push(marker);
|
|
335
|
+
}
|
|
338
336
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
337
|
+
// If the timeline has no markers defined we can use timeline-marker elements in the DOM. These must define times then
|
|
338
|
+
if (markersArray.length <= 0) {
|
|
339
|
+
const markers = document.querySelectorAll(`[data-timeline-marker]`);
|
|
340
|
+
markers.forEach((element) => {
|
|
341
|
+
const value = element.getAttribute("data-timeline-marker");
|
|
342
|
+
const time = parseFloat(value || ("NaN"));
|
|
343
|
+
if (!isNaN(time)) {
|
|
344
|
+
markersArray!.push({
|
|
345
|
+
time,
|
|
346
|
+
element: element as HTMLElement,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
else if (isDevEnvironment() || debug) {
|
|
350
|
+
console.warn("[ScrollFollow] data-timeline-marker attribute is not a valid number. Supported are numbers only (e.g. <div data-timeline-marker=\"0.5\">)");
|
|
351
|
+
}
|
|
352
|
+
});
|
|
344
353
|
}
|
|
345
|
-
|
|
346
|
-
|
|
354
|
+
|
|
355
|
+
// Init ViewTimeline for markers
|
|
356
|
+
for (const marker of markersArray) {
|
|
357
|
+
if (marker.element) {
|
|
358
|
+
// https://scroll-driven-animations.style/tools/view-timeline/ranges
|
|
359
|
+
/** @ts-ignore */
|
|
360
|
+
marker.timeline = new ViewTimeline({
|
|
361
|
+
subject: marker.element,
|
|
362
|
+
axis: 'block', // https://drafts.csswg.org/scroll-animations/#scroll-notation
|
|
363
|
+
});
|
|
364
|
+
}
|
|
347
365
|
}
|
|
348
366
|
}
|
|
349
367
|
|
|
350
368
|
|
|
351
|
-
|
|
352
|
-
const currentTop = this._scrollValue;
|
|
353
|
-
const currentBottom = currentTop + this._scrollContainerHeight;
|
|
354
|
-
|
|
355
369
|
weightsArray.length = 0;
|
|
356
370
|
let sum = 0;
|
|
357
371
|
const oneFrameTime = 1 / 60;
|
|
@@ -374,7 +388,7 @@ export class ScrollFollow extends Behaviour {
|
|
|
374
388
|
const time01 = calculateTimelinePositionNormalized(timeline);
|
|
375
389
|
// remap 0-1 to 0 - 1 - 0 (full weight at center)
|
|
376
390
|
const weight = 1 - Math.abs(time01 - 0.5) * 2;
|
|
377
|
-
const name =
|
|
391
|
+
const name = `marker${i}`;
|
|
378
392
|
if (time01 > 0 && time01 <= 1) {
|
|
379
393
|
const lerpTime = marker.time + (nextTime - marker.time) * time01;
|
|
380
394
|
weightsArray.push({ name, time: lerpTime, weight: weight });
|
|
@@ -391,38 +405,6 @@ export class ScrollFollow extends Behaviour {
|
|
|
391
405
|
sum += 1;
|
|
392
406
|
}
|
|
393
407
|
}
|
|
394
|
-
continue;
|
|
395
|
-
// if(this.context.time.frame % 10 === 0) console.log(marker.element?.className, timeline, calculateTimelinePositionNormalized(timeline!));
|
|
396
|
-
|
|
397
|
-
// const top = marker.element.offsetTop - this._scrollContainerHeight;
|
|
398
|
-
// const height = marker.element.offsetHeight + this._scrollContainerHeight;
|
|
399
|
-
// const bottom = top + height;
|
|
400
|
-
// let overlap = 0;
|
|
401
|
-
|
|
402
|
-
// // TODO: if we have two marker sections where no HTML overlaps (vor example because some large section is between them) we probably want to still virtually interpolate between them slowly in that region
|
|
403
|
-
|
|
404
|
-
// if (bottom < currentTop) {
|
|
405
|
-
// // marker is above scroll region
|
|
406
|
-
// overlap = 0;
|
|
407
|
-
// }
|
|
408
|
-
// else if (top > currentBottom) {
|
|
409
|
-
// // marker is below scroll region
|
|
410
|
-
// overlap = 0;
|
|
411
|
-
// }
|
|
412
|
-
// else {
|
|
413
|
-
// // calculate overlap in pixels
|
|
414
|
-
// const overlapTop = Math.max(top, currentTop);
|
|
415
|
-
// const overlapBottom = Math.min(bottom, currentBottom);
|
|
416
|
-
// const height = Math.max(1, currentBottom - currentTop);
|
|
417
|
-
// overlap = Math.max(0, overlapBottom - overlapTop);
|
|
418
|
-
// }
|
|
419
|
-
|
|
420
|
-
// // if(this.context.time.frame % 20 === 0) console.log(overlap)
|
|
421
|
-
|
|
422
|
-
// if (overlap > 0) {
|
|
423
|
-
// weightsArray.push({ time: marker.time, weight: overlap });
|
|
424
|
-
// sum += overlap;
|
|
425
|
-
// }
|
|
426
408
|
}
|
|
427
409
|
|
|
428
410
|
if (weightsArray.length <= 0 && markerCount <= 0) {
|
|
@@ -456,12 +438,14 @@ export class ScrollFollow extends Behaviour {
|
|
|
456
438
|
}
|
|
457
439
|
|
|
458
440
|
|
|
441
|
+
const timelineMarkerArrays: WeakMap<PlayableDirector,
|
|
442
|
+
Array<{
|
|
443
|
+
time: number,
|
|
444
|
+
element?: HTMLElement | null | undefined,
|
|
445
|
+
timeline?: ViewTimeline,
|
|
446
|
+
}>
|
|
447
|
+
> = new WeakMap();
|
|
459
448
|
|
|
460
|
-
const weightsArray: OverlapInfo[] = [];
|
|
461
|
-
const markersArray: Array<ScrollMarkerModel & {
|
|
462
|
-
element?: HTMLElement | null,
|
|
463
|
-
timeline?: ViewTimeline,
|
|
464
|
-
}> = [];
|
|
465
449
|
|
|
466
450
|
type OverlapInfo = {
|
|
467
451
|
name: string,
|
|
@@ -471,6 +455,8 @@ type OverlapInfo = {
|
|
|
471
455
|
weight: number,
|
|
472
456
|
}
|
|
473
457
|
|
|
458
|
+
const weightsArray: OverlapInfo[] = [];
|
|
459
|
+
|
|
474
460
|
|
|
475
461
|
// type SelectorCache = {
|
|
476
462
|
// /** The selector used to query the *elements */
|
|
@@ -480,41 +466,23 @@ type OverlapInfo = {
|
|
|
480
466
|
// }
|
|
481
467
|
// const querySelectorResults: Array<SelectorCache> = [];
|
|
482
468
|
|
|
483
|
-
const
|
|
484
|
-
const needleScrollMarkerNameCache = new Map<string, Element | null>();
|
|
469
|
+
const needleScrollMarkerCache = new Array<Element>();
|
|
485
470
|
let needsScrollMarkerRefresh = true;
|
|
486
471
|
|
|
487
|
-
function tryGetElementsForSelector(index: number
|
|
472
|
+
function tryGetElementsForSelector(index: number): Element | null {
|
|
488
473
|
|
|
489
474
|
if (!needsScrollMarkerRefresh) {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
if (element) return element;
|
|
493
|
-
// const isNumber = !isNaN(Number(name));
|
|
494
|
-
// if (!isNumber) {
|
|
495
|
-
// }
|
|
496
|
-
}
|
|
497
|
-
const element = needleScrollMarkerIndexCache.get(index) || null;
|
|
498
|
-
const value = element?.getAttribute("data-timeline-marker");
|
|
499
|
-
// if (value?.length) {
|
|
500
|
-
// if (cycle === 0) {
|
|
501
|
-
// // if the HTML marker we found by index does define a different marker name we try to find the correct HTML element by name
|
|
502
|
-
// return tryGetElementsForSelector(index, value, 1);
|
|
503
|
-
// }
|
|
504
|
-
// if (isDevEnvironment()) console.warn(`ScrollMarker name mismatch: expected "${name}", got "${value}"`);
|
|
505
|
-
// }
|
|
506
|
-
return element;
|
|
475
|
+
const element = needleScrollMarkerCache[index] || null;
|
|
476
|
+
if (element) return element;
|
|
507
477
|
}
|
|
508
478
|
needsScrollMarkerRefresh = false;
|
|
509
|
-
|
|
479
|
+
needleScrollMarkerCache.length = 0;
|
|
510
480
|
const markers = document.querySelectorAll(`[data-timeline-marker]`);
|
|
511
481
|
markers.forEach((m, i) => {
|
|
512
|
-
|
|
513
|
-
const name = m.getAttribute("data-timeline-marker");
|
|
514
|
-
if (name?.length) needleScrollMarkerNameCache.set(name, m);
|
|
482
|
+
needleScrollMarkerCache[i] = m;
|
|
515
483
|
});
|
|
516
484
|
needsScrollMarkerRefresh = false;
|
|
517
|
-
return tryGetElementsForSelector(index
|
|
485
|
+
return tryGetElementsForSelector(index);
|
|
518
486
|
}
|
|
519
487
|
|
|
520
488
|
|