@editframe/elements 0.25.1-beta.0 → 0.26.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.
Files changed (57) hide show
  1. package/dist/elements/EFAudio.d.ts +4 -4
  2. package/dist/elements/EFCaptions.d.ts +12 -12
  3. package/dist/elements/EFImage.d.ts +4 -4
  4. package/dist/elements/EFMedia/AssetMediaEngine.js +2 -1
  5. package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
  6. package/dist/elements/EFMedia/BaseMediaEngine.js +13 -0
  7. package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
  8. package/dist/elements/EFMedia/JitMediaEngine.js +2 -1
  9. package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
  10. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +11 -4
  11. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js.map +1 -1
  12. package/dist/elements/EFMedia/shared/BufferUtils.js +16 -1
  13. package/dist/elements/EFMedia/shared/BufferUtils.js.map +1 -1
  14. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +11 -4
  15. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js.map +1 -1
  16. package/dist/elements/EFMedia.d.ts +2 -2
  17. package/dist/elements/EFSurface.d.ts +4 -4
  18. package/dist/elements/EFTemporal.js +16 -2
  19. package/dist/elements/EFTemporal.js.map +1 -1
  20. package/dist/elements/EFThumbnailStrip.d.ts +4 -4
  21. package/dist/elements/EFTimegroup.d.ts +22 -0
  22. package/dist/elements/EFTimegroup.js +35 -0
  23. package/dist/elements/EFTimegroup.js.map +1 -1
  24. package/dist/elements/EFVideo.d.ts +4 -4
  25. package/dist/elements/EFWaveform.d.ts +4 -4
  26. package/dist/elements/updateAnimations.js +3 -1
  27. package/dist/elements/updateAnimations.js.map +1 -1
  28. package/dist/gui/EFConfiguration.d.ts +4 -4
  29. package/dist/gui/EFControls.d.ts +2 -2
  30. package/dist/gui/EFDial.d.ts +4 -4
  31. package/dist/gui/EFFocusOverlay.d.ts +4 -4
  32. package/dist/gui/EFPause.d.ts +2 -2
  33. package/dist/gui/EFPlay.d.ts +2 -2
  34. package/dist/gui/EFPreview.d.ts +4 -4
  35. package/dist/gui/EFResizableBox.d.ts +4 -4
  36. package/dist/gui/EFScrubber.d.ts +4 -4
  37. package/dist/gui/EFTimeDisplay.d.ts +4 -4
  38. package/dist/gui/EFToggleLoop.d.ts +2 -2
  39. package/dist/gui/EFTogglePlay.d.ts +4 -4
  40. package/dist/gui/EFWorkbench.d.ts +6 -6
  41. package/dist/style.css +10 -0
  42. package/dist/transcoding/types/index.d.ts +1 -0
  43. package/package.json +2 -2
  44. package/src/elements/EFMedia/AssetMediaEngine.ts +1 -0
  45. package/src/elements/EFMedia/BaseMediaEngine.ts +20 -0
  46. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +68 -0
  47. package/src/elements/EFMedia/JitMediaEngine.ts +1 -0
  48. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +12 -0
  49. package/src/elements/EFMedia/shared/BufferUtils.ts +42 -0
  50. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +12 -0
  51. package/src/elements/EFTemporal.ts +20 -4
  52. package/src/elements/EFTimegroup.browsertest.ts +198 -0
  53. package/src/elements/EFTimegroup.ts +57 -0
  54. package/src/elements/updateAnimations.browsertest.ts +801 -0
  55. package/src/elements/updateAnimations.ts +12 -1
  56. package/src/transcoding/types/index.ts +1 -0
  57. package/types.json +1 -1
@@ -1080,4 +1080,805 @@ describe("Timeline Element Synchronizer", () => {
1080
1080
  );
1081
1081
  });
1082
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
+ });
1083
1884
  });