@editframe/elements 0.26.2-beta.0 → 0.26.4-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/elements/EFTimegroup.js +7 -2
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/package.json +2 -2
- package/scripts/build-css.js +3 -3
- package/tsdown.config.ts +1 -1
- package/types.json +1 -1
- package/src/elements/ContextProxiesController.ts +0 -124
- package/src/elements/CrossUpdateController.ts +0 -22
- package/src/elements/EFAudio.browsertest.ts +0 -706
- package/src/elements/EFAudio.ts +0 -56
- package/src/elements/EFCaptions.browsertest.ts +0 -1960
- package/src/elements/EFCaptions.ts +0 -823
- package/src/elements/EFImage.browsertest.ts +0 -120
- package/src/elements/EFImage.ts +0 -113
- package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
- package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
- package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
- package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
- package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
- package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
- package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
- package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
- package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
- package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
- package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
- package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
- package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
- package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
- package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
- package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
- package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
- package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
- package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
- package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
- package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
- package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
- package/src/elements/EFMedia.browsertest.ts +0 -872
- package/src/elements/EFMedia.ts +0 -341
- package/src/elements/EFSourceMixin.ts +0 -60
- package/src/elements/EFSurface.browsertest.ts +0 -151
- package/src/elements/EFSurface.ts +0 -142
- package/src/elements/EFTemporal.browsertest.ts +0 -215
- package/src/elements/EFTemporal.ts +0 -800
- package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
- package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
- package/src/elements/EFThumbnailStrip.ts +0 -906
- package/src/elements/EFTimegroup.browsertest.ts +0 -870
- package/src/elements/EFTimegroup.ts +0 -878
- package/src/elements/EFVideo.browsertest.ts +0 -1482
- package/src/elements/EFVideo.ts +0 -564
- package/src/elements/EFWaveform.ts +0 -547
- package/src/elements/FetchContext.browsertest.ts +0 -401
- package/src/elements/FetchMixin.ts +0 -38
- package/src/elements/SampleBuffer.ts +0 -94
- package/src/elements/TargetController.browsertest.ts +0 -230
- package/src/elements/TargetController.ts +0 -224
- package/src/elements/TimegroupController.ts +0 -26
- package/src/elements/durationConverter.ts +0 -35
- package/src/elements/parseTimeToMs.ts +0 -9
- package/src/elements/printTaskStatus.ts +0 -16
- package/src/elements/renderTemporalAudio.ts +0 -108
- package/src/elements/updateAnimations.browsertest.ts +0 -1884
- package/src/elements/updateAnimations.ts +0 -217
- package/src/elements/util.ts +0 -24
- package/src/gui/ContextMixin.browsertest.ts +0 -860
- package/src/gui/ContextMixin.ts +0 -562
- package/src/gui/Controllable.browsertest.ts +0 -258
- package/src/gui/Controllable.ts +0 -41
- package/src/gui/EFConfiguration.ts +0 -40
- package/src/gui/EFControls.browsertest.ts +0 -389
- package/src/gui/EFControls.ts +0 -195
- package/src/gui/EFDial.browsertest.ts +0 -84
- package/src/gui/EFDial.ts +0 -172
- package/src/gui/EFFilmstrip.browsertest.ts +0 -712
- package/src/gui/EFFilmstrip.ts +0 -1349
- package/src/gui/EFFitScale.ts +0 -152
- package/src/gui/EFFocusOverlay.ts +0 -79
- package/src/gui/EFPause.browsertest.ts +0 -202
- package/src/gui/EFPause.ts +0 -73
- package/src/gui/EFPlay.browsertest.ts +0 -202
- package/src/gui/EFPlay.ts +0 -73
- package/src/gui/EFPreview.ts +0 -74
- package/src/gui/EFResizableBox.browsertest.ts +0 -79
- package/src/gui/EFResizableBox.ts +0 -898
- package/src/gui/EFScrubber.ts +0 -151
- package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
- package/src/gui/EFTimeDisplay.ts +0 -55
- package/src/gui/EFToggleLoop.ts +0 -35
- package/src/gui/EFTogglePlay.ts +0 -70
- package/src/gui/EFWorkbench.ts +0 -115
- package/src/gui/PlaybackController.ts +0 -527
- package/src/gui/TWMixin.css +0 -6
- package/src/gui/TWMixin.ts +0 -61
- package/src/gui/TargetOrContextMixin.ts +0 -185
- package/src/gui/currentTimeContext.ts +0 -5
- package/src/gui/durationContext.ts +0 -3
- package/src/gui/efContext.ts +0 -6
- package/src/gui/fetchContext.ts +0 -5
- package/src/gui/focusContext.ts +0 -7
- package/src/gui/focusedElementContext.ts +0 -5
- package/src/gui/playingContext.ts +0 -5
- package/src/otel/BridgeSpanExporter.ts +0 -150
- package/src/otel/setupBrowserTracing.ts +0 -73
- package/src/otel/tracingHelpers.ts +0 -251
- package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
- package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
- package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
- package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
- package/src/transcoding/types/index.ts +0 -312
- package/src/transcoding/utils/MediaUtils.ts +0 -63
- package/src/transcoding/utils/UrlGenerator.ts +0 -68
- package/src/transcoding/utils/constants.ts +0 -36
- package/src/utils/LRUCache.test.ts +0 -274
- package/src/utils/LRUCache.ts +0 -696
|
@@ -1,1884 +0,0 @@
|
|
|
1
|
-
import { LitElement } from "lit";
|
|
2
|
-
import { customElement } from "lit/decorators.js";
|
|
3
|
-
import { assert, beforeEach, describe, test } from "vitest";
|
|
4
|
-
import { EFTemporal } from "./EFTemporal.js";
|
|
5
|
-
import type { EFTimegroup } from "./EFTimegroup.js";
|
|
6
|
-
import {
|
|
7
|
-
type AnimatableElement,
|
|
8
|
-
updateAnimations,
|
|
9
|
-
} from "./updateAnimations.js";
|
|
10
|
-
|
|
11
|
-
import "./EFTimegroup.js";
|
|
12
|
-
|
|
13
|
-
// Create proper temporal test elements
|
|
14
|
-
@customElement("test-temporal-element")
|
|
15
|
-
class TestTemporalElement extends EFTemporal(LitElement) {
|
|
16
|
-
get intrinsicDurationMs() {
|
|
17
|
-
return this._durationMs;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
private _durationMs = 1000;
|
|
21
|
-
setDuration(duration: number) {
|
|
22
|
-
this._durationMs = duration;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
declare global {
|
|
27
|
-
interface HTMLElementTagNameMap {
|
|
28
|
-
"test-temporal-element": TestTemporalElement;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
beforeEach(() => {
|
|
33
|
-
// Clean up DOM
|
|
34
|
-
while (document.body.children.length) {
|
|
35
|
-
document.body.children[0]?.remove();
|
|
36
|
-
}
|
|
37
|
-
window.localStorage.clear();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
function createTestElement(
|
|
41
|
-
props: Partial<AnimatableElement> = {},
|
|
42
|
-
): AnimatableElement {
|
|
43
|
-
const element = document.createElement("div") as unknown as AnimatableElement;
|
|
44
|
-
// Override readonly properties for testing
|
|
45
|
-
Object.defineProperty(element, "currentTimeMs", {
|
|
46
|
-
value: props.currentTimeMs ?? 0,
|
|
47
|
-
writable: true,
|
|
48
|
-
});
|
|
49
|
-
Object.defineProperty(element, "durationMs", {
|
|
50
|
-
value: props.durationMs ?? 1000,
|
|
51
|
-
writable: true,
|
|
52
|
-
});
|
|
53
|
-
Object.defineProperty(element, "startTimeMs", {
|
|
54
|
-
value: props.startTimeMs ?? 0,
|
|
55
|
-
writable: true,
|
|
56
|
-
});
|
|
57
|
-
Object.defineProperty(element, "endTimeMs", {
|
|
58
|
-
value: props.endTimeMs ?? 1000,
|
|
59
|
-
writable: true,
|
|
60
|
-
});
|
|
61
|
-
Object.defineProperty(element, "rootTimegroup", {
|
|
62
|
-
value: props.rootTimegroup,
|
|
63
|
-
writable: true,
|
|
64
|
-
});
|
|
65
|
-
Object.defineProperty(element, "parentTimegroup", {
|
|
66
|
-
value: props.parentTimegroup,
|
|
67
|
-
writable: true,
|
|
68
|
-
});
|
|
69
|
-
Object.defineProperty(element, "ownCurrentTimeMs", {
|
|
70
|
-
value: props.ownCurrentTimeMs ?? 0,
|
|
71
|
-
writable: true,
|
|
72
|
-
});
|
|
73
|
-
document.body.appendChild(element);
|
|
74
|
-
return element;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
describe("Timeline Element Synchronizer", () => {
|
|
78
|
-
describe("CSS custom properties", () => {
|
|
79
|
-
test("sets --ef-progress based on currentTimeMs/durationMs ratio", () => {
|
|
80
|
-
const element = createTestElement({
|
|
81
|
-
currentTimeMs: 250,
|
|
82
|
-
durationMs: 1000,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
updateAnimations(element);
|
|
86
|
-
|
|
87
|
-
assert.equal(element.style.getPropertyValue("--ef-progress"), "25%");
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("clamps --ef-progress to 0-100% range", () => {
|
|
91
|
-
const element1 = createTestElement({
|
|
92
|
-
currentTimeMs: -100,
|
|
93
|
-
durationMs: 1000,
|
|
94
|
-
});
|
|
95
|
-
const element2 = createTestElement({
|
|
96
|
-
currentTimeMs: 1500,
|
|
97
|
-
durationMs: 1000,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
updateAnimations(element1);
|
|
101
|
-
updateAnimations(element2);
|
|
102
|
-
|
|
103
|
-
assert.equal(element1.style.getPropertyValue("--ef-progress"), "0%");
|
|
104
|
-
assert.equal(element2.style.getPropertyValue("--ef-progress"), "100%");
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test("sets --ef-duration to element durationMs", () => {
|
|
108
|
-
const element = createTestElement({
|
|
109
|
-
durationMs: 2000,
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
updateAnimations(element);
|
|
113
|
-
|
|
114
|
-
assert.equal(element.style.getPropertyValue("--ef-duration"), "2000ms");
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test("sets --ef-transition-duration based on parentTimegroup overlapMs", () => {
|
|
118
|
-
const parentTimegroup = document.createElement(
|
|
119
|
-
"ef-timegroup",
|
|
120
|
-
) as EFTimegroup;
|
|
121
|
-
parentTimegroup.overlapMs = 500;
|
|
122
|
-
|
|
123
|
-
const element = createTestElement({
|
|
124
|
-
parentTimegroup,
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
updateAnimations(element);
|
|
128
|
-
|
|
129
|
-
assert.equal(
|
|
130
|
-
element.style.getPropertyValue("--ef-transition-duration"),
|
|
131
|
-
"500ms",
|
|
132
|
-
);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test("sets --ef-transition-duration to 0ms when no parentTimegroup", () => {
|
|
136
|
-
const element = createTestElement();
|
|
137
|
-
|
|
138
|
-
updateAnimations(element);
|
|
139
|
-
|
|
140
|
-
assert.equal(
|
|
141
|
-
element.style.getPropertyValue("--ef-transition-duration"),
|
|
142
|
-
"0ms",
|
|
143
|
-
);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
test("sets --ef-transition-out-start correctly", () => {
|
|
147
|
-
const parentTimegroup = document.createElement(
|
|
148
|
-
"ef-timegroup",
|
|
149
|
-
) as EFTimegroup;
|
|
150
|
-
parentTimegroup.overlapMs = 200;
|
|
151
|
-
|
|
152
|
-
const element = createTestElement({
|
|
153
|
-
durationMs: 1000,
|
|
154
|
-
parentTimegroup,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
updateAnimations(element);
|
|
158
|
-
|
|
159
|
-
assert.equal(
|
|
160
|
-
element.style.getPropertyValue("--ef-transition-out-start"),
|
|
161
|
-
"800ms",
|
|
162
|
-
);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test("sets animation-related CSS properties when animations are present", () => {
|
|
166
|
-
const element = createTestElement({
|
|
167
|
-
durationMs: 2000,
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
// Create an animation to trigger CSS property setting
|
|
171
|
-
element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 1000 });
|
|
172
|
-
|
|
173
|
-
updateAnimations(element);
|
|
174
|
-
|
|
175
|
-
assert.equal(element.style.getPropertyValue("--ef-duration"), "2000ms");
|
|
176
|
-
assert.equal(
|
|
177
|
-
element.style.getPropertyValue("--ef-transition-duration"),
|
|
178
|
-
"0ms",
|
|
179
|
-
);
|
|
180
|
-
assert.equal(
|
|
181
|
-
element.style.getPropertyValue("--ef-transition-out-start"),
|
|
182
|
-
"2000ms",
|
|
183
|
-
);
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
describe("element visibility", () => {
|
|
188
|
-
test("hides element when timeline is before startTimeMs", () => {
|
|
189
|
-
const rootTimegroup = document.createElement(
|
|
190
|
-
"ef-timegroup",
|
|
191
|
-
) as EFTimegroup;
|
|
192
|
-
rootTimegroup.currentTimeMs = 100;
|
|
193
|
-
|
|
194
|
-
const element = createTestElement({
|
|
195
|
-
startTimeMs: 200,
|
|
196
|
-
endTimeMs: 800,
|
|
197
|
-
rootTimegroup,
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
updateAnimations(element);
|
|
201
|
-
|
|
202
|
-
assert.equal(element.style.display, "none");
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
test("hides element when timeline is after endTimeMs", () => {
|
|
206
|
-
const rootTimegroup = document.createElement(
|
|
207
|
-
"ef-timegroup",
|
|
208
|
-
) as EFTimegroup;
|
|
209
|
-
rootTimegroup.currentTimeMs = 900;
|
|
210
|
-
|
|
211
|
-
const element = createTestElement({
|
|
212
|
-
startTimeMs: 200,
|
|
213
|
-
endTimeMs: 800,
|
|
214
|
-
rootTimegroup,
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
updateAnimations(element);
|
|
218
|
-
|
|
219
|
-
assert.equal(element.style.display, "none");
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
test("shows element when timeline is within start/end range (using element currentTimeMs)", () => {
|
|
223
|
-
const element = createTestElement({
|
|
224
|
-
currentTimeMs: 500,
|
|
225
|
-
startTimeMs: 200,
|
|
226
|
-
endTimeMs: 800,
|
|
227
|
-
});
|
|
228
|
-
// Start with element hidden
|
|
229
|
-
element.style.display = "none";
|
|
230
|
-
|
|
231
|
-
updateAnimations(element);
|
|
232
|
-
|
|
233
|
-
assert.equal(element.style.display, "");
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
test("sequence elements remain coordinated at exact end boundary", () => {
|
|
237
|
-
// Create a root timegroup mock
|
|
238
|
-
const rootTimegroup = {
|
|
239
|
-
currentTimeMs: 3000,
|
|
240
|
-
durationMs: 3000,
|
|
241
|
-
startTimeMs: 0,
|
|
242
|
-
endTimeMs: 3000,
|
|
243
|
-
tagName: "EF-TIMEGROUP",
|
|
244
|
-
} as any;
|
|
245
|
-
|
|
246
|
-
// Create a child element in sequence that spans 2000-3000ms
|
|
247
|
-
const element = createTestElement({
|
|
248
|
-
startTimeMs: 2000,
|
|
249
|
-
endTimeMs: 3000,
|
|
250
|
-
durationMs: 1000,
|
|
251
|
-
ownCurrentTimeMs: 1000, // At exact end of its own duration
|
|
252
|
-
rootTimegroup: rootTimegroup,
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
// Create REAL animations using the Web Animations API
|
|
256
|
-
const animation1 = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
257
|
-
duration: 1000,
|
|
258
|
-
delay: 0,
|
|
259
|
-
iterations: 1,
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
const animation2 = element.animate(
|
|
263
|
-
[{ transform: "scale(1)" }, { transform: "scale(1.5)" }],
|
|
264
|
-
{
|
|
265
|
-
duration: 1000,
|
|
266
|
-
delay: 0,
|
|
267
|
-
iterations: 1,
|
|
268
|
-
},
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
// Start with animations running
|
|
272
|
-
animation1.play();
|
|
273
|
-
animation2.play();
|
|
274
|
-
|
|
275
|
-
// Verify we have real animations
|
|
276
|
-
const animations = element.getAnimations({ subtree: true });
|
|
277
|
-
assert.equal(animations.length, 2, "Should have 2 real animations");
|
|
278
|
-
|
|
279
|
-
updateAnimations(element);
|
|
280
|
-
|
|
281
|
-
// The element should be hidden due to exclusive end condition (3000 > 3000 = false)
|
|
282
|
-
assert.equal(
|
|
283
|
-
element.style.display,
|
|
284
|
-
"",
|
|
285
|
-
"Element should be hidden at exact end boundary due to inclusive end",
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
// BUT animations should still be coordinated to prevent jarring visual jumps
|
|
289
|
-
// This is the fix we want: animations coordinated even when element is hidden at exact boundary
|
|
290
|
-
animations.forEach((animation, index) => {
|
|
291
|
-
assert.approximately(
|
|
292
|
-
animation.currentTime as number,
|
|
293
|
-
999,
|
|
294
|
-
1,
|
|
295
|
-
`Animation ${index + 1} should be coordinated at exact end boundary to prevent visual jumps`,
|
|
296
|
-
);
|
|
297
|
-
assert.equal(
|
|
298
|
-
animation.playState,
|
|
299
|
-
"paused",
|
|
300
|
-
`Animation ${index + 1} should be paused after coordination`,
|
|
301
|
-
);
|
|
302
|
-
});
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
test("uses element currentTimeMs when no rootTimegroup", () => {
|
|
306
|
-
const element = createTestElement({
|
|
307
|
-
currentTimeMs: 500,
|
|
308
|
-
startTimeMs: 200,
|
|
309
|
-
endTimeMs: 800,
|
|
310
|
-
});
|
|
311
|
-
element.style.display = "none";
|
|
312
|
-
|
|
313
|
-
updateAnimations(element);
|
|
314
|
-
|
|
315
|
-
assert.equal(element.style.display, "");
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
test("element at exact start boundary is visible (using element currentTimeMs)", () => {
|
|
319
|
-
const element = createTestElement({
|
|
320
|
-
currentTimeMs: 200,
|
|
321
|
-
startTimeMs: 200,
|
|
322
|
-
endTimeMs: 800,
|
|
323
|
-
});
|
|
324
|
-
element.style.display = "none";
|
|
325
|
-
|
|
326
|
-
updateAnimations(element);
|
|
327
|
-
|
|
328
|
-
assert.equal(element.style.display, "");
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
test("bare temporal element at its exact end is visible (root element)", () => {
|
|
332
|
-
const element = createTestElement({
|
|
333
|
-
currentTimeMs: 1000,
|
|
334
|
-
startTimeMs: 0,
|
|
335
|
-
endTimeMs: 1000,
|
|
336
|
-
durationMs: 1000,
|
|
337
|
-
});
|
|
338
|
-
element.style.display = "";
|
|
339
|
-
|
|
340
|
-
updateAnimations(element);
|
|
341
|
-
|
|
342
|
-
assert.equal(
|
|
343
|
-
element.style.display,
|
|
344
|
-
"",
|
|
345
|
-
"Root element should remain visible at exact end to show final frame",
|
|
346
|
-
);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
test("deeply nested element aligned with root end is visible (2 levels)", () => {
|
|
350
|
-
const rootTimegroup = {
|
|
351
|
-
currentTimeMs: 3000,
|
|
352
|
-
durationMs: 3000,
|
|
353
|
-
startTimeMs: 0,
|
|
354
|
-
endTimeMs: 3000,
|
|
355
|
-
tagName: "EF-TIMEGROUP",
|
|
356
|
-
} as any;
|
|
357
|
-
|
|
358
|
-
const childTimegroup = {
|
|
359
|
-
currentTimeMs: 3000,
|
|
360
|
-
durationMs: 2000,
|
|
361
|
-
startTimeMs: 1000,
|
|
362
|
-
endTimeMs: 3000,
|
|
363
|
-
rootTimegroup,
|
|
364
|
-
parentTimegroup: rootTimegroup,
|
|
365
|
-
tagName: "EF-TIMEGROUP",
|
|
366
|
-
} as any;
|
|
367
|
-
|
|
368
|
-
const element = createTestElement({
|
|
369
|
-
currentTimeMs: 1000,
|
|
370
|
-
startTimeMs: 2000,
|
|
371
|
-
endTimeMs: 3000,
|
|
372
|
-
durationMs: 1000,
|
|
373
|
-
ownCurrentTimeMs: 1000,
|
|
374
|
-
rootTimegroup,
|
|
375
|
-
parentTimegroup: childTimegroup,
|
|
376
|
-
});
|
|
377
|
-
element.style.display = "";
|
|
378
|
-
|
|
379
|
-
updateAnimations(element);
|
|
380
|
-
|
|
381
|
-
assert.equal(
|
|
382
|
-
element.style.display,
|
|
383
|
-
"",
|
|
384
|
-
"Deeply nested element aligned with root end should remain visible",
|
|
385
|
-
);
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
test("deeply nested element aligned with root end is visible (3 levels)", () => {
|
|
389
|
-
const rootTimegroup = {
|
|
390
|
-
currentTimeMs: 4000,
|
|
391
|
-
durationMs: 4000,
|
|
392
|
-
startTimeMs: 0,
|
|
393
|
-
endTimeMs: 4000,
|
|
394
|
-
tagName: "EF-TIMEGROUP",
|
|
395
|
-
} as any;
|
|
396
|
-
|
|
397
|
-
const childTimegroup1 = {
|
|
398
|
-
currentTimeMs: 4000,
|
|
399
|
-
durationMs: 3000,
|
|
400
|
-
startTimeMs: 1000,
|
|
401
|
-
endTimeMs: 4000,
|
|
402
|
-
rootTimegroup,
|
|
403
|
-
parentTimegroup: rootTimegroup,
|
|
404
|
-
tagName: "EF-TIMEGROUP",
|
|
405
|
-
} as any;
|
|
406
|
-
|
|
407
|
-
const childTimegroup2 = {
|
|
408
|
-
currentTimeMs: 4000,
|
|
409
|
-
durationMs: 2000,
|
|
410
|
-
startTimeMs: 2000,
|
|
411
|
-
endTimeMs: 4000,
|
|
412
|
-
rootTimegroup,
|
|
413
|
-
parentTimegroup: childTimegroup1,
|
|
414
|
-
tagName: "EF-TIMEGROUP",
|
|
415
|
-
} as any;
|
|
416
|
-
|
|
417
|
-
const element = createTestElement({
|
|
418
|
-
currentTimeMs: 1000,
|
|
419
|
-
startTimeMs: 3000,
|
|
420
|
-
endTimeMs: 4000,
|
|
421
|
-
durationMs: 1000,
|
|
422
|
-
ownCurrentTimeMs: 1000,
|
|
423
|
-
rootTimegroup,
|
|
424
|
-
parentTimegroup: childTimegroup2,
|
|
425
|
-
});
|
|
426
|
-
element.style.display = "";
|
|
427
|
-
|
|
428
|
-
updateAnimations(element);
|
|
429
|
-
|
|
430
|
-
assert.equal(
|
|
431
|
-
element.style.display,
|
|
432
|
-
"",
|
|
433
|
-
"3+ level nested element aligned with root end should remain visible",
|
|
434
|
-
);
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
test("mid-composition element is hidden when timeline passes its end", () => {
|
|
438
|
-
const rootTimegroup = {
|
|
439
|
-
currentTimeMs: 3000,
|
|
440
|
-
durationMs: 3000,
|
|
441
|
-
startTimeMs: 0,
|
|
442
|
-
endTimeMs: 3000,
|
|
443
|
-
tagName: "EF-TIMEGROUP",
|
|
444
|
-
} as any;
|
|
445
|
-
|
|
446
|
-
const element = createTestElement({
|
|
447
|
-
currentTimeMs: 1000,
|
|
448
|
-
startTimeMs: 1000,
|
|
449
|
-
endTimeMs: 2000,
|
|
450
|
-
durationMs: 1000,
|
|
451
|
-
rootTimegroup,
|
|
452
|
-
parentTimegroup: rootTimegroup,
|
|
453
|
-
});
|
|
454
|
-
element.style.display = "";
|
|
455
|
-
|
|
456
|
-
updateAnimations(element);
|
|
457
|
-
|
|
458
|
-
assert.equal(
|
|
459
|
-
element.style.display,
|
|
460
|
-
"none",
|
|
461
|
-
"Mid-composition element should be hidden when timeline is past its end",
|
|
462
|
-
);
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
test("root timegroup at exact end is visible", () => {
|
|
466
|
-
const rootTimegroup = document.createElement(
|
|
467
|
-
"ef-timegroup",
|
|
468
|
-
) as EFTimegroup;
|
|
469
|
-
Object.defineProperty(rootTimegroup, "currentTimeMs", {
|
|
470
|
-
value: 1000,
|
|
471
|
-
writable: true,
|
|
472
|
-
});
|
|
473
|
-
Object.defineProperty(rootTimegroup, "durationMs", {
|
|
474
|
-
value: 1000,
|
|
475
|
-
writable: true,
|
|
476
|
-
});
|
|
477
|
-
Object.defineProperty(rootTimegroup, "startTimeMs", {
|
|
478
|
-
value: 0,
|
|
479
|
-
writable: true,
|
|
480
|
-
});
|
|
481
|
-
Object.defineProperty(rootTimegroup, "endTimeMs", {
|
|
482
|
-
value: 1000,
|
|
483
|
-
writable: true,
|
|
484
|
-
});
|
|
485
|
-
Object.defineProperty(rootTimegroup, "parentTimegroup", {
|
|
486
|
-
value: undefined,
|
|
487
|
-
writable: true,
|
|
488
|
-
});
|
|
489
|
-
document.body.appendChild(rootTimegroup);
|
|
490
|
-
|
|
491
|
-
updateAnimations(rootTimegroup as any);
|
|
492
|
-
|
|
493
|
-
assert.equal(
|
|
494
|
-
rootTimegroup.style.display,
|
|
495
|
-
"",
|
|
496
|
-
"Root timegroup should remain visible at exact end",
|
|
497
|
-
);
|
|
498
|
-
|
|
499
|
-
document.body.removeChild(rootTimegroup);
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
test("element at exact end boundary is visible when it is root (using element currentTimeMs)", () => {
|
|
503
|
-
const element = createTestElement({
|
|
504
|
-
currentTimeMs: 800,
|
|
505
|
-
startTimeMs: 200,
|
|
506
|
-
endTimeMs: 800,
|
|
507
|
-
});
|
|
508
|
-
element.style.display = "";
|
|
509
|
-
|
|
510
|
-
updateAnimations(element);
|
|
511
|
-
|
|
512
|
-
assert.equal(
|
|
513
|
-
element.style.display,
|
|
514
|
-
"",
|
|
515
|
-
"Root element should remain visible at exact end boundary",
|
|
516
|
-
);
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
test("element just before start boundary is hidden", () => {
|
|
520
|
-
const element = createTestElement({
|
|
521
|
-
currentTimeMs: 199,
|
|
522
|
-
startTimeMs: 200,
|
|
523
|
-
endTimeMs: 800,
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
updateAnimations(element);
|
|
527
|
-
|
|
528
|
-
assert.equal(element.style.display, "none");
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
test("element just after end boundary is hidden", () => {
|
|
532
|
-
const element = createTestElement({
|
|
533
|
-
currentTimeMs: 801,
|
|
534
|
-
startTimeMs: 200,
|
|
535
|
-
endTimeMs: 800,
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
updateAnimations(element);
|
|
539
|
-
|
|
540
|
-
assert.equal(element.style.display, "none");
|
|
541
|
-
});
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
describe("Web Animations API integration", () => {
|
|
545
|
-
test("skips animation processing when getAnimations is not available", () => {
|
|
546
|
-
const element = createTestElement();
|
|
547
|
-
// Mock missing getAnimations
|
|
548
|
-
delete (element as any).getAnimations;
|
|
549
|
-
|
|
550
|
-
// Should not throw and should still set CSS properties
|
|
551
|
-
updateAnimations(element);
|
|
552
|
-
|
|
553
|
-
assert.equal(element.style.getPropertyValue("--ef-progress"), "0%");
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
test("pauses running animations", async () => {
|
|
557
|
-
const element = createTestElement();
|
|
558
|
-
|
|
559
|
-
// Create a test animation
|
|
560
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
561
|
-
duration: 1000,
|
|
562
|
-
});
|
|
563
|
-
animation.play();
|
|
564
|
-
|
|
565
|
-
updateAnimations(element);
|
|
566
|
-
|
|
567
|
-
assert.equal(animation.playState, "paused");
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
test("ignores animations without KeyframeEffect", async () => {
|
|
571
|
-
const element = createTestElement();
|
|
572
|
-
|
|
573
|
-
// Create animation with non-KeyframeEffect (this is tricky to test, but we can verify no errors)
|
|
574
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
575
|
-
duration: 1000,
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
// Mock the effect to not be a KeyframeEffect
|
|
579
|
-
Object.defineProperty(animation, "effect", {
|
|
580
|
-
value: {},
|
|
581
|
-
writable: false,
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
// Should not throw
|
|
585
|
-
updateAnimations(element);
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
test("ignores animations without target", async () => {
|
|
589
|
-
const element = createTestElement();
|
|
590
|
-
|
|
591
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
592
|
-
duration: 1000,
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
// Mock the effect target to be null
|
|
596
|
-
if (animation.effect instanceof KeyframeEffect) {
|
|
597
|
-
Object.defineProperty(animation.effect, "target", {
|
|
598
|
-
value: null,
|
|
599
|
-
writable: false,
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
// Should not throw
|
|
604
|
-
updateAnimations(element);
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
test("handles missing timeTarget gracefully", async () => {
|
|
608
|
-
const element = createTestElement();
|
|
609
|
-
|
|
610
|
-
const target = document.createElement("div");
|
|
611
|
-
element.appendChild(target);
|
|
612
|
-
|
|
613
|
-
// Should not throw when target has no temporal parent
|
|
614
|
-
updateAnimations(element);
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
test("processes multiple animations on same element", async () => {
|
|
618
|
-
const element = createTestElement();
|
|
619
|
-
|
|
620
|
-
const animation1 = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
621
|
-
duration: 1000,
|
|
622
|
-
});
|
|
623
|
-
const animation2 = element.animate(
|
|
624
|
-
[{ transform: "scale(1)" }, { transform: "scale(2)" }],
|
|
625
|
-
{ duration: 500 },
|
|
626
|
-
);
|
|
627
|
-
|
|
628
|
-
animation1.play();
|
|
629
|
-
animation2.play();
|
|
630
|
-
|
|
631
|
-
updateAnimations(element);
|
|
632
|
-
|
|
633
|
-
// Both animations should be paused
|
|
634
|
-
assert.equal(animation1.playState, "paused");
|
|
635
|
-
assert.equal(animation2.playState, "paused");
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
test("handles animations with zero duration", async () => {
|
|
639
|
-
const element = createTestElement();
|
|
640
|
-
|
|
641
|
-
// Should not throw
|
|
642
|
-
updateAnimations(element);
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
test("handles animations with zero iterations", async () => {
|
|
646
|
-
const element = createTestElement();
|
|
647
|
-
|
|
648
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
649
|
-
duration: 1000,
|
|
650
|
-
iterations: 0,
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
updateAnimations(element);
|
|
654
|
-
|
|
655
|
-
// With 0 iterations and no timeTarget, currentIteration (0) >= iterations (0), so should be set to duration - epsilon
|
|
656
|
-
// But since there's no timeTarget, the code path continues and doesn't set currentTime
|
|
657
|
-
// The animation will continue with its default behavior
|
|
658
|
-
assert.equal(animation.currentTime, 0);
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
test("handles animations that are already paused", async () => {
|
|
662
|
-
const element = createTestElement();
|
|
663
|
-
|
|
664
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
665
|
-
duration: 1000,
|
|
666
|
-
});
|
|
667
|
-
animation.pause();
|
|
668
|
-
|
|
669
|
-
assert.equal(animation.playState, "paused");
|
|
670
|
-
|
|
671
|
-
// Should not throw and should still set currentTime
|
|
672
|
-
updateAnimations(element);
|
|
673
|
-
|
|
674
|
-
assert.equal(animation.playState, "paused");
|
|
675
|
-
});
|
|
676
|
-
|
|
677
|
-
test("keeps completed animations available for scrubbing", async () => {
|
|
678
|
-
// Create a timegroup with 10s duration
|
|
679
|
-
const timegroup = document.createElement("ef-timegroup") as EFTimegroup;
|
|
680
|
-
timegroup.setAttribute("mode", "fixed");
|
|
681
|
-
timegroup.setAttribute("duration", "10000ms");
|
|
682
|
-
document.body.appendChild(timegroup);
|
|
683
|
-
|
|
684
|
-
// Create a child element with a 5s animation
|
|
685
|
-
const child = document.createElement("div");
|
|
686
|
-
timegroup.appendChild(child);
|
|
687
|
-
|
|
688
|
-
child.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
689
|
-
duration: 5000, // 5s animation
|
|
690
|
-
iterations: 1,
|
|
691
|
-
delay: 0,
|
|
692
|
-
});
|
|
693
|
-
timegroup.currentTime = 6;
|
|
694
|
-
await timegroup.seekTask.run();
|
|
695
|
-
|
|
696
|
-
// Animation should still be available even though timeline (6s) > animation duration (5s)
|
|
697
|
-
// This prevents animations from being removed, enabling scrubbing backwards
|
|
698
|
-
const animations = timegroup.getAnimations({ subtree: true });
|
|
699
|
-
assert.equal(
|
|
700
|
-
animations.length,
|
|
701
|
-
1,
|
|
702
|
-
"REGRESSION TEST: Animation should remain available for scrubbing. This would fail with Number.EPSILON due to insufficient precision offset.",
|
|
703
|
-
);
|
|
704
|
-
});
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
describe("child element animation coordination", () => {
|
|
708
|
-
test("coordinates animations on non-temporal child elements", async () => {
|
|
709
|
-
// Create root timegroup
|
|
710
|
-
const rootTimegroup = document.createElement(
|
|
711
|
-
"ef-timegroup",
|
|
712
|
-
) as EFTimegroup;
|
|
713
|
-
rootTimegroup.currentTimeMs = 150; // Timeline at 150ms
|
|
714
|
-
document.body.appendChild(rootTimegroup);
|
|
715
|
-
|
|
716
|
-
// Create parent temporal element
|
|
717
|
-
const parentElement = document.createElement(
|
|
718
|
-
"test-temporal-element",
|
|
719
|
-
) as TestTemporalElement;
|
|
720
|
-
parentElement.setDuration(300); // 300ms duration
|
|
721
|
-
parentElement.setAttribute("offset", "100ms"); // Start at 100ms in root timeline
|
|
722
|
-
rootTimegroup.appendChild(parentElement);
|
|
723
|
-
|
|
724
|
-
// Create a regular NON-temporal HTML element inside the temporal element
|
|
725
|
-
const nonTemporalDiv = document.createElement("div");
|
|
726
|
-
parentElement.appendChild(nonTemporalDiv);
|
|
727
|
-
|
|
728
|
-
// Wait for elements to be connected and updated
|
|
729
|
-
await rootTimegroup.updateComplete;
|
|
730
|
-
await parentElement.updateComplete;
|
|
731
|
-
|
|
732
|
-
// Create animation on the NON-temporal child element
|
|
733
|
-
const nonTemporalAnimation = nonTemporalDiv.animate(
|
|
734
|
-
[{ opacity: 0 }, { opacity: 1 }],
|
|
735
|
-
{
|
|
736
|
-
duration: 1000,
|
|
737
|
-
},
|
|
738
|
-
);
|
|
739
|
-
nonTemporalAnimation.play();
|
|
740
|
-
|
|
741
|
-
// Call updateAnimations on root timegroup
|
|
742
|
-
updateAnimations(rootTimegroup);
|
|
743
|
-
|
|
744
|
-
// Parent should be visible at current timeline position (150ms is between 100ms-400ms)
|
|
745
|
-
assert.notEqual(
|
|
746
|
-
parentElement.style.display,
|
|
747
|
-
"",
|
|
748
|
-
"Parent should be visible at current timeline time",
|
|
749
|
-
);
|
|
750
|
-
|
|
751
|
-
// FIXED: Non-temporal child animation should be paused and coordinated
|
|
752
|
-
assert.equal(
|
|
753
|
-
nonTemporalAnimation.playState,
|
|
754
|
-
"paused",
|
|
755
|
-
"Non-temporal child element animation should be paused and coordinated with timeline",
|
|
756
|
-
);
|
|
757
|
-
});
|
|
758
|
-
|
|
759
|
-
test("coordinates animations on deeply nested non-temporal elements", async () => {
|
|
760
|
-
// Create root timegroup
|
|
761
|
-
const rootTimegroup = document.createElement(
|
|
762
|
-
"ef-timegroup",
|
|
763
|
-
) as EFTimegroup;
|
|
764
|
-
rootTimegroup.currentTimeMs = 150; // Timeline at 150ms
|
|
765
|
-
document.body.appendChild(rootTimegroup);
|
|
766
|
-
|
|
767
|
-
// Create parent temporal element
|
|
768
|
-
const parentElement = document.createElement(
|
|
769
|
-
"test-temporal-element",
|
|
770
|
-
) as TestTemporalElement;
|
|
771
|
-
parentElement.setDuration(300); // 300ms duration
|
|
772
|
-
parentElement.setAttribute("offset", "100ms"); // Start at 100ms in root timeline
|
|
773
|
-
rootTimegroup.appendChild(parentElement);
|
|
774
|
-
|
|
775
|
-
// Create nested non-temporal structure: temporal > div > div > span
|
|
776
|
-
const outerDiv = document.createElement("div");
|
|
777
|
-
const innerDiv = document.createElement("div");
|
|
778
|
-
const span = document.createElement("span");
|
|
779
|
-
|
|
780
|
-
parentElement.appendChild(outerDiv);
|
|
781
|
-
outerDiv.appendChild(innerDiv);
|
|
782
|
-
innerDiv.appendChild(span);
|
|
783
|
-
|
|
784
|
-
// Wait for elements to be connected and updated
|
|
785
|
-
await rootTimegroup.updateComplete;
|
|
786
|
-
await parentElement.updateComplete;
|
|
787
|
-
|
|
788
|
-
// Create animations on different levels of nesting
|
|
789
|
-
const outerAnimation = outerDiv.animate(
|
|
790
|
-
[{ transform: "scale(1)" }, { transform: "scale(1.1)" }],
|
|
791
|
-
{
|
|
792
|
-
duration: 800,
|
|
793
|
-
},
|
|
794
|
-
);
|
|
795
|
-
const innerAnimation = innerDiv.animate(
|
|
796
|
-
[{ opacity: 0.5 }, { opacity: 1 }],
|
|
797
|
-
{
|
|
798
|
-
duration: 1200,
|
|
799
|
-
},
|
|
800
|
-
);
|
|
801
|
-
const spanAnimation = span.animate(
|
|
802
|
-
[{ color: "red" }, { color: "blue" }],
|
|
803
|
-
{
|
|
804
|
-
duration: 600,
|
|
805
|
-
},
|
|
806
|
-
);
|
|
807
|
-
|
|
808
|
-
outerAnimation.play();
|
|
809
|
-
innerAnimation.play();
|
|
810
|
-
spanAnimation.play();
|
|
811
|
-
|
|
812
|
-
// Call updateAnimations on root timegroup
|
|
813
|
-
updateAnimations(rootTimegroup);
|
|
814
|
-
|
|
815
|
-
// All nested non-temporal animations should be coordinated
|
|
816
|
-
assert.equal(
|
|
817
|
-
outerAnimation.playState,
|
|
818
|
-
"paused",
|
|
819
|
-
"Outer div animation should be coordinated",
|
|
820
|
-
);
|
|
821
|
-
assert.equal(
|
|
822
|
-
innerAnimation.playState,
|
|
823
|
-
"paused",
|
|
824
|
-
"Inner div animation should be coordinated",
|
|
825
|
-
);
|
|
826
|
-
assert.equal(
|
|
827
|
-
spanAnimation.playState,
|
|
828
|
-
"paused",
|
|
829
|
-
"Span animation should be coordinated",
|
|
830
|
-
);
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
test("coordinates animations on child temporal elements when they are visible", async () => {
|
|
834
|
-
// Create root timegroup
|
|
835
|
-
const rootTimegroup = document.createElement(
|
|
836
|
-
"ef-timegroup",
|
|
837
|
-
) as EFTimegroup;
|
|
838
|
-
rootTimegroup.currentTimeMs = 150; // Timeline at 150ms
|
|
839
|
-
document.body.appendChild(rootTimegroup);
|
|
840
|
-
|
|
841
|
-
// Create parent element (timegroup acts as parent)
|
|
842
|
-
const parentTimegroup = document.createElement(
|
|
843
|
-
"ef-timegroup",
|
|
844
|
-
) as EFTimegroup;
|
|
845
|
-
parentTimegroup.setAttribute("duration", "1000ms");
|
|
846
|
-
rootTimegroup.appendChild(parentTimegroup);
|
|
847
|
-
|
|
848
|
-
// Create child temporal element that WILL be visible at timeline time 150ms
|
|
849
|
-
const childElement = document.createElement(
|
|
850
|
-
"test-temporal-element",
|
|
851
|
-
) as TestTemporalElement;
|
|
852
|
-
childElement.setDuration(300); // 300ms duration (from 100ms to 400ms in root timeline)
|
|
853
|
-
childElement.setAttribute("offset", "100ms"); // Start at 100ms in root timeline
|
|
854
|
-
parentTimegroup.appendChild(childElement);
|
|
855
|
-
|
|
856
|
-
// Wait for elements to be connected and updated
|
|
857
|
-
await rootTimegroup.updateComplete;
|
|
858
|
-
await parentTimegroup.updateComplete;
|
|
859
|
-
await childElement.updateComplete;
|
|
860
|
-
|
|
861
|
-
// Create animation on child element
|
|
862
|
-
const childAnimation = childElement.animate(
|
|
863
|
-
[{ opacity: 0 }, { opacity: 1 }],
|
|
864
|
-
{
|
|
865
|
-
duration: 1000,
|
|
866
|
-
},
|
|
867
|
-
);
|
|
868
|
-
childAnimation.play();
|
|
869
|
-
|
|
870
|
-
// Call updateAnimations on parent timegroup - this should coordinate child animations too
|
|
871
|
-
updateAnimations(parentTimegroup);
|
|
872
|
-
|
|
873
|
-
// Child should be visible at current timeline position (150ms is between 100ms-400ms)
|
|
874
|
-
assert.notEqual(
|
|
875
|
-
childElement.style.display,
|
|
876
|
-
"",
|
|
877
|
-
"Child should be visible at current timeline time",
|
|
878
|
-
);
|
|
879
|
-
|
|
880
|
-
// FIXED: Child animation should be paused and coordinated
|
|
881
|
-
assert.equal(
|
|
882
|
-
childAnimation.playState,
|
|
883
|
-
"paused",
|
|
884
|
-
"Child element animation should be paused and coordinated with timeline",
|
|
885
|
-
);
|
|
886
|
-
});
|
|
887
|
-
|
|
888
|
-
test("does not coordinate animations on child temporal elements when they are not visible", async () => {
|
|
889
|
-
// Create root timegroup
|
|
890
|
-
const rootTimegroup = document.createElement(
|
|
891
|
-
"ef-timegroup",
|
|
892
|
-
) as EFTimegroup;
|
|
893
|
-
rootTimegroup.currentTimeMs = 100; // Timeline at 100ms
|
|
894
|
-
document.body.appendChild(rootTimegroup);
|
|
895
|
-
|
|
896
|
-
// Create parent element (timegroup acts as parent)
|
|
897
|
-
const parentTimegroup = document.createElement(
|
|
898
|
-
"ef-timegroup",
|
|
899
|
-
) as EFTimegroup;
|
|
900
|
-
parentTimegroup.setAttribute("duration", "1000ms");
|
|
901
|
-
rootTimegroup.appendChild(parentTimegroup);
|
|
902
|
-
|
|
903
|
-
// Create child temporal element that will NOT be visible at timeline time 100ms
|
|
904
|
-
const childElement = document.createElement(
|
|
905
|
-
"test-temporal-element",
|
|
906
|
-
) as TestTemporalElement;
|
|
907
|
-
childElement.setDuration(200); // 200ms duration
|
|
908
|
-
childElement.setAttribute("offset", "500ms"); // Start at 500ms in root timeline (way after current time)
|
|
909
|
-
parentTimegroup.appendChild(childElement);
|
|
910
|
-
|
|
911
|
-
// Wait for elements to be connected and updated
|
|
912
|
-
await rootTimegroup.updateComplete;
|
|
913
|
-
await parentTimegroup.updateComplete;
|
|
914
|
-
await childElement.updateComplete;
|
|
915
|
-
|
|
916
|
-
// Create animation on child element
|
|
917
|
-
const childAnimation = childElement.animate(
|
|
918
|
-
[{ opacity: 0 }, { opacity: 1 }],
|
|
919
|
-
{
|
|
920
|
-
duration: 1000,
|
|
921
|
-
},
|
|
922
|
-
);
|
|
923
|
-
childAnimation.play();
|
|
924
|
-
|
|
925
|
-
// Call updateAnimations on parent timegroup
|
|
926
|
-
updateAnimations(parentTimegroup);
|
|
927
|
-
|
|
928
|
-
// Child should be hidden (display: none)
|
|
929
|
-
assert.equal(
|
|
930
|
-
childElement.style.display,
|
|
931
|
-
"none",
|
|
932
|
-
"Child should be hidden when not in visible time range",
|
|
933
|
-
);
|
|
934
|
-
|
|
935
|
-
// Child animation should still be running (not coordinated since child is not visible)
|
|
936
|
-
assert.equal(
|
|
937
|
-
childAnimation.playState,
|
|
938
|
-
"paused",
|
|
939
|
-
"Child animation should remain running when child element is not visible",
|
|
940
|
-
);
|
|
941
|
-
});
|
|
942
|
-
});
|
|
943
|
-
|
|
944
|
-
describe("edge cases", () => {
|
|
945
|
-
test("handles zero duration gracefully", () => {
|
|
946
|
-
const element = createTestElement({
|
|
947
|
-
currentTimeMs: 100,
|
|
948
|
-
durationMs: 0,
|
|
949
|
-
});
|
|
950
|
-
|
|
951
|
-
updateAnimations(element);
|
|
952
|
-
|
|
953
|
-
// Should handle division by zero
|
|
954
|
-
assert.equal(element.style.getPropertyValue("--ef-progress"), "100%");
|
|
955
|
-
});
|
|
956
|
-
|
|
957
|
-
test("handles negative currentTimeMs", () => {
|
|
958
|
-
const element = createTestElement({
|
|
959
|
-
currentTimeMs: -100,
|
|
960
|
-
durationMs: 1000,
|
|
961
|
-
});
|
|
962
|
-
|
|
963
|
-
updateAnimations(element);
|
|
964
|
-
|
|
965
|
-
assert.equal(element.style.getPropertyValue("--ef-progress"), "0%");
|
|
966
|
-
});
|
|
967
|
-
|
|
968
|
-
test("handles missing parentTimegroup overlapMs", () => {
|
|
969
|
-
const parentTimegroup = {} as EFTimegroup; // Missing overlapMs property
|
|
970
|
-
|
|
971
|
-
const element = createTestElement({
|
|
972
|
-
parentTimegroup,
|
|
973
|
-
durationMs: 1000,
|
|
974
|
-
});
|
|
975
|
-
|
|
976
|
-
updateAnimations(element);
|
|
977
|
-
|
|
978
|
-
assert.equal(
|
|
979
|
-
element.style.getPropertyValue("--ef-transition-duration"),
|
|
980
|
-
"0ms",
|
|
981
|
-
);
|
|
982
|
-
assert.equal(
|
|
983
|
-
element.style.getPropertyValue("--ef-transition-out-start"),
|
|
984
|
-
"1000ms",
|
|
985
|
-
);
|
|
986
|
-
});
|
|
987
|
-
|
|
988
|
-
test("handles large duration values", () => {
|
|
989
|
-
const element = createTestElement({
|
|
990
|
-
currentTimeMs: 5000,
|
|
991
|
-
durationMs: 1000000, // 1000 seconds
|
|
992
|
-
});
|
|
993
|
-
|
|
994
|
-
updateAnimations(element);
|
|
995
|
-
|
|
996
|
-
assert.equal(element.style.getPropertyValue("--ef-progress"), "0.5%");
|
|
997
|
-
});
|
|
998
|
-
|
|
999
|
-
test("handles very small time values", () => {
|
|
1000
|
-
const element = createTestElement({
|
|
1001
|
-
currentTimeMs: 0.5,
|
|
1002
|
-
durationMs: 10,
|
|
1003
|
-
});
|
|
1004
|
-
|
|
1005
|
-
updateAnimations(element);
|
|
1006
|
-
|
|
1007
|
-
assert.equal(element.style.getPropertyValue("--ef-progress"), "5%");
|
|
1008
|
-
});
|
|
1009
|
-
|
|
1010
|
-
test("does not modify display when element should remain visible", () => {
|
|
1011
|
-
const element = createTestElement({
|
|
1012
|
-
currentTimeMs: 500,
|
|
1013
|
-
startTimeMs: 200,
|
|
1014
|
-
endTimeMs: 800,
|
|
1015
|
-
});
|
|
1016
|
-
// Element starts visible (default state)
|
|
1017
|
-
const initialDisplay = element.style.display;
|
|
1018
|
-
|
|
1019
|
-
updateAnimations(element);
|
|
1020
|
-
|
|
1021
|
-
// Should not have been modified
|
|
1022
|
-
assert.equal(element.style.display, initialDisplay);
|
|
1023
|
-
});
|
|
1024
|
-
|
|
1025
|
-
test("does not modify display when element should remain hidden", () => {
|
|
1026
|
-
const element = createTestElement({
|
|
1027
|
-
currentTimeMs: 100,
|
|
1028
|
-
startTimeMs: 200,
|
|
1029
|
-
endTimeMs: 800,
|
|
1030
|
-
});
|
|
1031
|
-
element.style.display = "none";
|
|
1032
|
-
|
|
1033
|
-
updateAnimations(element);
|
|
1034
|
-
|
|
1035
|
-
// Should still be hidden
|
|
1036
|
-
assert.equal(element.style.display, "none");
|
|
1037
|
-
});
|
|
1038
|
-
});
|
|
1039
|
-
|
|
1040
|
-
describe("CSS variables with zero overlap", () => {
|
|
1041
|
-
test("correctly sets transition duration to 0ms when overlap is 0ms", () => {
|
|
1042
|
-
const element = createTestElement({
|
|
1043
|
-
currentTimeMs: 500,
|
|
1044
|
-
startTimeMs: 0,
|
|
1045
|
-
endTimeMs: 1000,
|
|
1046
|
-
durationMs: 1000,
|
|
1047
|
-
parentTimegroup: {
|
|
1048
|
-
overlapMs: 0, // Zero overlap case
|
|
1049
|
-
} as any,
|
|
1050
|
-
});
|
|
1051
|
-
|
|
1052
|
-
updateAnimations(element);
|
|
1053
|
-
|
|
1054
|
-
// When overlap is 0, transition duration should be 0ms (no overlap means no transition)
|
|
1055
|
-
const transitionDuration = element.style.getPropertyValue(
|
|
1056
|
-
"--ef-transition-duration",
|
|
1057
|
-
);
|
|
1058
|
-
assert.equal(
|
|
1059
|
-
transitionDuration,
|
|
1060
|
-
"0ms",
|
|
1061
|
-
"Transition duration should be 0ms when no overlap",
|
|
1062
|
-
);
|
|
1063
|
-
|
|
1064
|
-
// Transition out start should equal full duration (no transition period)
|
|
1065
|
-
const transitionOutStart = element.style.getPropertyValue(
|
|
1066
|
-
"--ef-transition-out-start",
|
|
1067
|
-
);
|
|
1068
|
-
assert.equal(
|
|
1069
|
-
transitionOutStart,
|
|
1070
|
-
"1000ms",
|
|
1071
|
-
"Transition out start should equal full duration when no overlap",
|
|
1072
|
-
);
|
|
1073
|
-
|
|
1074
|
-
// Duration variable should still be set correctly for within-clip animations
|
|
1075
|
-
const duration = element.style.getPropertyValue("--ef-duration");
|
|
1076
|
-
assert.equal(
|
|
1077
|
-
duration,
|
|
1078
|
-
"1000ms",
|
|
1079
|
-
"Duration should be set for within-clip animation calculations",
|
|
1080
|
-
);
|
|
1081
|
-
});
|
|
1082
|
-
});
|
|
1083
|
-
|
|
1084
|
-
describe("animation-direction support", () => {
|
|
1085
|
-
test("normal direction: maintains forward playback at start", () => {
|
|
1086
|
-
const element = createTestElement({
|
|
1087
|
-
ownCurrentTimeMs: 0,
|
|
1088
|
-
});
|
|
1089
|
-
|
|
1090
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1091
|
-
duration: 1000,
|
|
1092
|
-
direction: "normal",
|
|
1093
|
-
});
|
|
1094
|
-
|
|
1095
|
-
updateAnimations(element);
|
|
1096
|
-
|
|
1097
|
-
assert.equal(animation.currentTime, 0);
|
|
1098
|
-
});
|
|
1099
|
-
|
|
1100
|
-
test("normal direction: maintains forward playback at middle", () => {
|
|
1101
|
-
const element = createTestElement({
|
|
1102
|
-
ownCurrentTimeMs: 500,
|
|
1103
|
-
});
|
|
1104
|
-
|
|
1105
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1106
|
-
duration: 1000,
|
|
1107
|
-
direction: "normal",
|
|
1108
|
-
});
|
|
1109
|
-
|
|
1110
|
-
updateAnimations(element);
|
|
1111
|
-
|
|
1112
|
-
assert.approximately(animation.currentTime as number, 500, 1);
|
|
1113
|
-
});
|
|
1114
|
-
|
|
1115
|
-
test("normal direction: maintains forward playback near end", () => {
|
|
1116
|
-
const element = createTestElement({
|
|
1117
|
-
ownCurrentTimeMs: 999,
|
|
1118
|
-
});
|
|
1119
|
-
|
|
1120
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1121
|
-
duration: 1000,
|
|
1122
|
-
direction: "normal",
|
|
1123
|
-
});
|
|
1124
|
-
|
|
1125
|
-
updateAnimations(element);
|
|
1126
|
-
|
|
1127
|
-
assert.approximately(animation.currentTime as number, 999, 1);
|
|
1128
|
-
});
|
|
1129
|
-
|
|
1130
|
-
test("reverse direction: shows end frame at start", () => {
|
|
1131
|
-
const element = createTestElement({
|
|
1132
|
-
ownCurrentTimeMs: 0,
|
|
1133
|
-
});
|
|
1134
|
-
|
|
1135
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1136
|
-
duration: 1000,
|
|
1137
|
-
direction: "reverse",
|
|
1138
|
-
});
|
|
1139
|
-
|
|
1140
|
-
updateAnimations(element);
|
|
1141
|
-
|
|
1142
|
-
assert.approximately(animation.currentTime as number, 1000, 1);
|
|
1143
|
-
});
|
|
1144
|
-
|
|
1145
|
-
test("reverse direction: shows reversed progress at middle", () => {
|
|
1146
|
-
const element = createTestElement({
|
|
1147
|
-
ownCurrentTimeMs: 300,
|
|
1148
|
-
});
|
|
1149
|
-
|
|
1150
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1151
|
-
duration: 1000,
|
|
1152
|
-
direction: "reverse",
|
|
1153
|
-
});
|
|
1154
|
-
|
|
1155
|
-
updateAnimations(element);
|
|
1156
|
-
|
|
1157
|
-
assert.approximately(animation.currentTime as number, 700, 1);
|
|
1158
|
-
});
|
|
1159
|
-
|
|
1160
|
-
test("reverse direction: shows start frame near end", () => {
|
|
1161
|
-
const element = createTestElement({
|
|
1162
|
-
ownCurrentTimeMs: 999,
|
|
1163
|
-
});
|
|
1164
|
-
|
|
1165
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1166
|
-
duration: 1000,
|
|
1167
|
-
direction: "reverse",
|
|
1168
|
-
});
|
|
1169
|
-
|
|
1170
|
-
updateAnimations(element);
|
|
1171
|
-
|
|
1172
|
-
assert.approximately(animation.currentTime as number, 1, 2);
|
|
1173
|
-
});
|
|
1174
|
-
|
|
1175
|
-
test("alternate direction: plays forward in iteration 0", () => {
|
|
1176
|
-
const element = createTestElement({
|
|
1177
|
-
ownCurrentTimeMs: 250,
|
|
1178
|
-
});
|
|
1179
|
-
|
|
1180
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1181
|
-
duration: 1000,
|
|
1182
|
-
iterations: 3,
|
|
1183
|
-
direction: "alternate",
|
|
1184
|
-
});
|
|
1185
|
-
|
|
1186
|
-
updateAnimations(element);
|
|
1187
|
-
|
|
1188
|
-
assert.approximately(animation.currentTime as number, 250, 1);
|
|
1189
|
-
});
|
|
1190
|
-
|
|
1191
|
-
test("alternate direction: plays backward in iteration 1", () => {
|
|
1192
|
-
const element = createTestElement({
|
|
1193
|
-
ownCurrentTimeMs: 1250,
|
|
1194
|
-
});
|
|
1195
|
-
|
|
1196
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1197
|
-
duration: 1000,
|
|
1198
|
-
iterations: 3,
|
|
1199
|
-
direction: "alternate",
|
|
1200
|
-
});
|
|
1201
|
-
|
|
1202
|
-
updateAnimations(element);
|
|
1203
|
-
|
|
1204
|
-
assert.approximately(animation.currentTime as number, 750, 1);
|
|
1205
|
-
});
|
|
1206
|
-
|
|
1207
|
-
test("alternate direction: plays forward in iteration 2", () => {
|
|
1208
|
-
const element = createTestElement({
|
|
1209
|
-
ownCurrentTimeMs: 2250,
|
|
1210
|
-
});
|
|
1211
|
-
|
|
1212
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1213
|
-
duration: 1000,
|
|
1214
|
-
iterations: 3,
|
|
1215
|
-
direction: "alternate",
|
|
1216
|
-
});
|
|
1217
|
-
|
|
1218
|
-
updateAnimations(element);
|
|
1219
|
-
|
|
1220
|
-
assert.approximately(animation.currentTime as number, 250, 1);
|
|
1221
|
-
});
|
|
1222
|
-
|
|
1223
|
-
test("alternate-reverse direction: plays backward in iteration 0", () => {
|
|
1224
|
-
const element = createTestElement({
|
|
1225
|
-
ownCurrentTimeMs: 250,
|
|
1226
|
-
});
|
|
1227
|
-
|
|
1228
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1229
|
-
duration: 1000,
|
|
1230
|
-
iterations: 3,
|
|
1231
|
-
direction: "alternate-reverse",
|
|
1232
|
-
});
|
|
1233
|
-
|
|
1234
|
-
updateAnimations(element);
|
|
1235
|
-
|
|
1236
|
-
assert.approximately(animation.currentTime as number, 750, 1);
|
|
1237
|
-
});
|
|
1238
|
-
|
|
1239
|
-
test("alternate-reverse direction: plays forward in iteration 1", () => {
|
|
1240
|
-
const element = createTestElement({
|
|
1241
|
-
ownCurrentTimeMs: 1250,
|
|
1242
|
-
});
|
|
1243
|
-
|
|
1244
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1245
|
-
duration: 1000,
|
|
1246
|
-
iterations: 3,
|
|
1247
|
-
direction: "alternate-reverse",
|
|
1248
|
-
});
|
|
1249
|
-
|
|
1250
|
-
updateAnimations(element);
|
|
1251
|
-
|
|
1252
|
-
assert.approximately(animation.currentTime as number, 250, 1);
|
|
1253
|
-
});
|
|
1254
|
-
|
|
1255
|
-
test("alternate-reverse direction: plays backward in iteration 2", () => {
|
|
1256
|
-
const element = createTestElement({
|
|
1257
|
-
ownCurrentTimeMs: 2250,
|
|
1258
|
-
});
|
|
1259
|
-
|
|
1260
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1261
|
-
duration: 1000,
|
|
1262
|
-
iterations: 3,
|
|
1263
|
-
direction: "alternate-reverse",
|
|
1264
|
-
});
|
|
1265
|
-
|
|
1266
|
-
updateAnimations(element);
|
|
1267
|
-
|
|
1268
|
-
assert.approximately(animation.currentTime as number, 750, 1);
|
|
1269
|
-
});
|
|
1270
|
-
|
|
1271
|
-
test("alternate direction at exact iteration boundary (start of iteration 1)", () => {
|
|
1272
|
-
const element = createTestElement({
|
|
1273
|
-
ownCurrentTimeMs: 1000,
|
|
1274
|
-
});
|
|
1275
|
-
|
|
1276
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1277
|
-
duration: 1000,
|
|
1278
|
-
iterations: 3,
|
|
1279
|
-
direction: "alternate",
|
|
1280
|
-
});
|
|
1281
|
-
|
|
1282
|
-
updateAnimations(element);
|
|
1283
|
-
|
|
1284
|
-
assert.approximately(animation.currentTime as number, 1000, 1);
|
|
1285
|
-
});
|
|
1286
|
-
|
|
1287
|
-
test("alternate direction at exact iteration boundary (start of iteration 2)", () => {
|
|
1288
|
-
const element = createTestElement({
|
|
1289
|
-
ownCurrentTimeMs: 2000,
|
|
1290
|
-
});
|
|
1291
|
-
|
|
1292
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1293
|
-
duration: 1000,
|
|
1294
|
-
iterations: 3,
|
|
1295
|
-
direction: "alternate",
|
|
1296
|
-
});
|
|
1297
|
-
|
|
1298
|
-
updateAnimations(element);
|
|
1299
|
-
|
|
1300
|
-
assert.approximately(animation.currentTime as number, 0, 1);
|
|
1301
|
-
});
|
|
1302
|
-
|
|
1303
|
-
test("reverse direction with single iteration", () => {
|
|
1304
|
-
const element = createTestElement({
|
|
1305
|
-
ownCurrentTimeMs: 400,
|
|
1306
|
-
});
|
|
1307
|
-
|
|
1308
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1309
|
-
duration: 1000,
|
|
1310
|
-
iterations: 1,
|
|
1311
|
-
direction: "reverse",
|
|
1312
|
-
});
|
|
1313
|
-
|
|
1314
|
-
updateAnimations(element);
|
|
1315
|
-
|
|
1316
|
-
assert.approximately(animation.currentTime as number, 600, 1);
|
|
1317
|
-
});
|
|
1318
|
-
|
|
1319
|
-
test("multiple animations with different directions on same element", () => {
|
|
1320
|
-
const element = createTestElement({
|
|
1321
|
-
ownCurrentTimeMs: 300,
|
|
1322
|
-
});
|
|
1323
|
-
|
|
1324
|
-
const normalAnimation = element.animate(
|
|
1325
|
-
[{ opacity: 0 }, { opacity: 1 }],
|
|
1326
|
-
{
|
|
1327
|
-
duration: 1000,
|
|
1328
|
-
direction: "normal",
|
|
1329
|
-
},
|
|
1330
|
-
);
|
|
1331
|
-
|
|
1332
|
-
const reverseAnimation = element.animate(
|
|
1333
|
-
[{ transform: "scale(1)" }, { transform: "scale(2)" }],
|
|
1334
|
-
{
|
|
1335
|
-
duration: 1000,
|
|
1336
|
-
direction: "reverse",
|
|
1337
|
-
},
|
|
1338
|
-
);
|
|
1339
|
-
|
|
1340
|
-
const alternateAnimation = element.animate(
|
|
1341
|
-
[{ color: "red" }, { color: "blue" }],
|
|
1342
|
-
{
|
|
1343
|
-
duration: 1000,
|
|
1344
|
-
iterations: 3,
|
|
1345
|
-
direction: "alternate",
|
|
1346
|
-
},
|
|
1347
|
-
);
|
|
1348
|
-
|
|
1349
|
-
updateAnimations(element);
|
|
1350
|
-
|
|
1351
|
-
assert.approximately(normalAnimation.currentTime as number, 300, 1);
|
|
1352
|
-
assert.approximately(reverseAnimation.currentTime as number, 700, 1);
|
|
1353
|
-
assert.approximately(alternateAnimation.currentTime as number, 300, 1);
|
|
1354
|
-
});
|
|
1355
|
-
|
|
1356
|
-
test("alternate direction with delay: iteration 0 plays forward", () => {
|
|
1357
|
-
const element = createTestElement({
|
|
1358
|
-
ownCurrentTimeMs: 750,
|
|
1359
|
-
});
|
|
1360
|
-
|
|
1361
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1362
|
-
duration: 1000,
|
|
1363
|
-
delay: 500,
|
|
1364
|
-
iterations: 3,
|
|
1365
|
-
direction: "alternate",
|
|
1366
|
-
});
|
|
1367
|
-
|
|
1368
|
-
updateAnimations(element);
|
|
1369
|
-
|
|
1370
|
-
assert.approximately(animation.currentTime as number, 750, 1);
|
|
1371
|
-
});
|
|
1372
|
-
|
|
1373
|
-
test("alternate direction with delay: iteration 1 plays backward", () => {
|
|
1374
|
-
const element = createTestElement({
|
|
1375
|
-
ownCurrentTimeMs: 1750,
|
|
1376
|
-
});
|
|
1377
|
-
|
|
1378
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1379
|
-
duration: 1000,
|
|
1380
|
-
delay: 500,
|
|
1381
|
-
iterations: 3,
|
|
1382
|
-
direction: "alternate",
|
|
1383
|
-
});
|
|
1384
|
-
|
|
1385
|
-
updateAnimations(element);
|
|
1386
|
-
|
|
1387
|
-
assert.approximately(animation.currentTime as number, 1250, 1);
|
|
1388
|
-
});
|
|
1389
|
-
|
|
1390
|
-
test("reverse direction respects precision offset to prevent completion", () => {
|
|
1391
|
-
const element = createTestElement({
|
|
1392
|
-
ownCurrentTimeMs: 1000,
|
|
1393
|
-
});
|
|
1394
|
-
|
|
1395
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1396
|
-
duration: 1000,
|
|
1397
|
-
direction: "reverse",
|
|
1398
|
-
});
|
|
1399
|
-
|
|
1400
|
-
updateAnimations(element);
|
|
1401
|
-
|
|
1402
|
-
assert.isBelow(
|
|
1403
|
-
animation.currentTime as number,
|
|
1404
|
-
1000,
|
|
1405
|
-
"Animation should not reach exact completion",
|
|
1406
|
-
);
|
|
1407
|
-
assert.approximately(animation.currentTime as number, 999, 1);
|
|
1408
|
-
});
|
|
1409
|
-
|
|
1410
|
-
test("alternate direction at end of final iteration respects precision offset", () => {
|
|
1411
|
-
const element = createTestElement({
|
|
1412
|
-
ownCurrentTimeMs: 2999,
|
|
1413
|
-
});
|
|
1414
|
-
|
|
1415
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1416
|
-
duration: 1000,
|
|
1417
|
-
iterations: 3,
|
|
1418
|
-
direction: "alternate",
|
|
1419
|
-
});
|
|
1420
|
-
|
|
1421
|
-
updateAnimations(element);
|
|
1422
|
-
|
|
1423
|
-
assert.isBelow(
|
|
1424
|
-
animation.currentTime as number,
|
|
1425
|
-
1000,
|
|
1426
|
-
"Animation should not reach iteration completion",
|
|
1427
|
-
);
|
|
1428
|
-
assert.approximately(
|
|
1429
|
-
animation.currentTime as number,
|
|
1430
|
-
999,
|
|
1431
|
-
1,
|
|
1432
|
-
"Should be at end of iteration 2 with precision offset",
|
|
1433
|
-
);
|
|
1434
|
-
});
|
|
1435
|
-
});
|
|
1436
|
-
|
|
1437
|
-
describe("animation-fill-mode support", () => {
|
|
1438
|
-
test("fill-mode none: animation before delay shows no effect", () => {
|
|
1439
|
-
const element = createTestElement({
|
|
1440
|
-
ownCurrentTimeMs: 250,
|
|
1441
|
-
});
|
|
1442
|
-
|
|
1443
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1444
|
-
duration: 1000,
|
|
1445
|
-
delay: 500,
|
|
1446
|
-
fill: "none",
|
|
1447
|
-
});
|
|
1448
|
-
|
|
1449
|
-
updateAnimations(element);
|
|
1450
|
-
|
|
1451
|
-
assert.equal(
|
|
1452
|
-
animation.currentTime,
|
|
1453
|
-
0,
|
|
1454
|
-
"Animation should be at start when before delay with fill: none",
|
|
1455
|
-
);
|
|
1456
|
-
});
|
|
1457
|
-
|
|
1458
|
-
test("fill-mode backwards: animation before delay applies starting values", () => {
|
|
1459
|
-
const element = createTestElement({
|
|
1460
|
-
ownCurrentTimeMs: 250,
|
|
1461
|
-
});
|
|
1462
|
-
|
|
1463
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1464
|
-
duration: 1000,
|
|
1465
|
-
delay: 500,
|
|
1466
|
-
fill: "backwards",
|
|
1467
|
-
});
|
|
1468
|
-
|
|
1469
|
-
updateAnimations(element);
|
|
1470
|
-
|
|
1471
|
-
assert.equal(
|
|
1472
|
-
animation.currentTime,
|
|
1473
|
-
0,
|
|
1474
|
-
"Animation should be at start when before delay with fill: backwards",
|
|
1475
|
-
);
|
|
1476
|
-
});
|
|
1477
|
-
|
|
1478
|
-
test("fill-mode forwards: animation after completion holds final state", () => {
|
|
1479
|
-
const element = createTestElement({
|
|
1480
|
-
ownCurrentTimeMs: 1500,
|
|
1481
|
-
});
|
|
1482
|
-
|
|
1483
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1484
|
-
duration: 1000,
|
|
1485
|
-
fill: "forwards",
|
|
1486
|
-
});
|
|
1487
|
-
|
|
1488
|
-
updateAnimations(element);
|
|
1489
|
-
|
|
1490
|
-
assert.approximately(
|
|
1491
|
-
animation.currentTime as number,
|
|
1492
|
-
999,
|
|
1493
|
-
1,
|
|
1494
|
-
"Animation should be held at end with precision offset",
|
|
1495
|
-
);
|
|
1496
|
-
});
|
|
1497
|
-
|
|
1498
|
-
test("fill-mode both: applies both backwards and forwards behavior", () => {
|
|
1499
|
-
const element = createTestElement({
|
|
1500
|
-
ownCurrentTimeMs: 250,
|
|
1501
|
-
});
|
|
1502
|
-
|
|
1503
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1504
|
-
duration: 1000,
|
|
1505
|
-
delay: 500,
|
|
1506
|
-
fill: "both",
|
|
1507
|
-
});
|
|
1508
|
-
|
|
1509
|
-
updateAnimations(element);
|
|
1510
|
-
|
|
1511
|
-
assert.equal(
|
|
1512
|
-
animation.currentTime,
|
|
1513
|
-
0,
|
|
1514
|
-
"Animation should be at start when before delay with fill: both",
|
|
1515
|
-
);
|
|
1516
|
-
});
|
|
1517
|
-
|
|
1518
|
-
test("fill-mode forwards with element at exact end boundary", () => {
|
|
1519
|
-
const rootTimegroup = {
|
|
1520
|
-
currentTimeMs: 1000,
|
|
1521
|
-
durationMs: 1000,
|
|
1522
|
-
startTimeMs: 0,
|
|
1523
|
-
endTimeMs: 1000,
|
|
1524
|
-
tagName: "EF-TIMEGROUP",
|
|
1525
|
-
} as any;
|
|
1526
|
-
|
|
1527
|
-
const element = createTestElement({
|
|
1528
|
-
startTimeMs: 0,
|
|
1529
|
-
endTimeMs: 1000,
|
|
1530
|
-
durationMs: 1000,
|
|
1531
|
-
ownCurrentTimeMs: 1000,
|
|
1532
|
-
rootTimegroup,
|
|
1533
|
-
});
|
|
1534
|
-
|
|
1535
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1536
|
-
duration: 1000,
|
|
1537
|
-
fill: "forwards",
|
|
1538
|
-
});
|
|
1539
|
-
|
|
1540
|
-
updateAnimations(element);
|
|
1541
|
-
|
|
1542
|
-
assert.equal(
|
|
1543
|
-
element.style.display,
|
|
1544
|
-
"",
|
|
1545
|
-
"Element should be visible at exact end boundary (root element)",
|
|
1546
|
-
);
|
|
1547
|
-
|
|
1548
|
-
assert.approximately(
|
|
1549
|
-
animation.currentTime as number,
|
|
1550
|
-
999,
|
|
1551
|
-
1,
|
|
1552
|
-
"Animation should be coordinated near completion with precision offset",
|
|
1553
|
-
);
|
|
1554
|
-
});
|
|
1555
|
-
|
|
1556
|
-
test("fill-mode none: animation past completion has no effect", () => {
|
|
1557
|
-
const element = createTestElement({
|
|
1558
|
-
ownCurrentTimeMs: 1500,
|
|
1559
|
-
});
|
|
1560
|
-
|
|
1561
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1562
|
-
duration: 1000,
|
|
1563
|
-
fill: "none",
|
|
1564
|
-
});
|
|
1565
|
-
|
|
1566
|
-
updateAnimations(element);
|
|
1567
|
-
|
|
1568
|
-
assert.approximately(
|
|
1569
|
-
animation.currentTime as number,
|
|
1570
|
-
999,
|
|
1571
|
-
1,
|
|
1572
|
-
"Animation should still be coordinated at end even with fill: none",
|
|
1573
|
-
);
|
|
1574
|
-
});
|
|
1575
|
-
|
|
1576
|
-
test("fill-mode forwards with reverse direction", () => {
|
|
1577
|
-
const element = createTestElement({
|
|
1578
|
-
ownCurrentTimeMs: 1500,
|
|
1579
|
-
});
|
|
1580
|
-
|
|
1581
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1582
|
-
duration: 1000,
|
|
1583
|
-
direction: "reverse",
|
|
1584
|
-
fill: "forwards",
|
|
1585
|
-
});
|
|
1586
|
-
|
|
1587
|
-
updateAnimations(element);
|
|
1588
|
-
|
|
1589
|
-
assert.approximately(
|
|
1590
|
-
animation.currentTime as number,
|
|
1591
|
-
999,
|
|
1592
|
-
1,
|
|
1593
|
-
"Reverse animation with forwards fill should hold at logical end (visual start)",
|
|
1594
|
-
);
|
|
1595
|
-
});
|
|
1596
|
-
|
|
1597
|
-
test("fill-mode backwards with reverse direction", () => {
|
|
1598
|
-
const element = createTestElement({
|
|
1599
|
-
ownCurrentTimeMs: 250,
|
|
1600
|
-
});
|
|
1601
|
-
|
|
1602
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1603
|
-
duration: 1000,
|
|
1604
|
-
delay: 500,
|
|
1605
|
-
direction: "reverse",
|
|
1606
|
-
fill: "backwards",
|
|
1607
|
-
});
|
|
1608
|
-
|
|
1609
|
-
updateAnimations(element);
|
|
1610
|
-
|
|
1611
|
-
assert.equal(
|
|
1612
|
-
animation.currentTime,
|
|
1613
|
-
0,
|
|
1614
|
-
"Reverse animation with backwards fill should apply logical start (visual end) during delay",
|
|
1615
|
-
);
|
|
1616
|
-
});
|
|
1617
|
-
});
|
|
1618
|
-
|
|
1619
|
-
describe("animation-timing-function support", () => {
|
|
1620
|
-
test("ease timing function: correctly interpolates at midpoint", () => {
|
|
1621
|
-
const element = createTestElement({
|
|
1622
|
-
ownCurrentTimeMs: 500,
|
|
1623
|
-
});
|
|
1624
|
-
|
|
1625
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1626
|
-
duration: 1000,
|
|
1627
|
-
easing: "ease",
|
|
1628
|
-
});
|
|
1629
|
-
|
|
1630
|
-
updateAnimations(element);
|
|
1631
|
-
|
|
1632
|
-
assert.approximately(
|
|
1633
|
-
animation.currentTime as number,
|
|
1634
|
-
500,
|
|
1635
|
-
1,
|
|
1636
|
-
"Timeline position should be correct regardless of easing",
|
|
1637
|
-
);
|
|
1638
|
-
});
|
|
1639
|
-
|
|
1640
|
-
test("linear timing function: evenly distributes time", () => {
|
|
1641
|
-
const element = createTestElement({
|
|
1642
|
-
ownCurrentTimeMs: 500,
|
|
1643
|
-
});
|
|
1644
|
-
|
|
1645
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1646
|
-
duration: 1000,
|
|
1647
|
-
easing: "linear",
|
|
1648
|
-
});
|
|
1649
|
-
|
|
1650
|
-
updateAnimations(element);
|
|
1651
|
-
|
|
1652
|
-
assert.approximately(animation.currentTime as number, 500, 1);
|
|
1653
|
-
});
|
|
1654
|
-
|
|
1655
|
-
test("ease-in timing function: slow start", () => {
|
|
1656
|
-
const element = createTestElement({
|
|
1657
|
-
ownCurrentTimeMs: 250,
|
|
1658
|
-
});
|
|
1659
|
-
|
|
1660
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1661
|
-
duration: 1000,
|
|
1662
|
-
easing: "ease-in",
|
|
1663
|
-
});
|
|
1664
|
-
|
|
1665
|
-
updateAnimations(element);
|
|
1666
|
-
|
|
1667
|
-
assert.approximately(
|
|
1668
|
-
animation.currentTime as number,
|
|
1669
|
-
250,
|
|
1670
|
-
1,
|
|
1671
|
-
"Timeline coordination should be independent of easing curve",
|
|
1672
|
-
);
|
|
1673
|
-
});
|
|
1674
|
-
|
|
1675
|
-
test("ease-out timing function: slow end", () => {
|
|
1676
|
-
const element = createTestElement({
|
|
1677
|
-
ownCurrentTimeMs: 750,
|
|
1678
|
-
});
|
|
1679
|
-
|
|
1680
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1681
|
-
duration: 1000,
|
|
1682
|
-
easing: "ease-out",
|
|
1683
|
-
});
|
|
1684
|
-
|
|
1685
|
-
updateAnimations(element);
|
|
1686
|
-
|
|
1687
|
-
assert.approximately(animation.currentTime as number, 750, 1);
|
|
1688
|
-
});
|
|
1689
|
-
|
|
1690
|
-
test("ease-in-out timing function: slow start and end", () => {
|
|
1691
|
-
const element = createTestElement({
|
|
1692
|
-
ownCurrentTimeMs: 500,
|
|
1693
|
-
});
|
|
1694
|
-
|
|
1695
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1696
|
-
duration: 1000,
|
|
1697
|
-
easing: "ease-in-out",
|
|
1698
|
-
});
|
|
1699
|
-
|
|
1700
|
-
updateAnimations(element);
|
|
1701
|
-
|
|
1702
|
-
assert.approximately(animation.currentTime as number, 500, 1);
|
|
1703
|
-
});
|
|
1704
|
-
|
|
1705
|
-
test("cubic-bezier timing function: custom curve", () => {
|
|
1706
|
-
const element = createTestElement({
|
|
1707
|
-
ownCurrentTimeMs: 400,
|
|
1708
|
-
});
|
|
1709
|
-
|
|
1710
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1711
|
-
duration: 1000,
|
|
1712
|
-
easing: "cubic-bezier(0.42, 0, 0.58, 1)",
|
|
1713
|
-
});
|
|
1714
|
-
|
|
1715
|
-
updateAnimations(element);
|
|
1716
|
-
|
|
1717
|
-
assert.approximately(animation.currentTime as number, 400, 1);
|
|
1718
|
-
});
|
|
1719
|
-
|
|
1720
|
-
test("steps timing function: start - discrete jumps at interval starts", () => {
|
|
1721
|
-
const element = createTestElement({
|
|
1722
|
-
ownCurrentTimeMs: 250,
|
|
1723
|
-
});
|
|
1724
|
-
|
|
1725
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1726
|
-
duration: 1000,
|
|
1727
|
-
easing: "steps(4, start)",
|
|
1728
|
-
});
|
|
1729
|
-
|
|
1730
|
-
updateAnimations(element);
|
|
1731
|
-
|
|
1732
|
-
assert.approximately(
|
|
1733
|
-
animation.currentTime as number,
|
|
1734
|
-
250,
|
|
1735
|
-
1,
|
|
1736
|
-
"Steps timing should work with timeline coordination",
|
|
1737
|
-
);
|
|
1738
|
-
});
|
|
1739
|
-
|
|
1740
|
-
test("steps timing function: end - discrete jumps at interval ends", () => {
|
|
1741
|
-
const element = createTestElement({
|
|
1742
|
-
ownCurrentTimeMs: 250,
|
|
1743
|
-
});
|
|
1744
|
-
|
|
1745
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1746
|
-
duration: 1000,
|
|
1747
|
-
easing: "steps(4, end)",
|
|
1748
|
-
});
|
|
1749
|
-
|
|
1750
|
-
updateAnimations(element);
|
|
1751
|
-
|
|
1752
|
-
assert.approximately(animation.currentTime as number, 250, 1);
|
|
1753
|
-
});
|
|
1754
|
-
|
|
1755
|
-
test("step-start timing function: immediate jump to end value", () => {
|
|
1756
|
-
const element = createTestElement({
|
|
1757
|
-
ownCurrentTimeMs: 100,
|
|
1758
|
-
});
|
|
1759
|
-
|
|
1760
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1761
|
-
duration: 1000,
|
|
1762
|
-
easing: "step-start",
|
|
1763
|
-
});
|
|
1764
|
-
|
|
1765
|
-
updateAnimations(element);
|
|
1766
|
-
|
|
1767
|
-
assert.approximately(
|
|
1768
|
-
animation.currentTime as number,
|
|
1769
|
-
100,
|
|
1770
|
-
1,
|
|
1771
|
-
"Step-start should work with timeline coordination",
|
|
1772
|
-
);
|
|
1773
|
-
});
|
|
1774
|
-
|
|
1775
|
-
test("step-end timing function: hold start value until end", () => {
|
|
1776
|
-
const element = createTestElement({
|
|
1777
|
-
ownCurrentTimeMs: 999,
|
|
1778
|
-
});
|
|
1779
|
-
|
|
1780
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1781
|
-
duration: 1000,
|
|
1782
|
-
easing: "step-end",
|
|
1783
|
-
});
|
|
1784
|
-
|
|
1785
|
-
updateAnimations(element);
|
|
1786
|
-
|
|
1787
|
-
assert.approximately(animation.currentTime as number, 999, 1);
|
|
1788
|
-
});
|
|
1789
|
-
|
|
1790
|
-
test("timing function with reverse direction", () => {
|
|
1791
|
-
const element = createTestElement({
|
|
1792
|
-
ownCurrentTimeMs: 300,
|
|
1793
|
-
});
|
|
1794
|
-
|
|
1795
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1796
|
-
duration: 1000,
|
|
1797
|
-
direction: "reverse",
|
|
1798
|
-
easing: "ease-in",
|
|
1799
|
-
});
|
|
1800
|
-
|
|
1801
|
-
updateAnimations(element);
|
|
1802
|
-
|
|
1803
|
-
assert.approximately(
|
|
1804
|
-
animation.currentTime as number,
|
|
1805
|
-
700,
|
|
1806
|
-
1,
|
|
1807
|
-
"Reverse direction should correctly invert time with easing",
|
|
1808
|
-
);
|
|
1809
|
-
});
|
|
1810
|
-
|
|
1811
|
-
test("timing function with alternate direction", () => {
|
|
1812
|
-
const element = createTestElement({
|
|
1813
|
-
ownCurrentTimeMs: 1300,
|
|
1814
|
-
});
|
|
1815
|
-
|
|
1816
|
-
const animation = element.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
1817
|
-
duration: 1000,
|
|
1818
|
-
iterations: 3,
|
|
1819
|
-
direction: "alternate",
|
|
1820
|
-
easing: "ease-out",
|
|
1821
|
-
});
|
|
1822
|
-
|
|
1823
|
-
updateAnimations(element);
|
|
1824
|
-
|
|
1825
|
-
assert.approximately(
|
|
1826
|
-
animation.currentTime as number,
|
|
1827
|
-
700,
|
|
1828
|
-
1,
|
|
1829
|
-
"Alternate direction should work with easing on reversed iterations",
|
|
1830
|
-
);
|
|
1831
|
-
});
|
|
1832
|
-
|
|
1833
|
-
test("multiple animations with different timing functions", () => {
|
|
1834
|
-
const element = createTestElement({
|
|
1835
|
-
ownCurrentTimeMs: 500,
|
|
1836
|
-
});
|
|
1837
|
-
|
|
1838
|
-
const linearAnimation = element.animate(
|
|
1839
|
-
[{ opacity: 0 }, { opacity: 1 }],
|
|
1840
|
-
{
|
|
1841
|
-
duration: 1000,
|
|
1842
|
-
easing: "linear",
|
|
1843
|
-
},
|
|
1844
|
-
);
|
|
1845
|
-
|
|
1846
|
-
const easeAnimation = element.animate(
|
|
1847
|
-
[{ transform: "scale(1)" }, { transform: "scale(2)" }],
|
|
1848
|
-
{
|
|
1849
|
-
duration: 1000,
|
|
1850
|
-
easing: "ease-in-out",
|
|
1851
|
-
},
|
|
1852
|
-
);
|
|
1853
|
-
|
|
1854
|
-
const stepsAnimation = element.animate(
|
|
1855
|
-
[{ color: "red" }, { color: "blue" }],
|
|
1856
|
-
{
|
|
1857
|
-
duration: 1000,
|
|
1858
|
-
easing: "steps(5, end)",
|
|
1859
|
-
},
|
|
1860
|
-
);
|
|
1861
|
-
|
|
1862
|
-
updateAnimations(element);
|
|
1863
|
-
|
|
1864
|
-
assert.approximately(
|
|
1865
|
-
linearAnimation.currentTime as number,
|
|
1866
|
-
500,
|
|
1867
|
-
1,
|
|
1868
|
-
"Linear animation should be at midpoint",
|
|
1869
|
-
);
|
|
1870
|
-
assert.approximately(
|
|
1871
|
-
easeAnimation.currentTime as number,
|
|
1872
|
-
500,
|
|
1873
|
-
1,
|
|
1874
|
-
"Ease animation should be at midpoint timeline",
|
|
1875
|
-
);
|
|
1876
|
-
assert.approximately(
|
|
1877
|
-
stepsAnimation.currentTime as number,
|
|
1878
|
-
500,
|
|
1879
|
-
1,
|
|
1880
|
-
"Steps animation should be at midpoint timeline",
|
|
1881
|
-
);
|
|
1882
|
-
});
|
|
1883
|
-
});
|
|
1884
|
-
});
|