Skip to content

Commit 8f0416b

Browse files
[zoom] Make zoom continuously interpolatable behind a flag
Implements the CSSWG resolution[1] to animate `zoom` by computed value (number) instead of discretely. Gated on the experimental `CSSZoomAnimation` runtime flag. ChromeStatus: https://chromestatus.com/feature/5183671737909248 [1]: w3c/csswg-drafts#10872 Bug: 393810951 Change-Id: I5dee54793269a9751a33e9904484af99911a88c1 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7819114 Reviewed-by: Rune Lillesveen <futhark@chromium.org> Commit-Queue: Jason Leo <cgqaq@chromium.org> Cr-Commit-Position: refs/heads/main@{#1628654}
1 parent 048cb87 commit 8f0416b

1 file changed

Lines changed: 206 additions & 0 deletions

File tree

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
<!DOCTYPE html>
2+
<meta charset="UTF-8">
3+
<title>zoom interpolation</title>
4+
<link rel="help" href="https://drafts.csswg.org/css-viewport/#zoom-property">
5+
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/10872">
6+
<meta name="assert" content="zoom is animated by computed value (number).">
7+
8+
<script src="/resources/testharness.js"></script>
9+
<script src="/resources/testharnessreport.js"></script>
10+
<script src="/resources/testdriver.js"></script>
11+
<script src="/resources/testdriver-actions.js"></script>
12+
<script src="/resources/testdriver-vendor.js"></script>
13+
<script src="/css/support/interpolation-testcommon.js"></script>
14+
15+
<style>
16+
.parent {
17+
zoom: 3;
18+
}
19+
.target {
20+
width: 10px;
21+
height: 10px;
22+
background: black;
23+
zoom: 1;
24+
}
25+
</style>
26+
27+
<body>
28+
<template id="target-template">
29+
<div class="parent">
30+
<div class="target"></div>
31+
</div>
32+
</template>
33+
</body>
34+
35+
<script>
36+
test_interpolation({
37+
property: 'zoom',
38+
from: neutralKeyframe,
39+
to: '2',
40+
}, [
41+
{at: -0.3, expect: '0.7'},
42+
{at: 0, expect: '1'},
43+
{at: 0.3, expect: '1.3'},
44+
{at: 0.6, expect: '1.6'},
45+
{at: 1, expect: '2'},
46+
{at: 1.5, expect: '2.5'},
47+
]);
48+
49+
test_interpolation({
50+
property: 'zoom',
51+
from: 'initial',
52+
to: '2',
53+
}, [
54+
{at: -0.3, expect: '0.7'},
55+
{at: 0, expect: '1'},
56+
{at: 0.3, expect: '1.3'},
57+
{at: 0.6, expect: '1.6'},
58+
{at: 1, expect: '2'},
59+
{at: 1.5, expect: '2.5'},
60+
]);
61+
62+
test_interpolation({
63+
property: 'zoom',
64+
from: 'inherit',
65+
to: '2',
66+
}, [
67+
{at: -0.3, expect: '3.3'},
68+
{at: 0, expect: '3'},
69+
{at: 0.3, expect: '2.7'},
70+
{at: 0.6, expect: '2.4'},
71+
{at: 1, expect: '2'},
72+
{at: 1.5, expect: '1.5'},
73+
]);
74+
75+
test_interpolation({
76+
property: 'zoom',
77+
from: 'unset',
78+
to: '2',
79+
}, [
80+
{at: -0.3, expect: '0.7'},
81+
{at: 0, expect: '1'},
82+
{at: 0.3, expect: '1.3'},
83+
{at: 0.6, expect: '1.6'},
84+
{at: 1, expect: '2'},
85+
{at: 1.5, expect: '2.5'},
86+
]);
87+
88+
test_interpolation({
89+
property: 'zoom',
90+
from: '1',
91+
to: '2',
92+
}, [
93+
// at: -5 yields a raw value of -4; zoom is clamped to >= 0, and the legacy
94+
// `zoom: 0` -> 1 conversion then folds that to 1.
95+
{at: -5, expect: '1'},
96+
{at: -0.3, expect: '0.7'},
97+
{at: 0, expect: '1'},
98+
{at: 0.3, expect: '1.3'},
99+
{at: 0.6, expect: '1.6'},
100+
{at: 1, expect: '2'},
101+
{at: 1.5, expect: '2.5'},
102+
]);
103+
104+
test_interpolation({
105+
property: 'zoom',
106+
from: '0.5',
107+
to: '1.5',
108+
}, [
109+
{at: -5, expect: '1'},
110+
{at: -0.3, expect: '0.2'},
111+
{at: 0, expect: '0.5'},
112+
{at: 0.5, expect: '1'},
113+
{at: 1, expect: '1.5'},
114+
{at: 1.5, expect: '2'},
115+
]);
116+
117+
test_interpolation({
118+
property: 'zoom',
119+
from: 'normal',
120+
to: '3',
121+
}, [
122+
{at: -0.5, expect: '1'},
123+
{at: 0, expect: '1'},
124+
{at: 0.5, expect: '2'},
125+
{at: 1, expect: '3'},
126+
]);
127+
128+
test_interpolation({
129+
property: 'zoom',
130+
from: '100%',
131+
to: '300%',
132+
}, [
133+
{at: 0, expect: '1'},
134+
{at: 0.5, expect: '2'},
135+
{at: 1, expect: '3'},
136+
]);
137+
138+
test_interpolation({
139+
property: 'zoom',
140+
from: '50%',
141+
to: '2',
142+
}, [
143+
{at: 0, expect: '0.5'},
144+
{at: 0.5, expect: '1.25'},
145+
{at: 1, expect: '2'},
146+
]);
147+
148+
// Element-dependent percentage: sibling-index() == 1 inside the test
149+
// template, so calc(sibling-index() * 100%) resolves to 1. This exercises
150+
// the TreeCountingChecker path in CSSZoomInterpolationType::MaybeConvertValue.
151+
test_interpolation({
152+
property: 'zoom',
153+
from: 'calc(sibling-index() * 100%)',
154+
to: '2',
155+
}, [
156+
{at: 0, expect: '1'},
157+
{at: 0.5, expect: '1.5'},
158+
{at: 1, expect: '2'},
159+
]);
160+
161+
test(() => {
162+
const target = document.createElement('div');
163+
target.style.width = '10px';
164+
target.style.height = '10px';
165+
document.body.appendChild(target);
166+
167+
const animation = target.animate(
168+
[{zoom: '1'}, {zoom: '2'}],
169+
{duration: 1000, fill: 'both'});
170+
animation.pause();
171+
animation.currentTime = 500;
172+
173+
assert_approx_equals(parseFloat(getComputedStyle(target).zoom), 1.5, 0.01);
174+
assert_approx_equals(target.getBoundingClientRect().width, 15, 0.01);
175+
176+
target.remove();
177+
}, 'Animating zoom affects layout using the interpolated value');
178+
179+
promise_test(async () => {
180+
const target = document.createElement('div');
181+
target.id = 'hover-target';
182+
// 100s duration with -50s delay puts the transition at 50% progress on the
183+
// first frame after :hover takes effect, so the sample is deterministic.
184+
target.style.cssText = `
185+
width: 100px;
186+
height: 100px;
187+
transition: all 100s linear -50s;
188+
`;
189+
document.body.appendChild(target);
190+
191+
const style = document.createElement('style');
192+
style.textContent = '#hover-target:hover { zoom: 2; }';
193+
document.head.appendChild(style);
194+
195+
await new test_driver.Actions()
196+
.pointerMove(0, 0, {origin: target})
197+
.send();
198+
199+
assert_true(target.matches(':hover'));
200+
assert_approx_equals(parseFloat(getComputedStyle(target).zoom), 1.5, 0.01);
201+
assert_approx_equals(target.getBoundingClientRect().width, 150, 1);
202+
203+
target.remove();
204+
style.remove();
205+
}, 'transition: all animates zoom changes from :hover');
206+
</script>

0 commit comments

Comments
 (0)