Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 66 additions & 34 deletions css-transforms-1/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,45 +1092,76 @@ The scaling causes a non-invertible CTM for the coordinate space of the div box.
Interpolation of Transforms {#interpolation-of-transforms}
==========================================================

When animating or transitioning transforms, the transform function lists must be interpolated. For interpolation between one transform <em>from-transform</em> and a second transforms <em>to-transform</em>, the rules described below are applied.
[=Interpolation=] of transform function lists is performed as follows:

<ul>
<li id="none-none-animation">
If both the <em>from-</em> and <em>to-transform</em> are ''transform/none'':
* There is no interpolation necessary. The computed value stays ''transform/none''.

<li id="none-transform-animation">
If one of the <em>from-</em> or <em>to-transforms</em> is ''transform/none'':
* The value ''transform/none'' is replaced by an equivalent [=identity transform function=] list for the corresponding transform function list. Both transform function lists get interpolated following the next rule.

<div class="example">
For example, if <em>from-transform</em> is ''scale(2)'' and <em>to-transform</em> is ''transform/none'' then the value ''scale(1)'' will be used for <em>to-transform</em> and animation will proceed using the next rule. Similarly, if <em>from-transform</em> is ''transform/none'' and <em>to-transform</em> is ''scale(2) rotate(50deg)'' then the animation will execute as if <em>from-transform</em> is ''scale(1) rotate(0)''.

<li id=none-none-interpolation>
If both <var>V<sub>a</sub></var> and <var>V<sub>b</sub></var>
are ''transform/none'':
* <var>V<sub>result</sub></var> is ''transform/none''.

<li id=transform-interpolation-length-fixup>
Treating ''transform/none'' as a list of zero length,
if <var>V<sub>a</sub></var> or <var>V<sub>b</sub></var> differ in length:
* extend the shorter list to the length of the longer list,
setting the function at each additional position
to the [=identity transform function=] matching
the function at the corresponding position in the longer list.
Both transform function lists are then interpolated
following the next rule.

<div class=example>
For example, if <var>V<sub>a</sub></var> is ''scale(2)''
and <var>V<sub>b</sub></var> is ''transform/none''
then the value ''scale(1)'' will be used for <var>V<sub>b</sub></var>
and interpolation will proceed using the next rule.
Similarly, if <var>V<sub>a</sub></var> is ''scale(1)''
and <var>V<sub>b</sub></var> is ''scale(2) rotate(50deg)''
then the interpolation will be performed as if
<var>V<sub>a</sub></var> were ''scale(1) rotate(0)''.
</div>

<li id="transform-transform-animation">
If <em>from-</em> and <em>to-transform</em> have the same number of transform functions, each transform function pair has either the same name, or is a derivative of the same <a href="#transform-primitives">primitive</a>:
* Interpolate each transform function pair as described in <a href="#interpolation-of-transform-functions">Interpolation of transform functions</a>. The computed value is the resulting transform function list.

<div class="example">
For example, if <em>from-transform</em> is ''scale(1) translate(0)'' and <em>to-transform</em> is ''translate(100px) scale(2)'' then ''scale(1)'' and ''translate(100px)'' as well as ''translate(0)'' and ''scale(2)'' don't share a common primitive and therefore can not get interpolated following this rule.

<li>
Let <var>V<sub>result</sub></var> be an empty list.
Beginning at the start of
<var>V<sub>a</sub></var> and <var>V<sub>b</sub></var>,
compare the corresponding functions at each position:
* While the functions have either the same name,
or are derivatives of the same
[[#transform-primitives|primitive transform function]],
interpolate the corresponding pair of functions as described in
[[#interpolation-of-transform-functions]]
and append the result to <var>V<sub>result</sub></var>.
* If the pair do not have a common name
or [[#transform-primitives|primitive transform function]],
post-multiply the remaining transform functions in each of
<var>V<sub>a</sub></var> and <var>V<sub>b</sub></var> respectively
to produce two 4x4 matrices.
[=Interpolate=] these two matrices as described in
[[#matrix-interpolation]],
append the result to <var>V<sub>result</sub></var>,
and cease iterating over
<var>V<sub>a</sub></var> and <var>V<sub>b</sub></var>.

<div class=example>
For example,
if <var>V<sub>a</sub></var> is ''rotate(0deg) scale(1) translate(20px)''
and <var>V<sub>b</sub></var> is ''rotate(270deg) translate(10px) scale(2)'',
the ''rotate(0deg)'' and ''rotate(360deg)'' functions will be interpolated
according to [[#interpolation-of-transform-functions]]
while the remainder of each list--
''scale(1) translate(20px)'' and ''translate(10px) scale(2)''--
will first be converted to equivalent 4x4 matrices
and then interpolated as described in [[#matrix-interpolation]].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example, if I'm thinking correctly, gives the same result whether you use whole-property matrix interpolation or this smarter interpolation. I recommend providing an example that showcases the differences between the two; an obvious example would be having the common prefix be a rotate(0deg) and rotate(360deg), which is hugely different.

Then you can also have a small note in this that the spec previously used whole-property matrix interpolation, which would have a different effect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good idea. I have no idea why I chose the values I did.


A previous version of this specification
did not attempt to interpolate matching pairs of transform functions
unless all functions in the list matched.
As a result, the two lists in this example would be interpolated
using matrix interpolation only
and the ''rotate(360deg)'' component of the second list would be lost.
</div>

<li id="transform-transform-neutral-extend-animation">
If <em>from-</em> and <em>to-transform</em> have a different number of transform functions, and the functions of the shorter transform list have either the same name, or are derivatives of the same <a href="#transform-primitives">primitive</a> of the function at the equivalent position in the longer list:
* Extend the shorter list to the length of the longer list, setting the function at each additional position to the [=identity transform function=] matching the function at the equivalent position in the longer list.

* Interpolate each transform function pair as described in <a href="#interpolation-of-transform-functions">Interpolation of transform functions</a>. The computed value is the resulting transform function list.

<div class="example">
For example, if <em>from-transform</em> is ''scale(1.5)'' and <em>to-transform</em> is ''scale(1.5) rotate(720deg)'', it interpolates as if the <em>from-transform</em> were specified as ''scale(1.5) rotate(0deg)''.
</div>

<li id="other-animation">
In all other cases:
* The transform functions of each transform function list on the <em>from-</em> and <em>to-transform</em> get <a>post-multiplied</a> and converted into 4x4 matrices. Each of the matrices gets interpolated following the instructions in <a href="#matrix-interpolation">Interpolation of matrices</a>. The computed value is the transform function <<matrix()>>.

</ul>

In some cases, an animation might cause a transformation matrix to be singular or non-invertible. For example, an animation in which scale moves from 1 to -1. At the time when the matrix is in such a state, the transformed element is not rendered.
Expand Down Expand Up @@ -1502,7 +1533,8 @@ The following changes were made since the <a href="https://www.w3.org/TR/2017/WD
* Clarify behavior of 'transform' on overflow area.
* Remove ''translateX(0)'', ''translateY(0)'', ''scaleX(0)'', ''scaleY(0)'' from the list of neutral elements.
* Remove any reference of 3D transformations of transform function definitions.
* Allow interpolation between some <<transform-list>>s with different length.
* Specify interpolation between <<transform-list>>s to match lengths and
avoid matrix interpolation for the common prefix of the two lists.
* No 'transform' on non-replaced inline boxes, table-column boxes, and table-column-group boxes.
* Define target coordinate space for transformations on <{pattern}>, <{linearGradient}>, <{radialGradient}> and <{clipPath}> elements.
* Remove 3-value <<rotate()>> from transform function primitives.
Expand Down