From 3a01cf5b4ea054075a720aaf7ecadff7725c2db9 Mon Sep 17 00:00:00 2001 From: Kevin Ellis Date: Mon, 27 Mar 2023 08:50:21 -0700 Subject: [PATCH] Fix formatting of getKeyframes response. * Show specified offsets in getKeyframes * Report unreachable keyframes (computed offset may be null) * Handle mix of computed and timeline offsets * Sort as follows: * CSS: percent in ascending order before timeline offsets in specified order * Programmatic: preserve specified order. Bug: 1424543 Change-Id: I9d243d15cb37243aa3e7c1b50000c378bea51ae9 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4341670 Reviewed-by: Robert Flack Commit-Queue: Kevin Ellis Cr-Commit-Position: refs/heads/main@{#1122433} --- ...in-keyframe-change-timeline.tentative.html | 35 ++- ...eline-offset-keyframes-hidden-subject.html | 16 +- ...fset-keyframes-with-document-timeline.html | 20 +- .../get-keyframes-with-timeline-offset.html | 203 ++++++++++++++++++ web-animations/resources/keyframe-utils.js | 14 +- 5 files changed, 267 insertions(+), 21 deletions(-) create mode 100644 scroll-animations/view-timelines/get-keyframes-with-timeline-offset.html diff --git a/scroll-animations/css/timeline-offset-in-keyframe-change-timeline.tentative.html b/scroll-animations/css/timeline-offset-in-keyframe-change-timeline.tentative.html index 6456e8aa779da7..5ac54081845605 100644 --- a/scroll-animations/css/timeline-offset-in-keyframe-change-timeline.tentative.html +++ b/scroll-animations/css/timeline-offset-in-keyframe-change-timeline.tentative.html @@ -76,7 +76,14 @@ // Initially using a document timeline, so the keyframes should be // ignored. let frames = anim.effect.getKeyframes(); - let expected = []; + let expected = [ + { offset: { rangeName: 'cover', offset: CSS.percent(0) }, + computedOffset: null, easing: "linear", composite: "auto", + marginLeft: "0px", opacity: "0" }, + { offset: { rangeName: 'cover', offset: CSS.percent(100) }, + computedOffset: null, easing: "linear", composite: "auto", + marginRight: "0px", opacity: "1" } + ]; assert_frame_lists_equal(frames, expected); // Once a view-timeline is added, the kefyrames must update to reflect @@ -88,13 +95,15 @@ frames = anim.effect.getKeyframes(); expected = [ - { offset: -1, computedOffset: -1, easing: "linear", composite: "auto", - marginLeft: "0px", opacity: "0" }, { offset: 0, computedOffset: 0, easing: "linear", composite: "replace", marginRight: "10px" }, { offset: 1, computedOffset: 1, easing: "linear", composite: "replace", marginLeft: "10px" }, - { offset: 2, computedOffset: 2, easing: "linear", composite: "auto", + { offset: { rangeName: 'cover', offset: CSS.percent(0) }, + computedOffset: -1, easing: "linear", composite: "auto", + marginLeft: "0px", opacity: "0" }, + { offset: { rangeName: 'cover', offset: CSS.percent(100) }, + computedOffset: 2, easing: "linear", composite: "auto", marginRight: "0px", opacity: "1" }, ]; assert_frame_lists_equal(frames, expected); @@ -105,13 +114,15 @@ await waitForNextFrame(); frames = anim.effect.getKeyframes(); expected = [ - { offset: -1/3, computedOffset: -1/3, easing: "linear", - composite: "auto", marginLeft: "0px", opacity: "0" }, { offset: 0, computedOffset: 0, easing: "linear", composite: "replace", marginRight: "10px" }, { offset: 1, computedOffset: 1, easing: "linear", composite: "replace", marginLeft: "10px" }, - { offset: 4/3, computedOffset: 4/3, easing: "linear", composite: "auto", + { offset: { rangeName: 'cover', offset: CSS.percent(0) }, + computedOffset: -1/3, easing: "linear", + composite: "auto", marginLeft: "0px", opacity: "0" }, + { offset: { rangeName: 'cover', offset: CSS.percent(100) }, + computedOffset: 4/3, easing: "linear", composite: "auto", marginRight: "0px", opacity: "1" }, ]; assert_frame_lists_equal(frames, expected); @@ -120,9 +131,15 @@ assert_equals(getComputedStyle(target).animationTimeline, 'auto', 'Switch back to document timeline'); frames = anim.effect.getKeyframes(); - expected = []; + expected = [ + { offset: { rangeName: 'cover', offset: CSS.percent(0) }, + computedOffset: null, easing: "linear", composite: "auto", + marginLeft: "0px", opacity: "0" }, + { offset: { rangeName: 'cover', offset: CSS.percent(100) }, + computedOffset: null, easing: "linear", composite: "auto", + marginRight: "0px", opacity: "1" } + ]; assert_frame_lists_equal(frames, expected); - }, 'getKeyframes with timeline-offsets'); } diff --git a/scroll-animations/css/timeline-offset-keyframes-hidden-subject.html b/scroll-animations/css/timeline-offset-keyframes-hidden-subject.html index d3b124ba18e39d..047acfed33f36a 100644 --- a/scroll-animations/css/timeline-offset-keyframes-hidden-subject.html +++ b/scroll-animations/css/timeline-offset-keyframes-hidden-subject.html @@ -68,15 +68,17 @@ let frames = anim.effect.getKeyframes(); let expected_resolved_offsets = [ - { offset: -1/3, computedOffset: -1/3, easing: "linear", - composite: "auto", marginLeft: "0px" }, { offset: 0, computedOffset: 0, easing: "linear", composite: "replace", marginRight: "10px", opacity: "1" }, { offset: 1/2, computedOffset: 1/2, easing: "linear", composite: "auto", opacity: "0.5" }, { offset: 1, computedOffset: 1, easing: "linear", composite: "replace", marginLeft: "10px", opacity: "1" }, - { offset: 4/3, computedOffset: 4/3, easing: "linear", composite: "auto", + { offset: { rangeName: 'cover', offset: CSS.percent(0) }, + computedOffset: -1/3, easing: "linear", + composite: "auto", marginLeft: "0px" }, + { offset: { rangeName: 'cover', offset: CSS.percent(100) }, + computedOffset: 4/3, easing: "linear", composite: "auto", marginRight: "0px" }, ]; assert_frame_lists_equal(frames, expected_resolved_offsets, @@ -92,7 +94,13 @@ { offset: 0.5, computedOffset: 0.5, opacity: "0.5", easing: "linear", composite: "auto", }, { offset: 1, computedOffset: 1, opacity: "1", easing: "linear", - composite: "replace" } + composite: "replace" }, + { offset: { rangeName: 'cover', offset: CSS.percent(0) }, + computedOffset: null, easing: "linear", + composite: "auto", marginLeft: "0px" }, + { offset: { rangeName: 'cover', offset: CSS.percent(100) }, + computedOffset: null, easing: "linear", composite: "auto", + marginRight: "0px" } ]; assert_frame_lists_equal(frames, expected_unresolved_offsets, 'Keyframes with invalid view timeline'); diff --git a/scroll-animations/css/timeline-offset-keyframes-with-document-timeline.html b/scroll-animations/css/timeline-offset-keyframes-with-document-timeline.html index 95a0ea4eae0cbf..03ee381fd9276f 100644 --- a/scroll-animations/css/timeline-offset-keyframes-with-document-timeline.html +++ b/scroll-animations/css/timeline-offset-keyframes-with-document-timeline.html @@ -12,15 +12,15 @@ + +
+
+
+ + + diff --git a/web-animations/resources/keyframe-utils.js b/web-animations/resources/keyframe-utils.js index 8e6e5840f7f872..60fb9781a0c080 100644 --- a/web-animations/resources/keyframe-utils.js +++ b/web-animations/resources/keyframe-utils.js @@ -31,8 +31,20 @@ function assert_frames_equal(a, b, name) { `properties on ${name} should match`); // Iterates sorted keys to ensure stable failures. for (const p of Object.keys(a).sort()) { - if (typeof a[p] == 'number') + if (typeof b[p] == 'number') assert_approx_equals(a[p], b[p], 1e-6, `value for '${p}' on ${name}`); + else if (typeof b[p] == 'object') { + for (const key in b[p]) { + if (typeof b[p][key] == 'number') { + assert_approx_equals(a[p][key], b[p][key], 1e-6, + `value for '${p}.${key}' on ${name}`); + } else { + assert_equals((a[p][key] || 'undefined').toString(), + b[p][key].toString(), + `value for '${p}.${key}' on ${name}`); + } + } + } else assert_equals(a[p], b[p], `value for '${p}' on ${name}`); }