@editframe/elements 0.30.1-beta.0 → 0.31.0-beta.0
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/dist/EF_FRAMEGEN.d.ts +5 -0
- package/dist/EF_FRAMEGEN.js +20 -4
- package/dist/EF_FRAMEGEN.js.map +1 -1
- package/dist/EF_INTERACTIVE.js.map +1 -1
- package/dist/_virtual/rolldown_runtime.js +27 -0
- package/dist/canvas/EFCanvas.d.ts +311 -0
- package/dist/canvas/EFCanvas.js +1089 -0
- package/dist/canvas/EFCanvas.js.map +1 -0
- package/dist/canvas/EFCanvasItem.d.ts +55 -0
- package/dist/canvas/EFCanvasItem.js +72 -0
- package/dist/canvas/EFCanvasItem.js.map +1 -0
- package/dist/canvas/api/CanvasAPI.d.ts +115 -0
- package/dist/canvas/api/CanvasAPI.js +182 -0
- package/dist/canvas/api/CanvasAPI.js.map +1 -0
- package/dist/canvas/api/types.d.ts +42 -0
- package/dist/canvas/coordinateTransform.js +90 -0
- package/dist/canvas/coordinateTransform.js.map +1 -0
- package/dist/canvas/getElementBounds.js +40 -0
- package/dist/canvas/getElementBounds.js.map +1 -0
- package/dist/canvas/overlays/SelectionOverlay.js +265 -0
- package/dist/canvas/overlays/SelectionOverlay.js.map +1 -0
- package/dist/canvas/overlays/overlayState.js +153 -0
- package/dist/canvas/overlays/overlayState.js.map +1 -0
- package/dist/canvas/selection/SelectionController.js +105 -0
- package/dist/canvas/selection/SelectionController.js.map +1 -0
- package/dist/canvas/selection/SelectionModel.d.ts +98 -0
- package/dist/canvas/selection/SelectionModel.js +229 -0
- package/dist/canvas/selection/SelectionModel.js.map +1 -0
- package/dist/canvas/selection/selectionContext.d.ts +31 -0
- package/dist/canvas/selection/selectionContext.js +12 -0
- package/dist/canvas/selection/selectionContext.js.map +1 -0
- package/dist/elements/ContainerInfo.d.ts +29 -0
- package/dist/elements/ContainerInfo.js +30 -0
- package/dist/elements/ContainerInfo.js.map +1 -0
- package/dist/elements/EFAudio.d.ts +13 -3
- package/dist/elements/EFAudio.js +64 -10
- package/dist/elements/EFAudio.js.map +1 -1
- package/dist/elements/EFCaptions.d.ts +18 -16
- package/dist/elements/EFCaptions.js +110 -19
- package/dist/elements/EFCaptions.js.map +1 -1
- package/dist/elements/EFImage.d.ts +16 -6
- package/dist/elements/EFImage.js +79 -9
- package/dist/elements/EFImage.js.map +1 -1
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +51 -4
- package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/AssetMediaEngine.js +125 -52
- package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/BaseMediaEngine.js +24 -6
- package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/JitMediaEngine.js +12 -8
- package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +46 -7
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +98 -73
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +28 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +18 -6
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +8 -2
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +31 -6
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +28 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +97 -72
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js.map +1 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/BufferUtils.js +1 -1
- package/dist/elements/EFMedia/shared/BufferUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +25 -14
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +47 -16
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +37 -19
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +65 -21
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +8 -3
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +32 -9
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +33 -10
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +23 -8
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +34 -10
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +31 -8
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +31 -114
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +44 -8
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js.map +1 -1
- package/dist/elements/EFMedia.d.ts +18 -7
- package/dist/elements/EFMedia.js +23 -3
- package/dist/elements/EFMedia.js.map +1 -1
- package/dist/elements/EFPanZoom.d.ts +96 -0
- package/dist/elements/EFPanZoom.js +290 -0
- package/dist/elements/EFPanZoom.js.map +1 -0
- package/dist/elements/EFSourceMixin.js +7 -6
- package/dist/elements/EFSourceMixin.js.map +1 -1
- package/dist/elements/EFSurface.d.ts +6 -6
- package/dist/elements/EFSurface.js +7 -2
- package/dist/elements/EFSurface.js.map +1 -1
- package/dist/elements/EFTemporal.d.ts +2 -1
- package/dist/elements/EFTemporal.js +192 -71
- package/dist/elements/EFTemporal.js.map +1 -1
- package/dist/elements/EFText.d.ts +5 -4
- package/dist/elements/EFText.js +102 -13
- package/dist/elements/EFText.js.map +1 -1
- package/dist/elements/EFTextSegment.d.ts +32 -6
- package/dist/elements/EFTextSegment.js +53 -15
- package/dist/elements/EFTextSegment.js.map +1 -1
- package/dist/elements/EFThumbnailStrip.d.ts +118 -56
- package/dist/elements/EFThumbnailStrip.js +522 -358
- package/dist/elements/EFThumbnailStrip.js.map +1 -1
- package/dist/elements/EFTimegroup.d.ts +223 -27
- package/dist/elements/EFTimegroup.js +851 -148
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/dist/elements/EFVideo.d.ts +42 -5
- package/dist/elements/EFVideo.js +165 -11
- package/dist/elements/EFVideo.js.map +1 -1
- package/dist/elements/EFWaveform.d.ts +6 -6
- package/dist/elements/EFWaveform.js +2 -1
- package/dist/elements/EFWaveform.js.map +1 -1
- package/dist/elements/ElementPositionInfo.d.ts +35 -0
- package/dist/elements/ElementPositionInfo.js +49 -0
- package/dist/elements/ElementPositionInfo.js.map +1 -0
- package/dist/elements/FetchMixin.js +16 -1
- package/dist/elements/FetchMixin.js.map +1 -1
- package/dist/elements/SessionThumbnailCache.js +152 -0
- package/dist/elements/SessionThumbnailCache.js.map +1 -0
- package/dist/elements/TargetController.js +3 -1
- package/dist/elements/TargetController.js.map +1 -1
- package/dist/elements/TimegroupController.js +9 -3
- package/dist/elements/TimegroupController.js.map +1 -1
- package/dist/elements/findRootTemporal.js +30 -0
- package/dist/elements/findRootTemporal.js.map +1 -0
- package/dist/elements/renderTemporalAudio.js +18 -5
- package/dist/elements/renderTemporalAudio.js.map +1 -1
- package/dist/elements/updateAnimations.js +492 -109
- package/dist/elements/updateAnimations.js.map +1 -1
- package/dist/getRenderInfo.d.ts +2 -2
- package/dist/gui/ContextMixin.js +4 -2
- package/dist/gui/ContextMixin.js.map +1 -1
- package/dist/gui/Controllable.js +74 -1
- package/dist/gui/Controllable.js.map +1 -1
- package/dist/gui/EFActiveRootTemporal.d.ts +50 -0
- package/dist/gui/EFActiveRootTemporal.js +94 -0
- package/dist/gui/EFActiveRootTemporal.js.map +1 -0
- package/dist/gui/EFConfiguration.d.ts +11 -5
- package/dist/gui/EFConfiguration.js.map +1 -1
- package/dist/gui/EFControls.d.ts +2 -2
- package/dist/gui/EFControls.js +109 -13
- package/dist/gui/EFControls.js.map +1 -1
- package/dist/gui/EFDial.d.ts +4 -4
- package/dist/gui/EFFilmstrip.d.ts +11 -214
- package/dist/gui/EFFilmstrip.js +53 -1152
- package/dist/gui/EFFilmstrip.js.map +1 -1
- package/dist/gui/EFFitScale.d.ts +3 -3
- package/dist/gui/EFFitScale.js +39 -12
- package/dist/gui/EFFitScale.js.map +1 -1
- package/dist/gui/EFFocusOverlay.d.ts +4 -4
- package/dist/gui/EFOverlayItem.d.ts +48 -0
- package/dist/gui/EFOverlayItem.js +97 -0
- package/dist/gui/EFOverlayItem.js.map +1 -0
- package/dist/gui/EFOverlayLayer.d.ts +70 -0
- package/dist/gui/EFOverlayLayer.js +104 -0
- package/dist/gui/EFOverlayLayer.js.map +1 -0
- package/dist/gui/EFPause.d.ts +4 -4
- package/dist/gui/EFPlay.d.ts +4 -4
- package/dist/gui/EFPreview.d.ts +4 -4
- package/dist/gui/EFResizableBox.d.ts +12 -16
- package/dist/gui/EFResizableBox.js +109 -451
- package/dist/gui/EFResizableBox.js.map +1 -1
- package/dist/gui/EFScrubber.d.ts +30 -5
- package/dist/gui/EFScrubber.js +224 -31
- package/dist/gui/EFScrubber.js.map +1 -1
- package/dist/gui/EFTimeDisplay.d.ts +4 -4
- package/dist/gui/EFTimeDisplay.js +4 -1
- package/dist/gui/EFTimeDisplay.js.map +1 -1
- package/dist/gui/EFTimelineRuler.d.ts +71 -0
- package/dist/gui/EFTimelineRuler.js +320 -0
- package/dist/gui/EFTimelineRuler.js.map +1 -0
- package/dist/gui/EFToggleLoop.d.ts +4 -4
- package/dist/gui/EFTogglePlay.d.ts +4 -4
- package/dist/gui/EFTransformHandles.d.ts +91 -0
- package/dist/gui/EFTransformHandles.js +393 -0
- package/dist/gui/EFTransformHandles.js.map +1 -0
- package/dist/gui/EFWorkbench.d.ts +182 -4
- package/dist/gui/EFWorkbench.js +2067 -22
- package/dist/gui/EFWorkbench.js.map +1 -1
- package/dist/gui/FitScaleHelpers.d.ts +31 -0
- package/dist/gui/FitScaleHelpers.js +41 -0
- package/dist/gui/FitScaleHelpers.js.map +1 -0
- package/dist/gui/PlaybackController.d.ts +2 -1
- package/dist/gui/PlaybackController.js +46 -15
- package/dist/gui/PlaybackController.js.map +1 -1
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/TWMixin.js.map +1 -1
- package/dist/gui/hierarchy/EFHierarchy.d.ts +65 -0
- package/dist/gui/hierarchy/EFHierarchy.js +338 -0
- package/dist/gui/hierarchy/EFHierarchy.js.map +1 -0
- package/dist/gui/hierarchy/EFHierarchyItem.d.ts +118 -0
- package/dist/gui/hierarchy/EFHierarchyItem.js +551 -0
- package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -0
- package/dist/gui/hierarchy/hierarchyContext.d.ts +38 -0
- package/dist/gui/hierarchy/hierarchyContext.js +8 -0
- package/dist/gui/hierarchy/hierarchyContext.js.map +1 -0
- package/dist/gui/icons.js +34 -0
- package/dist/gui/icons.js.map +1 -0
- package/dist/gui/panZoomTransformContext.js +12 -0
- package/dist/gui/panZoomTransformContext.js.map +1 -0
- package/dist/gui/previewSettingsContext.js +12 -0
- package/dist/gui/previewSettingsContext.js.map +1 -0
- package/dist/gui/timeline/EFTimeline.d.ts +270 -0
- package/dist/gui/timeline/EFTimeline.js +1369 -0
- package/dist/gui/timeline/EFTimeline.js.map +1 -0
- package/dist/gui/timeline/EFTimelineRow.js +374 -0
- package/dist/gui/timeline/EFTimelineRow.js.map +1 -0
- package/dist/gui/timeline/TrimHandles.d.ts +36 -0
- package/dist/gui/timeline/TrimHandles.js +204 -0
- package/dist/gui/timeline/TrimHandles.js.map +1 -0
- package/dist/gui/timeline/flattenHierarchy.js +31 -0
- package/dist/gui/timeline/flattenHierarchy.js.map +1 -0
- package/dist/gui/timeline/timelineStateContext.d.ts +26 -0
- package/dist/gui/timeline/timelineStateContext.js +42 -0
- package/dist/gui/timeline/timelineStateContext.js.map +1 -0
- package/dist/gui/timeline/tracks/AudioTrack.js +264 -0
- package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/CaptionsTrack.js +595 -0
- package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js +19 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/ImageTrack.js +53 -0
- package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TextTrack.js +250 -0
- package/dist/gui/timeline/tracks/TextTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TimegroupTrack.js +143 -0
- package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TrackItem.js +269 -0
- package/dist/gui/timeline/tracks/TrackItem.js.map +1 -0
- package/dist/gui/timeline/tracks/VideoTrack.js +265 -0
- package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/WaveformTrack.js +19 -0
- package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/ensureTrackItemInit.js +1 -0
- package/dist/gui/timeline/tracks/preloadTracks.js +9 -0
- package/dist/gui/timeline/tracks/renderTrackChildren.js +119 -0
- package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -0
- package/dist/gui/timeline/tracks/waveformUtils.js +80 -0
- package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -0
- package/dist/gui/transformCalculations.js +217 -0
- package/dist/gui/transformCalculations.js.map +1 -0
- package/dist/gui/transformUtils.d.ts +37 -0
- package/dist/gui/transformUtils.js +77 -0
- package/dist/gui/transformUtils.js.map +1 -0
- package/dist/gui/tree/EFTree.d.ts +59 -0
- package/dist/gui/tree/EFTree.js +174 -0
- package/dist/gui/tree/EFTree.js.map +1 -0
- package/dist/gui/tree/EFTreeItem.d.ts +38 -0
- package/dist/gui/tree/EFTreeItem.js +146 -0
- package/dist/gui/tree/EFTreeItem.js.map +1 -0
- package/dist/gui/tree/treeContext.d.ts +60 -0
- package/dist/gui/tree/treeContext.js +23 -0
- package/dist/gui/tree/treeContext.js.map +1 -0
- package/dist/index.d.ts +32 -8
- package/dist/index.js +30 -6
- package/dist/index.js.map +1 -1
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +688 -0
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +1 -0
- package/dist/node_modules/react/cjs/react.development.js +1521 -0
- package/dist/node_modules/react/cjs/react.development.js.map +1 -0
- package/dist/node_modules/react/index.js +13 -0
- package/dist/node_modules/react/index.js.map +1 -0
- package/dist/node_modules/react/jsx-runtime.js +13 -0
- package/dist/node_modules/react/jsx-runtime.js.map +1 -0
- package/dist/preview/AdaptiveResolutionTracker.js +228 -0
- package/dist/preview/AdaptiveResolutionTracker.js.map +1 -0
- package/dist/preview/RenderProfiler.js +135 -0
- package/dist/preview/RenderProfiler.js.map +1 -0
- package/dist/preview/previewSettings.js +131 -0
- package/dist/preview/previewSettings.js.map +1 -0
- package/dist/preview/previewTypes.js +64 -0
- package/dist/preview/previewTypes.js.map +1 -0
- package/dist/preview/renderTimegroupPreview.js +656 -0
- package/dist/preview/renderTimegroupPreview.js.map +1 -0
- package/dist/preview/renderTimegroupToCanvas.d.ts +37 -0
- package/dist/preview/renderTimegroupToCanvas.js +840 -0
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -0
- package/dist/preview/renderTimegroupToVideo.d.ts +39 -0
- package/dist/preview/renderTimegroupToVideo.js +274 -0
- package/dist/preview/renderTimegroupToVideo.js.map +1 -0
- package/dist/preview/renderers.js +16 -0
- package/dist/preview/renderers.js.map +1 -0
- package/dist/preview/statsTrackingStrategy.js +201 -0
- package/dist/preview/statsTrackingStrategy.js.map +1 -0
- package/dist/preview/thumbnailCacheSettings.js +52 -0
- package/dist/preview/thumbnailCacheSettings.js.map +1 -0
- package/dist/preview/workers/WorkerPool.js +178 -0
- package/dist/preview/workers/WorkerPool.js.map +1 -0
- package/dist/sandbox/PlaybackControls.js +10 -0
- package/dist/sandbox/PlaybackControls.js.map +1 -0
- package/dist/sandbox/ScenarioRunner.js +1 -0
- package/dist/sandbox/index.js +2 -0
- package/dist/style.css +66 -69
- package/dist/transcoding/types/index.d.ts +2 -1
- package/dist/transcoding/utils/UrlGenerator.d.ts +6 -1
- package/dist/transcoding/utils/UrlGenerator.js +12 -3
- package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
- package/dist/utils/LRUCache.js +1 -375
- package/dist/utils/LRUCache.js.map +1 -1
- package/dist/utils/frameTime.js +14 -0
- package/dist/utils/frameTime.js.map +1 -0
- package/package.json +3 -3
- package/test/profilingPlugin.ts +223 -0
- package/test/recordReplayProxyPlugin.js +22 -27
- package/test/thumbnail-performance-test.html +116 -0
- package/test/visualRegressionUtils.ts +286 -0
- package/types.json +1 -1
- package/dist/elements/TimegroupController.d.ts +0 -18
- package/dist/msToTimeCode.js +0 -17
- package/dist/msToTimeCode.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deepGetTemporalElements } from "./EFTemporal.js";
|
|
1
|
+
import { deepGetTemporalElements, isEFTemporal } from "./EFTemporal.js";
|
|
2
2
|
|
|
3
3
|
//#region src/elements/updateAnimations.ts
|
|
4
4
|
const ANIMATION_PRECISION_OFFSET = .1;
|
|
@@ -8,144 +8,527 @@ const DURATION_PROPERTY = "--ef-duration";
|
|
|
8
8
|
const TRANSITION_DURATION_PROPERTY = "--ef-transition-duration";
|
|
9
9
|
const TRANSITION_OUT_START_PROPERTY = "--ef-transition-out-start";
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Tracks animations per element to prevent them from being lost when they complete.
|
|
12
|
+
* Once an animation reaches 100% completion, it's removed from getAnimations(),
|
|
13
|
+
* but we keep a reference to it so we can continue controlling it.
|
|
14
|
+
*/
|
|
15
|
+
const animationTracker = /* @__PURE__ */ new WeakMap();
|
|
16
|
+
/**
|
|
17
|
+
* Tracks whether DOM structure has changed for an element, requiring animation rediscovery.
|
|
18
|
+
* For render clones (static DOM), this stays false after initial discovery.
|
|
19
|
+
* For prime timeline (interactive), this is set to true when mutations occur.
|
|
20
|
+
*/
|
|
21
|
+
const domStructureChanged = /* @__PURE__ */ new WeakMap();
|
|
22
|
+
/**
|
|
23
|
+
* Tracks the last known animation count for an element to detect new animations.
|
|
24
|
+
* Used as a lightweight check before calling expensive getAnimations().
|
|
25
|
+
*/
|
|
26
|
+
const lastAnimationCount = /* @__PURE__ */ new WeakMap();
|
|
27
|
+
/**
|
|
28
|
+
* Checks if an element is in a render clone (static DOM context).
|
|
29
|
+
* Render clones are in containers with class "ef-render-clone-container".
|
|
30
|
+
*/
|
|
31
|
+
const isRenderClone = (element) => {
|
|
32
|
+
return element.closest(".ef-render-clone-container") !== null;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Validates that an animation is still valid and controllable.
|
|
36
|
+
* Animations become invalid when:
|
|
37
|
+
* - They've been cancelled (idle state and not in getAnimations())
|
|
38
|
+
* - Their effect is null (animation was removed)
|
|
39
|
+
* - Their target is no longer in the DOM
|
|
40
|
+
*/
|
|
41
|
+
const isAnimationValid = (animation, currentAnimations) => {
|
|
42
|
+
if (animation.playState === "idle" && !currentAnimations.includes(animation)) return false;
|
|
43
|
+
const effect = animation.effect;
|
|
44
|
+
if (!effect) return false;
|
|
45
|
+
if (effect instanceof KeyframeEffect) {
|
|
46
|
+
const target = effect.target;
|
|
47
|
+
if (target && target instanceof Element) {
|
|
48
|
+
if (!target.isConnected) return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Discovers and tracks animations on an element and its subtree.
|
|
55
|
+
* This ensures we have references to animations even after they complete.
|
|
56
|
+
*
|
|
57
|
+
* Tracks animations per element where they exist, not just on the root element.
|
|
58
|
+
* This allows us to find animations on any element in the subtree.
|
|
59
|
+
*
|
|
60
|
+
* OPTIMIZATION: For render clones (static DOM), discovery happens once at creation.
|
|
61
|
+
* For prime timeline (interactive), discovery is responsive to DOM changes.
|
|
62
|
+
*
|
|
63
|
+
* Also cleans up invalid animations (cancelled, removed from DOM, etc.)
|
|
64
|
+
*/
|
|
65
|
+
const discoverAndTrackAnimations = (element) => {
|
|
66
|
+
const isClone = isRenderClone(element);
|
|
67
|
+
const hasTrackedAnimations = animationTracker.has(element);
|
|
68
|
+
const structureChanged = domStructureChanged.get(element) ?? true;
|
|
69
|
+
if (isClone && hasTrackedAnimations && !structureChanged) {
|
|
70
|
+
const rootTracked$1 = animationTracker.get(element);
|
|
71
|
+
const currentAnimations$1 = [];
|
|
72
|
+
const rootDirectAnimations = element.getAnimations();
|
|
73
|
+
for (const animation of rootTracked$1) if (isAnimationValid(animation, rootDirectAnimations)) currentAnimations$1.push(animation);
|
|
74
|
+
return {
|
|
75
|
+
tracked: rootTracked$1,
|
|
76
|
+
current: currentAnimations$1
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const currentAnimations = element.getAnimations({ subtree: true });
|
|
80
|
+
if (isClone) domStructureChanged.set(element, false);
|
|
81
|
+
lastAnimationCount.set(element, currentAnimations.length);
|
|
82
|
+
for (const animation of currentAnimations) {
|
|
83
|
+
const effect = animation.effect;
|
|
84
|
+
const target = effect && effect instanceof KeyframeEffect ? effect.target : null;
|
|
85
|
+
if (target && target instanceof Element) {
|
|
86
|
+
let tracked = animationTracker.get(target);
|
|
87
|
+
if (!tracked) {
|
|
88
|
+
tracked = /* @__PURE__ */ new Set();
|
|
89
|
+
animationTracker.set(target, tracked);
|
|
90
|
+
}
|
|
91
|
+
tracked.add(animation);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
let rootTracked = animationTracker.get(element);
|
|
95
|
+
if (!rootTracked) {
|
|
96
|
+
rootTracked = /* @__PURE__ */ new Set();
|
|
97
|
+
animationTracker.set(element, rootTracked);
|
|
98
|
+
}
|
|
99
|
+
for (const animation of currentAnimations) rootTracked.add(animation);
|
|
100
|
+
for (const animation of rootTracked) if (!isAnimationValid(animation, currentAnimations)) rootTracked.delete(animation);
|
|
101
|
+
const allTargets = /* @__PURE__ */ new Set();
|
|
102
|
+
for (const animation of currentAnimations) {
|
|
103
|
+
const effect = animation.effect;
|
|
104
|
+
const target = effect && effect instanceof KeyframeEffect ? effect.target : null;
|
|
105
|
+
if (target && target instanceof Element) allTargets.add(target);
|
|
106
|
+
}
|
|
107
|
+
const subtreeElements = element.querySelectorAll("*");
|
|
108
|
+
for (const el of subtreeElements) {
|
|
109
|
+
const tracked = animationTracker.get(el);
|
|
110
|
+
if (tracked) {
|
|
111
|
+
for (const animation of tracked) if (!isAnimationValid(animation, Array.from(el.getAnimations()))) tracked.delete(animation);
|
|
112
|
+
if (tracked.size === 0) animationTracker.delete(el);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
tracked: rootTracked,
|
|
117
|
+
current: currentAnimations
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Cleans up tracked animations when an element is disconnected.
|
|
122
|
+
* This prevents memory leaks.
|
|
123
|
+
*/
|
|
124
|
+
const cleanupTrackedAnimations = (element) => {
|
|
125
|
+
animationTracker.delete(element);
|
|
126
|
+
domStructureChanged.delete(element);
|
|
127
|
+
lastAnimationCount.delete(element);
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Determines what phase an element is in relative to the timeline.
|
|
131
|
+
*
|
|
132
|
+
* WHY: Phase is the primary concept that drives all decisions. By explicitly
|
|
133
|
+
* enumerating phases, we make the code's logic clear: phase determines visibility,
|
|
134
|
+
* animation coordination, and visual state.
|
|
135
|
+
*
|
|
136
|
+
* Phases:
|
|
137
|
+
* - before-start: Timeline is before element's start time
|
|
138
|
+
* - active: Timeline is within element's active range (start to end, exclusive of end)
|
|
139
|
+
* - at-end-boundary: Timeline is exactly at element's end time
|
|
140
|
+
* - after-end: Timeline is after element's end time
|
|
141
|
+
*
|
|
142
|
+
* Note: We detect "at-end-boundary" by checking if timeline equals end time.
|
|
143
|
+
* The boundary policy will then determine if this should be treated as visible/active
|
|
144
|
+
* or not based on element characteristics.
|
|
145
|
+
*/
|
|
146
|
+
const determineElementPhase = (element, timelineTimeMs) => {
|
|
147
|
+
const endTimeMs = element.endTimeMs;
|
|
148
|
+
const startTimeMs = element.startTimeMs;
|
|
149
|
+
if (endTimeMs <= startTimeMs) return "active";
|
|
150
|
+
if (timelineTimeMs < startTimeMs) return "before-start";
|
|
151
|
+
const epsilon = .001;
|
|
152
|
+
const diff = timelineTimeMs - endTimeMs;
|
|
153
|
+
if (diff > epsilon) return "after-end";
|
|
154
|
+
if (Math.abs(diff) <= epsilon) return "at-end-boundary";
|
|
155
|
+
return "active";
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Visibility policy: determines when elements should be visible for display purposes.
|
|
159
|
+
*
|
|
160
|
+
* WHY: Root elements, elements aligned with composition end, and text segments
|
|
161
|
+
* should remain visible at exact end time to prevent flicker and show final frames.
|
|
162
|
+
* Other elements use exclusive end for clean transitions between elements.
|
|
163
|
+
*/
|
|
164
|
+
var VisibilityPolicy = class {
|
|
165
|
+
shouldIncludeEndBoundary(element) {
|
|
166
|
+
if (!element.parentTimegroup) return true;
|
|
167
|
+
if (element.endTimeMs === element.rootTimegroup?.endTimeMs) return true;
|
|
168
|
+
if (this.isTextSegment(element)) return true;
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Checks if element is a text segment.
|
|
173
|
+
* Encapsulates the tag name check to hide implementation detail.
|
|
174
|
+
*/
|
|
175
|
+
isTextSegment(element) {
|
|
176
|
+
return element.tagName === "EF-TEXT-SEGMENT";
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Animation policy: determines when animations should be coordinated.
|
|
181
|
+
*
|
|
182
|
+
* WHY: When an animation reaches exactly the end time of an element, using exclusive
|
|
183
|
+
* end would make the element invisible, causing the animation to be removed from the
|
|
184
|
+
* DOM and creating a visual jump. By using inclusive end, we ensure animations remain
|
|
185
|
+
* coordinated even at exact boundary times, providing smooth visual transitions.
|
|
186
|
+
*/
|
|
187
|
+
var AnimationPolicy = class {
|
|
188
|
+
shouldIncludeEndBoundary(_element) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const visibilityPolicy = new VisibilityPolicy();
|
|
193
|
+
const animationPolicy = new AnimationPolicy();
|
|
194
|
+
/**
|
|
195
|
+
* Determines if an element should be visible based on its phase and visibility policy.
|
|
196
|
+
*/
|
|
197
|
+
const shouldBeVisible = (phase, element) => {
|
|
198
|
+
if (phase === "before-start" || phase === "after-end") return false;
|
|
199
|
+
if (phase === "active") return true;
|
|
200
|
+
return visibilityPolicy.shouldIncludeEndBoundary(element);
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Determines if animations should be coordinated based on element phase and animation policy.
|
|
204
|
+
*/
|
|
205
|
+
const shouldCoordinateAnimations = (phase, element) => {
|
|
206
|
+
if (phase === "before-start" || phase === "after-end") return false;
|
|
207
|
+
if (phase === "active") return true;
|
|
208
|
+
return animationPolicy.shouldIncludeEndBoundary(element);
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Evaluates what the element's state should be based on the timeline.
|
|
212
|
+
*
|
|
213
|
+
* WHY: This function determines the complete temporal state including phase,
|
|
214
|
+
* which becomes the primary driver for all subsequent decisions.
|
|
12
215
|
*/
|
|
13
216
|
const evaluateTemporalState = (element) => {
|
|
14
217
|
const timelineTimeMs = (element.rootTimegroup ?? element).currentTimeMs;
|
|
15
218
|
const progress = element.durationMs <= 0 ? 1 : Math.max(0, Math.min(1, element.currentTimeMs / element.durationMs));
|
|
16
|
-
const
|
|
17
|
-
const isLastElementInComposition = element.endTimeMs === element.rootTimegroup?.endTimeMs;
|
|
18
|
-
const isTextSegment = element.tagName === "EF-TEXT-SEGMENT";
|
|
19
|
-
const useInclusiveEnd = isRootElement || isLastElementInComposition || isTextSegment;
|
|
219
|
+
const phase = determineElementPhase(element, timelineTimeMs);
|
|
20
220
|
return {
|
|
21
221
|
progress,
|
|
22
|
-
isVisible:
|
|
23
|
-
timelineTimeMs
|
|
222
|
+
isVisible: shouldBeVisible(phase, element),
|
|
223
|
+
timelineTimeMs,
|
|
224
|
+
phase
|
|
24
225
|
};
|
|
25
226
|
};
|
|
26
227
|
/**
|
|
27
|
-
* Evaluates element visibility specifically for animation coordination
|
|
28
|
-
* Uses inclusive end boundaries to prevent animation jumps at exact boundaries
|
|
228
|
+
* Evaluates element visibility state specifically for animation coordination.
|
|
229
|
+
* Uses inclusive end boundaries to prevent animation jumps at exact boundaries.
|
|
230
|
+
*
|
|
231
|
+
* This is exported for external use cases that need animation-specific visibility
|
|
232
|
+
* evaluation without the full ElementUpdateContext.
|
|
29
233
|
*/
|
|
30
|
-
const
|
|
31
|
-
const
|
|
234
|
+
const evaluateAnimationVisibilityState = (element) => {
|
|
235
|
+
const state = evaluateTemporalState(element);
|
|
236
|
+
const shouldCoordinate = shouldCoordinateAnimations(state.phase, element);
|
|
32
237
|
return {
|
|
33
|
-
|
|
34
|
-
isVisible:
|
|
35
|
-
timelineTimeMs
|
|
238
|
+
...state,
|
|
239
|
+
isVisible: shouldCoordinate
|
|
36
240
|
};
|
|
37
241
|
};
|
|
38
242
|
/**
|
|
39
|
-
*
|
|
243
|
+
* Capability check: determines if an element supports stagger offset.
|
|
244
|
+
* Encapsulates the knowledge of which element types support this feature.
|
|
40
245
|
*/
|
|
41
|
-
const
|
|
42
|
-
element.
|
|
246
|
+
const supportsStaggerOffset = (element) => {
|
|
247
|
+
return element.tagName === "EF-TEXT-SEGMENT";
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* Calculates effective delay including stagger offset if applicable.
|
|
251
|
+
*
|
|
252
|
+
* Stagger offset allows elements (like text segments) to have their animations
|
|
253
|
+
* start at different times while keeping their visibility timing unchanged.
|
|
254
|
+
* This enables staggered animation effects within a single timegroup.
|
|
255
|
+
*/
|
|
256
|
+
const calculateEffectiveDelay = (delay, element) => {
|
|
257
|
+
if (supportsStaggerOffset(element)) {
|
|
258
|
+
const segment = element;
|
|
259
|
+
if (segment.staggerOffsetMs !== void 0 && segment.staggerOffsetMs !== null) return delay + segment.staggerOffsetMs;
|
|
260
|
+
let cssValue = element.style.getPropertyValue("--ef-stagger-offset").trim();
|
|
261
|
+
if (!cssValue) cssValue = window.getComputedStyle(element).getPropertyValue("--ef-stagger-offset").trim();
|
|
262
|
+
if (cssValue) {
|
|
263
|
+
const match = cssValue.match(/(\d+(?:\.\d+)?)\s*ms?/);
|
|
264
|
+
if (match) {
|
|
265
|
+
const staggerOffset = parseFloat(match[1]);
|
|
266
|
+
if (!isNaN(staggerOffset)) return delay + staggerOffset;
|
|
267
|
+
} else {
|
|
268
|
+
const numValue = parseFloat(cssValue);
|
|
269
|
+
if (!isNaN(numValue)) return delay + numValue;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return delay;
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* Calculates maximum safe animation time to prevent completion.
|
|
277
|
+
*
|
|
278
|
+
* WHY: Once an animation reaches "finished" state, it can no longer be manually controlled
|
|
279
|
+
* via currentTime. By clamping to just before completion (using ANIMATION_PRECISION_OFFSET),
|
|
280
|
+
* we ensure the animation remains in a controllable state, allowing us to synchronize it
|
|
281
|
+
* with the timeline even when it would naturally be complete.
|
|
282
|
+
*/
|
|
283
|
+
const calculateMaxSafeAnimationTime = (duration, iterations) => {
|
|
284
|
+
return duration * iterations - ANIMATION_PRECISION_OFFSET;
|
|
285
|
+
};
|
|
286
|
+
/**
|
|
287
|
+
* Determines if the current iteration should be reversed based on direction
|
|
288
|
+
*/
|
|
289
|
+
const shouldReverseIteration = (direction, currentIteration) => {
|
|
290
|
+
return direction === "reverse" || direction === "alternate" && currentIteration % 2 === 1 || direction === "alternate-reverse" && currentIteration % 2 === 0;
|
|
291
|
+
};
|
|
292
|
+
/**
|
|
293
|
+
* Applies direction to iteration time (reverses if needed)
|
|
294
|
+
*/
|
|
295
|
+
const applyDirectionToIterationTime = (currentIterationTime, duration, direction, currentIteration) => {
|
|
296
|
+
if (shouldReverseIteration(direction, currentIteration)) return duration - currentIterationTime;
|
|
297
|
+
return currentIterationTime;
|
|
298
|
+
};
|
|
299
|
+
/**
|
|
300
|
+
* Maps element time to animation time for normal direction.
|
|
301
|
+
* Uses cumulative time throughout the animation.
|
|
302
|
+
* Note: elementTime should already be adjusted (elementTime - effectiveDelay).
|
|
303
|
+
*/
|
|
304
|
+
const mapNormalDirectionTime = (elementTime, duration, maxSafeTime) => {
|
|
305
|
+
const currentIteration = Math.floor(elementTime / duration);
|
|
306
|
+
const iterationTime = elementTime % duration;
|
|
307
|
+
const cumulativeTime = currentIteration * duration + iterationTime;
|
|
308
|
+
return Math.min(cumulativeTime, maxSafeTime);
|
|
309
|
+
};
|
|
310
|
+
/**
|
|
311
|
+
* Maps element time to animation time for reverse direction.
|
|
312
|
+
* Uses cumulative time with reversed iterations.
|
|
313
|
+
* Note: elementTime should already be adjusted (elementTime - effectiveDelay).
|
|
314
|
+
*/
|
|
315
|
+
const mapReverseDirectionTime = (elementTime, duration, maxSafeTime) => {
|
|
316
|
+
const currentIteration = Math.floor(elementTime / duration);
|
|
317
|
+
const reversedIterationTime = duration - elementTime % duration;
|
|
318
|
+
const cumulativeTime = currentIteration * duration + reversedIterationTime;
|
|
319
|
+
return Math.min(cumulativeTime, maxSafeTime);
|
|
320
|
+
};
|
|
321
|
+
/**
|
|
322
|
+
* Maps element time to animation time for alternate/alternate-reverse directions.
|
|
323
|
+
*
|
|
324
|
+
* WHY SPECIAL HANDLING: Alternate directions oscillate between forward and reverse iterations.
|
|
325
|
+
* Without delay, we use iteration time (0 to duration) because the animation naturally
|
|
326
|
+
* resets each iteration. However, with delay, iteration 0 needs to account for the delay
|
|
327
|
+
* offset (using ownCurrentTimeMs), and later iterations need cumulative time to properly
|
|
328
|
+
* track progress across multiple iterations. This complexity requires a dedicated mapper
|
|
329
|
+
* rather than trying to handle it in the general case.
|
|
330
|
+
*/
|
|
331
|
+
const mapAlternateDirectionTime = (elementTime, effectiveDelay, duration, direction, maxSafeTime) => {
|
|
332
|
+
const adjustedTime = elementTime - effectiveDelay;
|
|
333
|
+
if (effectiveDelay > 0) {
|
|
334
|
+
if (Math.floor(adjustedTime / duration) === 0) return Math.min(elementTime, maxSafeTime);
|
|
335
|
+
return Math.min(adjustedTime, maxSafeTime);
|
|
336
|
+
}
|
|
337
|
+
const currentIteration = Math.floor(elementTime / duration);
|
|
338
|
+
const iterationTime = applyDirectionToIterationTime(elementTime % duration, duration, direction, currentIteration);
|
|
339
|
+
return Math.min(iterationTime, maxSafeTime);
|
|
340
|
+
};
|
|
341
|
+
/**
|
|
342
|
+
* Maps element time to animation time based on direction.
|
|
343
|
+
*
|
|
344
|
+
* WHY: This function explicitly transforms element time to animation time, making
|
|
345
|
+
* the time mapping concept clear. Different directions require different transformations
|
|
346
|
+
* to achieve the desired visual effect.
|
|
347
|
+
*/
|
|
348
|
+
const mapElementTimeToAnimationTime = (elementTime, timing, effectiveDelay) => {
|
|
349
|
+
const { duration, iterations, direction } = timing;
|
|
350
|
+
const maxSafeTime = calculateMaxSafeAnimationTime(duration, iterations);
|
|
351
|
+
const adjustedTime = elementTime - effectiveDelay;
|
|
352
|
+
if (direction === "reverse") return mapReverseDirectionTime(adjustedTime, duration, maxSafeTime);
|
|
353
|
+
if (direction === "alternate" || direction === "alternate-reverse") return mapAlternateDirectionTime(elementTime, effectiveDelay, duration, direction, maxSafeTime);
|
|
354
|
+
return mapNormalDirectionTime(adjustedTime, duration, maxSafeTime);
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
357
|
+
* Determines the animation time for a completed animation based on direction.
|
|
358
|
+
*/
|
|
359
|
+
const getCompletedAnimationTime = (timing, maxSafeTime) => {
|
|
360
|
+
const { direction, iterations, duration } = timing;
|
|
361
|
+
if (direction === "alternate" || direction === "alternate-reverse") {
|
|
362
|
+
const finalIteration = iterations - 1;
|
|
363
|
+
if (direction === "alternate" && finalIteration % 2 === 1 || direction === "alternate-reverse" && finalIteration % 2 === 0) return Math.min(duration - ANIMATION_PRECISION_OFFSET, maxSafeTime);
|
|
364
|
+
}
|
|
365
|
+
return maxSafeTime;
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* Validates that animation effect is a KeyframeEffect with a target
|
|
369
|
+
*/
|
|
370
|
+
const validateAnimationEffect = (effect) => {
|
|
371
|
+
return effect !== null && effect instanceof KeyframeEffect && effect.target !== null;
|
|
372
|
+
};
|
|
373
|
+
/**
|
|
374
|
+
* Extracts timing information from an animation effect.
|
|
375
|
+
* Duration and delay from getTiming() are already in milliseconds.
|
|
376
|
+
* We use getTiming().delay directly from the animation object.
|
|
377
|
+
*/
|
|
378
|
+
const extractAnimationTiming = (effect) => {
|
|
379
|
+
const timing = effect.getTiming();
|
|
380
|
+
return {
|
|
381
|
+
duration: Number(timing.duration) || 0,
|
|
382
|
+
delay: Number(timing.delay) || 0,
|
|
383
|
+
iterations: Number(timing.iterations) || DEFAULT_ANIMATION_ITERATIONS,
|
|
384
|
+
direction: timing.direction || "normal"
|
|
385
|
+
};
|
|
386
|
+
};
|
|
387
|
+
/**
|
|
388
|
+
* Prepares animation for manual control by ensuring it's paused
|
|
389
|
+
*/
|
|
390
|
+
const prepareAnimation = (animation) => {
|
|
391
|
+
if (animation.playState === "finished") animation.cancel();
|
|
392
|
+
else if (animation.playState === "running") animation.pause();
|
|
393
|
+
};
|
|
394
|
+
/**
|
|
395
|
+
* Maps element time to animation currentTime and sets it on the animation.
|
|
396
|
+
*
|
|
397
|
+
* WHY: This function explicitly performs the time mapping transformation,
|
|
398
|
+
* making it clear that we're transforming element time to animation time.
|
|
399
|
+
*/
|
|
400
|
+
const mapAndSetAnimationTime = (animation, element, timing, effectiveDelay) => {
|
|
401
|
+
const elementTime = element.ownCurrentTimeMs ?? 0;
|
|
402
|
+
if (animation.playState === "running") animation.pause();
|
|
403
|
+
const adjustedTime = elementTime - effectiveDelay;
|
|
404
|
+
if (adjustedTime < 0) {
|
|
405
|
+
if (timing.delay > 0) animation.currentTime = elementTime;
|
|
406
|
+
else animation.currentTime = 0;
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const { duration, iterations } = timing;
|
|
410
|
+
const currentIteration = Math.floor(adjustedTime / duration);
|
|
411
|
+
if (currentIteration >= iterations) {
|
|
412
|
+
const completedAnimationTime = getCompletedAnimationTime(timing, calculateMaxSafeAnimationTime(duration, iterations));
|
|
413
|
+
if (timing.delay > 0) animation.currentTime = effectiveDelay + completedAnimationTime;
|
|
414
|
+
else animation.currentTime = completedAnimationTime;
|
|
415
|
+
} else {
|
|
416
|
+
const animationTime = mapElementTimeToAnimationTime(elementTime, timing, effectiveDelay);
|
|
417
|
+
const { direction, delay } = timing;
|
|
418
|
+
if (delay > 0) if ((direction === "alternate" || direction === "alternate-reverse") && effectiveDelay > 0 && currentIteration === 0) animation.currentTime = elementTime;
|
|
419
|
+
else animation.currentTime = effectiveDelay + animationTime;
|
|
420
|
+
else animation.currentTime = animationTime;
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
/**
|
|
424
|
+
* Synchronizes a single animation with the timeline using the element as the time source.
|
|
425
|
+
*
|
|
426
|
+
* For animations in this element's subtree, always use this element as the time source.
|
|
427
|
+
* This handles both animations directly on the temporal element and on its non-temporal children.
|
|
428
|
+
*/
|
|
429
|
+
const synchronizeAnimation = (animation, element) => {
|
|
430
|
+
const effect = animation.effect;
|
|
431
|
+
if (!validateAnimationEffect(effect)) return;
|
|
432
|
+
const timing = extractAnimationTiming(effect);
|
|
433
|
+
if (timing.duration <= 0) {
|
|
434
|
+
animation.currentTime = 0;
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const target = effect.target;
|
|
438
|
+
let timeSource = element;
|
|
439
|
+
if (target && target instanceof HTMLElement) {
|
|
440
|
+
const nearestTimegroup = target.closest("ef-timegroup");
|
|
441
|
+
if (nearestTimegroup && isEFTemporal(nearestTimegroup)) timeSource = nearestTimegroup;
|
|
442
|
+
}
|
|
443
|
+
let staggerElement = timeSource;
|
|
444
|
+
if (target && target instanceof HTMLElement) {
|
|
445
|
+
const targetAsAnimatable = target;
|
|
446
|
+
if (supportsStaggerOffset(targetAsAnimatable)) staggerElement = targetAsAnimatable;
|
|
447
|
+
else {
|
|
448
|
+
const parentSegment = target.closest("ef-text-segment");
|
|
449
|
+
if (parentSegment && supportsStaggerOffset(parentSegment)) staggerElement = parentSegment;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
const effectiveDelay = calculateEffectiveDelay(timing.delay, staggerElement);
|
|
453
|
+
mapAndSetAnimationTime(animation, timeSource, timing, effectiveDelay);
|
|
454
|
+
};
|
|
455
|
+
/**
|
|
456
|
+
* Coordinates animations for a single element and its subtree, using the element as the time source.
|
|
457
|
+
*
|
|
458
|
+
* Uses tracked animations to ensure we can control animations even after they complete.
|
|
459
|
+
* Both CSS animations (created via the 'animation' property) and WAAPI animations are included.
|
|
460
|
+
*
|
|
461
|
+
* CRITICAL: CSS animations are created asynchronously when classes are added. This function
|
|
462
|
+
* discovers new animations on each call and tracks them in memory. Once animations complete,
|
|
463
|
+
* they're removed from getAnimations(), but we keep references to them so we can continue
|
|
464
|
+
* controlling them.
|
|
465
|
+
*/
|
|
466
|
+
const coordinateElementAnimations = (element) => {
|
|
467
|
+
const { tracked: trackedAnimations, current: currentAnimations } = discoverAndTrackAnimations(element);
|
|
468
|
+
for (const animation of trackedAnimations) {
|
|
469
|
+
if (!isAnimationValid(animation, currentAnimations)) continue;
|
|
470
|
+
prepareAnimation(animation);
|
|
471
|
+
synchronizeAnimation(animation, element);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
/**
|
|
475
|
+
* Applies visual state (CSS + display) to match temporal state.
|
|
476
|
+
*
|
|
477
|
+
* WHY: This function applies visual state based on the element's phase and state.
|
|
478
|
+
* Phase determines what should be visible, and this function applies that decision.
|
|
479
|
+
*/
|
|
480
|
+
const applyVisualState = (element, state) => {
|
|
481
|
+
element.style.setProperty(PROGRESS_PROPERTY, `${state.progress}`);
|
|
43
482
|
if (!state.isVisible) {
|
|
44
|
-
|
|
483
|
+
element.style.setProperty("display", "none");
|
|
45
484
|
return;
|
|
46
485
|
}
|
|
47
|
-
|
|
486
|
+
element.style.removeProperty("display");
|
|
48
487
|
element.style.setProperty(DURATION_PROPERTY, `${element.durationMs}ms`);
|
|
49
488
|
element.style.setProperty(TRANSITION_DURATION_PROPERTY, `${element.parentTimegroup?.overlapMs ?? 0}ms`);
|
|
50
489
|
element.style.setProperty(TRANSITION_OUT_START_PROPERTY, `${element.durationMs - (element.parentTimegroup?.overlapMs ?? 0)}ms`);
|
|
51
490
|
};
|
|
52
491
|
/**
|
|
53
|
-
*
|
|
492
|
+
* Applies animation coordination if the element phase requires it.
|
|
493
|
+
*
|
|
494
|
+
* WHY: Animation coordination is driven by phase. If the element is in a phase
|
|
495
|
+
* where animations should be coordinated, we coordinate them.
|
|
54
496
|
*/
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const timing = effect.getTiming();
|
|
68
|
-
const duration = Number(timing.duration) || 0;
|
|
69
|
-
let delay = Number(timing.delay) || 0;
|
|
70
|
-
if (target instanceof HTMLElement) {
|
|
71
|
-
const animationDelays = window.getComputedStyle(target).animationDelay.split(", ").map((s) => s.trim());
|
|
72
|
-
const parseDelay = (delayStr) => {
|
|
73
|
-
if (delayStr === "0s" || delayStr === "0ms") return 0;
|
|
74
|
-
const delayMatch = delayStr.match(/^([\d.]+)(s|ms)$/);
|
|
75
|
-
if (delayMatch?.[1] && delayMatch[2]) {
|
|
76
|
-
const value = Number.parseFloat(delayMatch[1]);
|
|
77
|
-
return delayMatch[2] === "s" ? value * 1e3 : value;
|
|
78
|
-
}
|
|
79
|
-
return 0;
|
|
80
|
-
};
|
|
81
|
-
if (animationDelays.length === 1 && animationDelays[0]) {
|
|
82
|
-
const parsedDelay = parseDelay(animationDelays[0]);
|
|
83
|
-
if (parsedDelay > 0) delay = parsedDelay;
|
|
84
|
-
else if ((animationDelays[0] === "0s" || animationDelays[0] === "0ms") && delay === 0) delay = 0;
|
|
85
|
-
} else if (animationDelays.length > 1) {
|
|
86
|
-
const animationIndex = Array.from(target.getAnimations()).indexOf(animation);
|
|
87
|
-
if (animationIndex >= 0 && animationIndex < animationDelays.length && animationDelays[animationIndex]) {
|
|
88
|
-
const parsedDelay = parseDelay(animationDelays[animationIndex]);
|
|
89
|
-
if (parsedDelay > 0) delay = parsedDelay;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const iterations = Number(timing.iterations) || DEFAULT_ANIMATION_ITERATIONS;
|
|
94
|
-
if (duration <= 0) {
|
|
95
|
-
animation.currentTime = 0;
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
const currentTime = element.ownCurrentTimeMs ?? 0;
|
|
99
|
-
let effectiveDelay = delay;
|
|
100
|
-
if (element.tagName === "EF-TEXT-SEGMENT" && element.staggerOffsetMs !== void 0) {
|
|
101
|
-
const staggerOffsetMs = element.staggerOffsetMs;
|
|
102
|
-
effectiveDelay = delay + staggerOffsetMs;
|
|
103
|
-
}
|
|
104
|
-
if (currentTime < effectiveDelay) {
|
|
105
|
-
animation.currentTime = 0;
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
const adjustedTime = currentTime - effectiveDelay;
|
|
109
|
-
const currentIteration = Math.floor(adjustedTime / duration);
|
|
110
|
-
let currentIterationTime = adjustedTime % duration;
|
|
111
|
-
const direction = timing.direction || "normal";
|
|
112
|
-
const isAlternate = direction === "alternate" || direction === "alternate-reverse";
|
|
113
|
-
if (direction === "reverse" || direction === "alternate" && currentIteration % 2 === 1 || direction === "alternate-reverse" && currentIteration % 2 === 0) currentIterationTime = duration - currentIterationTime;
|
|
114
|
-
if (currentIteration >= iterations) {
|
|
115
|
-
const maxSafeAnimationTime = duration * iterations - ANIMATION_PRECISION_OFFSET;
|
|
116
|
-
if (isAlternate) {
|
|
117
|
-
const finalIteration = iterations - 1;
|
|
118
|
-
if (direction === "alternate" && finalIteration % 2 === 1 || direction === "alternate-reverse" && finalIteration % 2 === 0) animation.currentTime = Math.min(duration - ANIMATION_PRECISION_OFFSET, maxSafeAnimationTime);
|
|
119
|
-
else animation.currentTime = maxSafeAnimationTime;
|
|
120
|
-
} else animation.currentTime = maxSafeAnimationTime;
|
|
121
|
-
} else if (isAlternate) if (effectiveDelay > 0) if (currentIteration === 0) animation.currentTime = currentTime;
|
|
122
|
-
else {
|
|
123
|
-
const maxSafeAnimationTime = duration * iterations - ANIMATION_PRECISION_OFFSET;
|
|
124
|
-
animation.currentTime = Math.min(adjustedTime, maxSafeAnimationTime);
|
|
125
|
-
}
|
|
126
|
-
else animation.currentTime = currentIterationTime;
|
|
127
|
-
else {
|
|
128
|
-
const timeWithinAnimation = currentIteration * duration + currentIterationTime;
|
|
129
|
-
const maxSafeAnimationTime = duration * iterations - ANIMATION_PRECISION_OFFSET;
|
|
130
|
-
animation.currentTime = Math.min(timeWithinAnimation, maxSafeAnimationTime);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
497
|
+
const applyAnimationCoordination = (element, phase) => {
|
|
498
|
+
if (shouldCoordinateAnimations(phase, element)) coordinateElementAnimations(element);
|
|
499
|
+
};
|
|
500
|
+
/**
|
|
501
|
+
* Evaluates the complete state for an element update.
|
|
502
|
+
* This separates evaluation (what should the state be?) from application (apply that state).
|
|
503
|
+
*/
|
|
504
|
+
const evaluateElementState = (element) => {
|
|
505
|
+
return {
|
|
506
|
+
element,
|
|
507
|
+
state: evaluateTemporalState(element)
|
|
508
|
+
};
|
|
133
509
|
};
|
|
134
510
|
/**
|
|
135
|
-
* Main function: synchronizes DOM element with timeline
|
|
511
|
+
* Main function: synchronizes DOM element with timeline.
|
|
512
|
+
*
|
|
513
|
+
* Orchestrates clear flow: Phase → Policy → Time Mapping → State Application
|
|
514
|
+
*
|
|
515
|
+
* WHY: This function makes the conceptual flow explicit:
|
|
516
|
+
* 1. Determine phase (what phase is the element in?)
|
|
517
|
+
* 2. Apply policies (should it be visible/coordinated based on phase?)
|
|
518
|
+
* 3. Map time for animations (transform element time to animation time)
|
|
519
|
+
* 4. Apply visual state (update CSS and display based on phase and policies)
|
|
136
520
|
*/
|
|
137
521
|
const updateAnimations = (element) => {
|
|
138
|
-
const
|
|
139
|
-
deepGetTemporalElements(element).
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (evaluateTemporalStateForAnimation(temporalElement).isVisible) coordinateAnimationsForSingleElement(temporalElement);
|
|
522
|
+
const rootContext = evaluateElementState(element);
|
|
523
|
+
const childContexts = deepGetTemporalElements(element).map((temporalElement) => evaluateElementState(temporalElement));
|
|
524
|
+
applyVisualState(rootContext.element, rootContext.state);
|
|
525
|
+
applyAnimationCoordination(rootContext.element, rootContext.state.phase);
|
|
526
|
+
childContexts.forEach((context) => {
|
|
527
|
+
applyVisualState(context.element, context.state);
|
|
528
|
+
applyAnimationCoordination(context.element, context.state.phase);
|
|
146
529
|
});
|
|
147
530
|
};
|
|
148
531
|
|
|
149
532
|
//#endregion
|
|
150
|
-
export {
|
|
533
|
+
export { cleanupTrackedAnimations, evaluateAnimationVisibilityState, updateAnimations };
|
|
151
534
|
//# sourceMappingURL=updateAnimations.js.map
|