Skip to content

Commit 02eb632

Browse files
noamrkhushalsagar
andauthored
[css-view-transitions-2] First pass at layered capture (w3c#11045)
* [css-view-transitions-2] First pass at layered capture Describe how layered capture works, which CSS properties it targets and how it affects rendering. Based on [this resolution](w3c#10585 (comment)). This is a first pass, and still has open issues (will open separately): 1. Should there be a CSS property to decide on this? What should be the default? 2. How do we capture overflow/box-sizing? 3. (More capture questions if they come up) Closes w3c#10585 * Adjust border * Adjust nested transform by border * Add note about border-box * Update css-view-transitions-2/Overview.bs Co-authored-by: Khushal Sagar <63884798+khushalsagar@users.noreply.github.com> * Update css-view-transitions-2/Overview.bs Co-authored-by: Khushal Sagar <63884798+khushalsagar@users.noreply.github.com> * Refactor a few things * note * Overhaul nested geometry * Fold padding back into regular props * Apply suggestions from code review Co-authored-by: Khushal Sagar <63884798+khushalsagar@users.noreply.github.com> * Update Overview.bs * Apply box-sizing in the right place --------- Co-authored-by: Khushal Sagar <63884798+khushalsagar@users.noreply.github.com>
1 parent ed7566f commit 02eb632

File tree

1 file changed

+186
-10
lines changed

1 file changed

+186
-10
lines changed

css-view-transitions-2/Overview.bs

+186-10
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ spec:css-view-transitions-1;
3434
text: snapshot containing block; type: dfn;
3535
text: old transform; for: captured element; type: dfn;
3636
text: new element; for: captured element; type: dfn;
37+
text: old width; for: captured element; type: dfn;
38+
text: old height; for: captured element; type: dfn;
3739
text: updateCallbackDone; type: property; for: ViewTransition;
3840
text: phase; type: dfn; for: ViewTransition;
3941
text: call the update callback; type: dfn;
@@ -46,6 +48,8 @@ spec:css-view-transitions-1;
4648
text: group styles rule; type: dfn;
4749
text: update pseudo-element styles rule; type: dfn;
4850
text: document-scoped view transition name; type: dfn;
51+
text: capture rendering characteristics; type: dfn;
52+
text: capturing the image; type: dfn;
4953
spec:dom; type:dfn; text:document
5054
spec:css22; type:dfn; text:element
5155
spec:selectors-4; type:dfn;
@@ -63,7 +67,23 @@ spec:geometry-1
6367
text:multiply; type:dfn;
6468
text:matrix; type:dfn;
6569
spec:infra; type:dfn; text:list
66-
spec:css-borders-4; type: property; text:border-radius;
70+
spec:css-borders-4;
71+
type: property; text:border-color;
72+
type: property; text:border-radius;
73+
type: property; text:box-shadow;
74+
type: property; text:border-top;
75+
type: property; text:border-right;
76+
type: property; text:border-bottom;
77+
type: property; text:border-left;
78+
type: property; text:border-left-width;
79+
type: property; text:border-top-width;
80+
spec:css-sizing-3;
81+
type: value; text:box-sizing;
82+
type: value; text:border-box;
83+
type: value; text:content-box;
84+
spec:css-box-4;
85+
type: dfn; text:padding box; for: /;
86+
type: dfn; text:content box; for: /;
6787
</pre>
6888

6989
<style>
@@ -885,6 +905,48 @@ and by applying ''view-transition-group'' to the internal element referencing th
885905
The [=used value=] for 'view-transition-group' resolves to a 'view-transition-name' in its ancestor chain, or to ''view-transition-name/none''. When generating the [=named view transition pseudo-element=], the ''::view-transition-group()'' with that name
886906
would be the parent of the ''::view-transition-group()'' generated for this element's 'view-transition-name'.
887907

908+
# Layered capture # {#layered-capture}
909+
910+
## Overview ## {#layered-capture-overview}
911+
912+
*This section is non-normative.*
913+
914+
In [[css-view-transitions-1]], the old and new states are captured as snapshots, and the initial and final geometry are captured,
915+
creating a crossfade animation by default. This is a simple model and mostly creates the desired outcome.
916+
917+
However, crossfading two flattened snapshots is not always the most expressive animation. CSS allows animating borders, gradient backgrounds, 'filter', 'box-shadow' etc.
918+
which can feel more expressive and natural than crossfade depending on the desired UX.
919+
920+
In addition, when using <a href="#nested-view-transitions">nested view transitions</a>, some of the animations could look "wrong" when the CSS properties are flattened to a snapshot.
921+
This includes tree effects, such as 'opacity', 'mask', 'clip-path' and 'filter', and also clipping using 'overflow'. These effects are designed to apply to the whole tree of elements, not just to one element and its content.
922+
923+
With layered capture, all the CSS properties that affect the entire tree, as well as box decorations, are captured as style and animate as part of the group.
924+
925+
Issue: should this behavior be an opt-in/opt-out with a CSS property? See <a href="https://github.com/w3c/csswg-drafts/issues/11078">issue 11078</a>.
926+
927+
928+
## Table of captured CSS properties {#layered-captured-css-properties}
929+
930+
The following list of <dfn>layered capture properties</dfn> defines the CSS properties that participate in layered capture.
931+
When the old or new state of the element are captured, these properties are captured as style, and the user agent must render the snapshot
932+
with disregard to that property:
933+
934+
- 'background'
935+
- 'border-left'
936+
- 'border-top'
937+
- 'border-bottom'
938+
- 'border-right'
939+
- 'border-radius'
940+
- 'border-image'
941+
- 'box-shadow'
942+
- 'box-sizing'
943+
- 'clip-path'
944+
- 'filter'
945+
- 'mask'
946+
- 'opacity'
947+
- 'outline'
948+
- 'padding'
949+
888950
# Algorithms # {#algorithms}
889951

890952
## Data structures ## {#cross-doc-data-structures}
@@ -939,15 +1001,32 @@ It has the following [=struct/items=]:
9391001

9401002
### Captured elements extension ### {#capture-classes-data-structure}
9411003
The [=captured element=] struct should contain these fields, in addition to the existing ones:
942-
<dl>
943-
: <dfn for="captured element">class list</dfn>
1004+
<dl dfn-for="captured element">
1005+
: <dfn>class list</dfn>
9441006
:: a [=/list=] of strings, initially empty.
9451007

946-
: <dfn for="captured element">containing group name</dfn>
1008+
: <dfn>containing group name</dfn>
9471009
:: Null or a string, initially null.
9481010

949-
: <dfn for="captured element">transform from snapshot containing block</dfn>
950-
:: A [=matrix=], initially the identity matrix.
1011+
: <dfn>old layered-capture style</dfn>
1012+
:: A string.
1013+
1014+
: <dfn>transform from snapshot containing block</dfn>
1015+
:: A [=matrix=], initially the [=identity transform function=].
1016+
1017+
: <dfn>old box properties
1018+
: <dfn>new box properties
1019+
:: A [=layered box properties=] or null, initially null.
1020+
</dl>
1021+
1022+
The <dfn>layered box properties</dfn> is a struct, containing the following fields:
1023+
<dl dfn-for="layered box properties">
1024+
: <dfn>box sizing</dfn>
1025+
:: ''box-sizing/border-box'' or ''box-sizing/content-box''.
1026+
1027+
: <dfn>content box</dfn>
1028+
: <dfn>padding box</dfn>
1029+
:: A rectangle, in CSS pixel units, relative to the border box.
9511030
</dl>
9521031

9531032
## Resolving the ''@view-transition'' rule ## {#vt-rule-algo}
@@ -1239,25 +1318,122 @@ When [[css-view-transitions-1#setup-transition-pseudo-elements-algorithm|setting
12391318

12401319
1. Append |group| to |parentGroup|.
12411320

1242-
1. When setting the animation keyframes given |transform|, [=multiply=] |transform| by the inverse matrix of |groupContainerElement|'s [=old transform=].
1321+
1. When setting the animation keyframes given |transform|, [=adjust the nested group transform=] to |transform|, given |groupContainerElement|'s [=old transform=], |groupContainerElement|'s [=captured element/old width=], |groupContainerElement|'s [=old height=], and |groupContainerElement|'s [=old box properties=].
12431322

12441323
Note: It is TBD how this is resolved when the old and new groups mismatch. See <a href="https://github.com/w3c/csswg-drafts/issues/10631">Issue 10631</a>.
12451324
</div>
12461325

12471326
### Adjust the group's 'transform' to be relative to its containing ''::view-transition-group()'' ### {#vt-group-transform-adjust}
12481327
<div algorithm="additional pseudo-element style update steps (group)">
1249-
When [[css-view-transitions-1#style-transition-pseudo-elements-algorithm|updating the style of the transition pseudo-element]], perform the following steps before setting the [=captured element/group styles rule=], given a [=captured element=] |capturedElement|, given a |transform| and a {{ViewTransition}} |transition|:
1328+
When [[css-view-transitions-1#style-transition-pseudo-elements-algorithm|updating the style of the transition pseudo-element]], perform the following steps before setting the [=captured element/group styles rule=], given a [=captured element=] |capturedElement|, a |transform|, and a {{ViewTransition}} |transition|:
12501329

12511330
1. Set |capturedElement|'s [=transform from snapshot containing block=] to |transform|.
12521331

12531332
1. If |capturedElement|'s [=containing group name=] is not null, then:
12541333

1255-
1. Let |groupContainerElement| be |transition|'s [=ViewTransition/named elements=][|capturedElement|'s [=containing group name=]].
1334+
1. Let |groupContainerElement| be |transition|'s [=ViewTransition/named elements=][|capturedElement|'s [=containing group name=].
1335+
1336+
1. Let |containerRect| be [=snapshot containing block=] if |capturedElement| is the [=document element=],
1337+
otherwise, |capturedElement|'s [=border box=].
1338+
1339+
1. [=Adjust the nested group transform=] to |transform|, given |groupContainerElement|'s [=transform from snapshot containing block=], |containerRect|'s width, |containerRect|'s height, and |groupContainerElement|'s [=new box properties=].</div>
1340+
1341+
## Capturing layered CSS properties ## {#layered-capture-algorithms}
1342+
1343+
### Compute the layered-capture style ### {#layered-capture-compute-style}
12561344

1257-
1. [=Multiply=] |transform| by the inverse matrix of |groupContainerElement|'s [=transform from snapshot containing block=].
1345+
<div algorithm="compute layered capture style">
1346+
To compute the <dfn>layered capture style</dfn> of an {{Element}} |element|:
1347+
1348+
1. Let |propertiesToCapture| be a new [=/list=] corresponding to the [=layered capture properties=],
1349+
1. Issue: Specify the behavior of 'overflow' and containment. See <a href="https://github.com/w3c/csswg-drafts/issues/11079">issue 11079</a>.
1350+
1. Let |styles| be a « ».
1351+
1. For each |property| in |propertiesToCapture|, [=list/append=] the [=string/concatenate|concatentation=] of « |property|, `":"`, |element|'s [=computed value=] of |property|, `";"` » to |styles|.
1352+
1. Return the [=string/concatenate|concatentation=] of |styles|.
1353+
</div>
1354+
1355+
<div algorithm="compute layered capture geometry">
1356+
To compute the <dfn>layered capture geometry</dfn> of an {{Element}} |element|, return a new [=layered box properties=],
1357+
whose [=layered box properties/box sizing=] is |element|'s [=computed value|computed=] 'box-sizing',
1358+
[=layered box properties/padding box=] is |element|'s [=/padding box=],
1359+
and whose [=layered box properties/content box=] is |element|'s [=/content box=].
12581360
</div>
12591361

1362+
To compute the <dfn>default group size</dfn> given |element|
1363+
return |element|'s [=/content box=]'s size if |element|'s [=computed value|computed=] 'box-sizing' is ''box-sizing/content-box'', otherwise |element|'s [=/border box=]'s size.
1364+
1365+
### Capture the old layered properties ### {#capture-new-layered-props-algorithm}
1366+
<div algorithm="additional old capture steps (layered)">
1367+
When [[css-view-transitions-1#capture-new-state-algorithm|capturing the old state for an element]], perform the following steps given a [=captured element=] |capturedElement| and an [=element=] |element|:
1368+
1369+
1. Set |capturedElement|'s [=old layered-capture style=] to |element|'s [=layered capture style=].
1370+
1. Set |capturedElement|'s [=old box properties=] to |element|'s [=layered capture geometry=].
1371+
1. Set |capturedElement|'s ([=old width=], [=old height=] to |element|'s [=default group size=].
1372+
</div>
1373+
1374+
### Adjustment to image capture size ### {#capture-image-size-algorithm}
1375+
<div algorithm="adjust image capture size (layered)">
1376+
When [=capturing the image=], the [=natural dimensions=] of the image will be based on the element's [=/content box=]'s size rather than on the [=border box=].
1377+
</div>
1378+
1379+
### Render the snapshot with layered capture ### {#layered-capture-rendering}
1380+
1381+
When capturing an element into a snapshot, only the [=element contents=] are painted, without the element's effects and box decorations.
1382+
Specifically, the element's 'background', 'border', 'border-image', 'box-shadow', and 'outline' are not painted, and its 'border-radius', 'clip-path', 'filter', 'mask', and 'opacity' are not applied.
1383+
1384+
### Adjust the nested group transform ### {#vt-adjust-nested-group-transform}
1385+
To <dfn>adjust the nested group transform</dfn> given a [=matrix=] |transform|, a [=matrix=] |parentTransform|, |borderBoxWidth|, |borderBoxHeight|, and a [=layered box properties=] |boxProperties|, perform the following steps:
1386+
1. [=Multiply=] |transform| with the inverse matrix of |parentTransform|.
1387+
1. Let (left, top) be the border edge based on |boxProperties|'s [=layered box properties/padding box=] inside (|borderBoxWidth|, |borderBoxHeight|).
1388+
1. Translate |transform| by (-left, -top).
12601389

1390+
Note: These operations ensure that the default position of this nested group would be equivalent to the original element's position, given that by default a nested ':view-transition-group' would be positioned relative to the parent's [=padding edge=].
1391+
1392+
### Apply the old layered-capture properties to ''::view-transition-group()'' keyframes ### {#vt-layered-capture-apply-keyframes}
1393+
<div algorithm="additional pseudo-element setup steps (layered capture keyframes)">
1394+
When [[css-view-transitions-1#setup-transition-pseudo-elements-algorithm|setting up the transition pseudo-element]] for a [=captured element=] |capturedElement|, given a |transitionName|:
1395+
1. Let |keyframesName| be the [=string/concatenate|concatentation=] of « `-ua-view-transition-group-anim-`, |transitionName| »
1396+
1. Append |capturedElement|'s [=old layered-capture style=] to the constructed rule for |keyframesName|.
1397+
</div>
1398+
1399+
### Apply the new layered-capture properties to ''::view-transition-group()'' ### {#vt-layered-capture-apply-new-state}
1400+
<div algorithm="additional pseudo-element style update steps (layered capture new state)">
1401+
When [[css-view-transitions-1#style-transition-pseudo-elements-algorithm|updating the style of the transition pseudo-element]], perform the following steps before setting the [=captured element/group styles rule=], given a |transitionName|, a |capturedElement|, |width|, |height|, and an {{Element}} |element|:
1402+
1403+
Note: this might change and |width|, |height|.
1404+
1405+
1. Let |style| be |element|'s [=layered capture style=].
1406+
1. Set |capturedElement|'s [=new box properties=] to |element|'s [=layered capture properties=].
1407+
1. Set (|width|, |height|) to the |element|'s [=default group size=].
1408+
1. Append the [=string/concatenate|concatentation=] of « `"::view-transition-group("`, |transitionName|, `") {"`, |style| , `"}"` » to the constructed user-agent stylesheet.
1409+
1. Let (|oldContentWidth|, |oldContentHeight|) be (|capturedElement|'s [=captured element/old width=], |capturedElement|'s [=captured element/old height=]) if |capturedElement|'s [=old box properties=] is null, otherwise |capturedElement|'s [=old box properties=]'s [=layered box properties/content box=]'s size.
1410+
1. Let (|newContentWidth|, |newContentHeight|) be (|width|, |height|) if [=new box properties=] is null, otherwise |capturedElement|'s [=new box properties=]'s [=layered box properties/content box=]'s size.
1411+
1. Append the next string (with replaced variables) to the user agent stylesheet:
1412+
1413+
<pre highlight="css">
1414+
@keyframes -ua-view-transition-content-geometry-<var>transitionName</var> {
1415+
from {
1416+
width: <var>oldContentWidth</var>;
1417+
height: <var>oldContentHeight</var>;
1418+
}
1419+
}
1420+
1421+
:root::view-transition-image-pair(<var>transitionName</var>) {
1422+
position: relative;
1423+
inset: unset;
1424+
width: <var>newContentWidth</var>;
1425+
height: <var>newContentHeight</var>;
1426+
animation-name: -ua-view-transition-<var>transitionName</var>;
1427+
animation-direction: inherit;
1428+
animation-timing-function: inherit;
1429+
animation-iteration-count: inherit;
1430+
animation-duration: inherit;
1431+
}
1432+
</pre>
1433+
1434+
Note: the ':view-transition-image-pair' pseudo-element is using ''position/relative'' positioning so that the group's 'padding' will take effect.
1435+
1436+
</div>
12611437

12621438
<h2 id="priv" class="no-num">Privacy Considerations</h2>
12631439

0 commit comments

Comments
 (0)