Bug 228896 - CSS animation timing is broken when animating from scaleX(0) to scale(1)
Summary: CSS animation timing is broken when animating from scaleX(0) to scale(1)
Status: RESOLVED CONFIGURATION CHANGED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Animations (show other bugs)
Version: Safari 14
Hardware: Mac (Intel) macOS 11
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2021-08-07 06:33 PDT by evan.exe
Modified: 2023-05-11 08:01 PDT (History)
6 users (show)

See Also:


Attachments
A screen capture showing Chrome working correctly and Safari working incorrectly (2.36 MB, video/quicktime)
2021-08-07 06:33 PDT, evan.exe
no flags Details
HTML snapshot of the current state of https://esbuild.github.io/ (20.48 KB, text/html)
2021-08-07 06:45 PDT, evan.exe
no flags Details
Testcase (1.88 KB, text/html)
2021-08-09 20:43 PDT, Simon Fraser (smfr)
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description evan.exe 2021-08-07 06:33:08 PDT
Created attachment 435129 [details]
A screen capture showing Chrome working correctly and Safari working incorrectly

Repro:

1. Visit https://esbuild.github.io/ on Chrome or Firefox
2. Notice how the progress bars move together

3. Visit https://esbuild.github.io/ on Safari
4. Notice how the progress bars move separately at different speeds

The animation is a simple linear CSS animation from left to right, and is completely broken in Safari.
Comment 1 evan.exe 2021-08-07 06:35:29 PDT
Looks like this is also broken on the latest version of iOS (version 14.7.1).
Comment 2 evan.exe 2021-08-07 06:45:05 PDT
Created attachment 435130 [details]
HTML snapshot of the current state of https://esbuild.github.io/
Comment 3 Simon Fraser (smfr) 2021-08-09 20:43:23 PDT
Created attachment 435240 [details]
Testcase
Comment 4 Simon Fraser (smfr) 2021-08-09 20:44:08 PDT
The broken animation has mismatched transform functions:

        @keyframes scale-bar {
            0% { transform: scaleX(0) }
            to { transform: scale(1) }
        }

so it interpolates via matrix interpolation. This appears to use the wrong timing function.
Comment 5 Simon Fraser (smfr) 2021-08-09 20:51:54 PDT
scaleX(0) is not invertible so we fall back to software animation.
Comment 6 Simon Fraser (smfr) 2021-08-09 21:12:22 PDT
This is about interpolation via matrix decomposition.

We decompose the scaleX(0) matrix to:
(WebCore::TransformationMatrix::Decomposed2Type) $1 = (scaleX = 0, scaleY = 1, translateX = 0, translateY = 0, angle = 0, m11 = 0, m12 = 0, m21 = 0, m22 = 1)

and the scale(1) matrix to:
(WebCore::TransformationMatrix::Decomposed2Type) $2 = (scaleX = 1, scaleY = 1, translateX = 0, translateY = 0, angle = 0, m11 = 1, m12 = 0, m21 = 0, m22 = 1)

then interpolate scaleX and m11. Recomposing the matrix then causes m11 to affect scaleX in matrix[0][0]
Comment 7 Radar WebKit Bug Importer 2021-08-12 10:08:57 PDT
<rdar://problem/81854922>
Comment 8 evan.exe 2021-08-12 16:03:45 PDT
Thanks for determining the root cause. In that case I'm going to change https://esbuild.github.io/ to use a small but non-zero scale value to avoid a non-invertible matrix. This means the original repro steps will no longer work, but you will still be able to reproduce this with the attached test case.
Comment 9 Simon Fraser (smfr) 2021-08-12 17:05:55 PDT
A small scale would work. You could also use scale(x, y) or scaleX(x) in both keyframes.
Comment 10 evan.exe 2021-08-12 17:20:06 PDT
> A small scale would work. You could also use scale(x, y) or scaleX(x) in both keyframes.

Yes, but the problem is that CSS minifiers convert "scaleX(1)" into "scale(1)" and "scale(0, 1)" into "scaleX(0)", which prevents using both "scale" or both "scaleX" in this case if you're using a CSS minifier. See this code for example: https://github.com/cssnano/cssnano/blob/04bd16e9de5e2d4409fef74034edb7534cd9e457/packages/postcss-reduce-transforms/src/index.js#L93-L123.

I believe that this CSS minification transformation is valid since these I'd expect for these forms to generate the equivalent matrix (i.e. "scaleX(0) == scale(0, 1)" and "scaleX(1) == scale(1, 1)"). Please let me know if that's not a valid CSS minification transformation and I can file an issue with the relevant CSS minifiers.
Comment 11 Antoine Quint 2023-05-11 08:01:50 PDT
This is working fine in Safari 16.