Skip to content

Commit

Permalink
View transitions layered capture
Browse files Browse the repository at this point in the history
Instead of baking tree-effects into view transition snapshots,
render the snapshot without these effects and add them as CSS to the
::view-transition-group pseudo-element.

This is done by changing the effect order in PaintPropertyTreeBuilder, so that these effects are ancestors of the capture effect. In that way
the effects are not applied inside the capture.

Also had to modify draw_property_utils, to ensure some of the surface
computations don't assume that the view-transition capture would later
be painted into an effect surface.

See design document with detailed conversation and alternatives:
https://docs.google.com/document/d/1jSkIqqlrI4rzZ34cTWC-6TB81cspvHxTcpBD6nDCbE4/edit?tab=t.0#heading=h.tf5nf0fynnc2

Impacted CSS properties:
- opacity
- mask
- clip-path
- filter

The following properties are also affected, will need a follow-up patch to finish this:
- clip
- border-radius (when clipping overflow). This is not entirely set up yet, and will follow up.

Bug: 347947051
Change-Id: I8f24adf0c2be89ff673f111f099eac065fd4bf7f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5789282
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: Khushal Sagar <khushalsagar@chromium.org>
Commit-Queue: Noam Rosenthal <nrosenthal@chromium.org>
Reviewed-by: Vladimir Levin <vmpstr@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1358591}
  • Loading branch information
noamr authored and chromium-wpt-export-bot committed Sep 21, 2024
1 parent aebd0be commit 8e164c2
Show file tree
Hide file tree
Showing 18 changed files with 448 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
animation-duration: 500s;
opacity: 0;
}
::view-transition-image-pair(root) {
display: none;
}
::view-transition-group(target) {
animation-duration: 0s;
}
Expand Down
26 changes: 26 additions & 0 deletions css/css-view-transitions/layered-capture/nested-opacity-ref.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<body>
<style>
body {
margin: 0;
}

.parent {
opacity: .4;
}
.child {
left: 50px;
top: 50px;
}
div.green {
position: absolute;
width: 100px;
height: 100px;
background: green;
will-change: opacity;
}
</style>
<div class="green parent">
<div class="green child"></div>
</div>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html class=reftest-wait>
<title>Captured opacity is applied as group style</title>
<meta name=fuzzy content="maxDifference=0-255; totalPixels=0-515">
<link rel="help" href="https://drafts.csswg.org/css-view-transitions-2/">
<link rel="match" href="nested-opacity-ref.html">
<script src="/common/reftest-wait.js"></script>
<script src="../nested/resources/compute-test.js"></script>
<style>
body {
margin: 0;
}
div {
position: absolute;
width: 100px;
height: 100px;
background: green;
}

.parent {
opacity: 0.7;
view-transition-name: parent;
will-change: opacity;
}

.child {
top: 50px;
left: 50px;
}

::view-transition-group(parent) {
opacity: 0.4;
animation-name: none;
}
::view-transition-old(*),
::view-transition-new(*) {
animation-play-state: paused;
}
</style>
<body>
<div class="parent">
<div class="child"></div>
</div>
</body>
39 changes: 39 additions & 0 deletions css/css-view-transitions/layered-capture/tree-effects-ref.sub.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<title>Tree effects are captured as group style</title>
<link rel="help" href="https://drafts.csswg.org/css-view-transitions-2/">
<style>
body {
margin: 0;
background: lightpink;
}

.target {
width: 100px;
height: 100px;
margin: 20px;
background: green;
will-change: {{GET[prop]}};
}

@keyframes anim {
from { {{GET[prop]}}: {{GET[old]}}; }
to { {{GET[prop]}}: {{GET[new]}}; }
}

.passthrough {
{{GET[prop]}}: {{GET[old]}};
}

.animate {
animation-name: anim;
animation-timing-function: steps(1, start);
animation-play-state: paused;
animation-duration: 100s;
}
</style>
<body>
<div class="override target"></div>
<div class="passthrough target"></div>
<div class="animate target"></div>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!DOCTYPE html>
<html class=reftest-wait>
<title>Tree effects are captured as group style</title>
<meta name=fuzzy content="maxDifference=0-10; totalPixels=0-200000">
<meta name="variant" content="?prop=filter&old=blur(3px)&new=blur(10px)">
<meta name="variant" content="?prop=opacity&old=0.3&new=1">
<meta name="variant" content="?prop=clip-path&old=inset(10px 10px)&new=inset(20px 20px)">
<meta name="variant" content="?prop=mask-image&old=linear-gradient(rgb(0 0 0 / 80%), transparent)&new=none">
<meta name="variant" content="?prop=mask&old=linear-gradient(red,blue) luminance&new=none">
<link rel="help" href="https://drafts.csswg.org/css-view-transitions-2/">
<link rel="match" href="tree-effects-ref.sub.html">
<script src="/common/reftest-wait.js"></script>
<style>
html {
view-transition-name: none;
}
body {
margin: 0;
}

.target {
width: 100px;
height: 100px;
margin: 20px;
background: green;
{{GET[prop]}}: {{GET[old]}};
}

html.vt-new .target {
{{GET[prop]}}: {{GET[new]}};
}

.override {
view-transition-name: override;
}

.passthrough {
view-transition-name: passthrough;
}
.animate {
view-transition-name: animate;
}

html::view-transition-group(*) {
animation-play-state: paused;
}

html::view-transition-group(override) {
{{GET[prop]}}: unset;
animation: none;
}

html::view-transition-group(animate) {
animation-timing-function: steps(1, start);
animation-play-state: paused;
animation-duration: 100s;
}

html::view-transition {
background: lightpink;
}
</style>
<body>
<div class="override target"></div>
<div class="passthrough target"></div>
<div class="animate target"></div>
<script>
const transition = document.startViewTransition(() => {
document.documentElement.classList.add("vt-new");
});
transition.ready.then(() => takeScreenshot());
</script>
</body>
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
background: lightpink;
}
::view-transition-group(root) {
animation-duration: 300s;
opacity: 0;
visibility: hidden;
animation-play-state: paused;
}

::view-transition-old(inner), ::view-transition-new(inner) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<style>
::view-transition-group(yellow) {
opacity: 0;
background: yellow;
animation: none;
}

.yellow {
Expand Down
26 changes: 26 additions & 0 deletions css/css-view-transitions/nested/nested-opacity-ref.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<body>
<style>
body {
margin: 0;
}

.parent {
opacity: .4;
}
.child {
left: 50px;
top: 50px;
}
div.green {
position: absolute;
width: 100px;
height: 100px;
background: green;
will-change: opacity;
}
</style>
<div class="green parent">
<div class="green child"></div>
</div>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html class=reftest-wait>
<title>Opacity should apply to the whole group</title>
<meta name=fuzzy content="maxDifference=0-255; totalPixels=0-515">
<link rel="help" href="https://drafts.csswg.org/css-view-transitions-2/">
<link rel="match" href="nested-opacity-ref.html">
<script src="/common/reftest-wait.js"></script>
<script src="resources/compute-test.js"></script>
<style>
body {
margin: 0;
}
div {
position: absolute;
width: 100px;
height: 100px;
background: green;
}

.parent {
view-transition-name: parent;
}

.vt-old .parent {
opacity: 0.4;
}

.child {
view-transition-name: child;
view-transition-group: parent;
top: 50px;
left: 50px;
}

::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation-play-state: paused;
}
</style>
<body>
<div class="parent">
<div class="child"></div>
</div>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html class=reftest-wait>
<title>Opacity should apply to the whole group</title>
<meta name=fuzzy content="maxDifference=0-255; totalPixels=0-515">
<link rel="help" href="https://drafts.csswg.org/css-view-transitions-2/">
<link rel="match" href="nested-opacity-ref.html">
<script src="/common/reftest-wait.js"></script>
<script src="resources/compute-test.js"></script>
<style>
body {
margin: 0;
}
div {
position: absolute;
width: 100px;
height: 100px;
background: green;
}

.parent {
view-transition-name: parent;
opacity: 0.4;
}

.child {
view-transition-name: child;
view-transition-group: parent;
top: 50px;
left: 50px;
}

::view-transition-old(*),
::view-transition-new(*) {
animation-play-state: paused;
}
</style>
<body>
<div class="parent">
<div class="child"></div>
</div>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<title>Applied opacity should not be observable via getComputedStyle</title>
<link rel="help" href="https://drafts.csswg.org/css-view-transitions-2/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
body {
margin: 0;
}
div {
position: absolute;
width: 100px;
height: 100px;
background: green;
}

.parent {
view-transition-name: parent;
opacity: 0.4;
}

.child {
view-transition-name: child;
view-transition-group: parent;
top: 50px;
left: 50px;
}
</style>
<body>
<div class="parent">
<div class="child"></div>
</div>
<script>
promise_test(async t => {
const assert_opacity = label => {
assert_equals(getComputedStyle(parent).opacity, "0.4", label);
};
const parent = document.querySelector(".parent");
assert_opacity("before transition");
const transition = document.startViewTransition(() => {
assert_opacity("in update callback");
});
await transition.ready;
assert_opacity("when ready");
await transition.finished;
assert_opacity("when finished");
});
</script>
</body>
Loading

0 comments on commit 8e164c2

Please sign in to comment.