Bug 215826

Summary: REGRESSION (r263506): scale transform transitions won't overshoot
Product: WebKit Reporter: Tim Guan-tin Chien [:timdream] <timdream>
Component: AnimationsAssignee: Antoine Quint <graouts>
Status: RESOLVED FIXED    
Severity: Normal CC: dino, graouts, j, simon.fraser, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: Safari Technology Preview   
Hardware: Unspecified   
OS: Unspecified   
See Also: https://bugs.webkit.org/show_bug.cgi?id=213495
https://bugs.webkit.org/show_bug.cgi?id=215918
Attachments:
Description Flags
scale-test.html
none
Patch simon.fraser: review+

Description Tim Guan-tin Chien [:timdream] 2020-08-25 14:42:22 PDT
Created attachment 407234 [details]
scale-test.html

STR:

See test case, a <div> comes with `transition: 1s transform cubic-bezier(0, 2, 1, 2)` set.

Expected:

The bezier curve should get the div to scale up a bit and does down to the final size during the CSS transition.

Actual:

The transition looks linear and disregard the curve set?

Note:

Firefox and Chrome goes with the expected behavior.

Version:

Release 110 (Safari 14.0, WebKit 15610.1.21.0.2)
macOS 10.15.5 (19F101)
Comment 1 Radar WebKit Bug Importer 2020-08-25 14:42:47 PDT
<rdar://problem/67759310>
Comment 2 Antoine Quint 2020-08-27 06:46:05 PDT
This regressed with r263506, the fix for bug 213495.
Comment 3 Antoine Quint 2020-08-27 08:16:43 PDT
Strange, we are setting the correct animation-wide timing function in GraphicsLayerCA::setupAnimation() and then returning the correct, default linear timing function for the keyframe in GraphicsLayerCA::timingFunctionForAnimationValue().
Comment 4 Antoine Quint 2020-08-27 08:21:39 PDT
Applying the timing function to the keyframe instead of the CA animation fixes the issue. This could be a Core Animation bug or an unexpected behavior of the API.
Comment 5 Antoine Quint 2020-08-28 01:47:48 PDT
I confirmed this is an issue with Core Animation: setting the `timingFunction` property on a CAKeyframeAnimation clips the animation when the timing function is outside of the 0-1 range. However, applying the same timing function to the `timingFunctions` (plural) property addresses the issue.

This means that for CSS Animations this is not an issue since CSS Animations never apply an animation-wide timing function.

For CSS Transitions, we can work around this issue since there is a single interval.

However, for JS-originated animations which can have both multiple intervals (more than 2 keyframes) and an animation-wide timing function, we will require a fix to Core Animation.

Applying a workaround to fix CSS Transitions seems well worth it since:

1. this is a regression
2. the reported bug is about those specifically
3. they are more common than JS-originated animations at this stage (which never worked for this behavior)
Comment 6 Antoine Quint 2020-08-28 01:51:33 PDT
My plan is to add a new member to PlatformCAAnimation to return the number of values for an animation, which we'll use to determine whether we have an animation with 2 values or more to determine whether we should use the `timingFunctions` (plural) property rather than the `timingFunction` (singular) property.
Comment 7 Antoine Quint 2020-08-28 03:09:00 PDT
Actually, it'll be even easier to use Animation::property() so that GraphicsLayerCA can tell we're dealing with a CSS Transition.
Comment 8 Antoine Quint 2020-08-28 04:38:13 PDT
Created attachment 407461 [details]
Patch
Comment 9 Simon Fraser (smfr) 2020-08-28 09:26:02 PDT
Comment on attachment 407461 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=407461&action=review

> Source/WebCore/animation/KeyframeEffect.cpp:1680
> +        // If we are dealing with a CSS Transition and using a cubic bezier timing function, we set up
> +        // the Animation object to reflect this which will allow the GraphicsLayerCA code to set the
> +        // timing function on the single keyframe interval rather than on the animation at large,
> +        // working around a Core Animation issue where setting a cubic bezier on a CAKeyframeAnimation's
> +        // timingFunction property can lead to clipped animations should a value be above 1.

I think this comment could be reduced to:
// Do blah to work around a limitation of Core Animation: webkit.org/b/215918

> Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp:3341
> +        // A CSS Transition is the only scenario where Animation::property() will have
> +        // its mode set to SingleProperty. In this case, we don't set the animation-wide
> +        // timing function, instead opting to set it using the timingFunctions (plural)
> +        // property to work around a Core Animation issue where setting a cubic bezier
> +        // on a CAKeyframeAnimation's timingFunction property (singular) can lead to
> +        // clipped animations should a value be above 1.
> +        // FIXME: https://bugs.webkit.org/show_bug.cgi?id=215918

I think this comment could be reduced to:
// Do blah to work around a limitation of Core Animation: webkit.org/b/215918

> Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp:3357
> +            // A CSS Transition is the only scenario where Animation::property() will have
> +            // its mode set to SingleProperty. In this case, we chose not to set set the
> +            // animation-wide timing function, so we set it on the single keyframe interval
> +            // to work around a Core Animation issue where setting a cubic bezier on a
> +            // CAKeyframeAnimation's timingFunction property (singular) can lead to clipped
> +            // animations should a value be above 1.
> +            // FIXME: https://bugs.webkit.org/show_bug.cgi?id=215918

I think this comment could be reduced to:
// Do blah to work around a limitation of Core Animation: webkit.org/b/215918

> LayoutTests/webanimations/accelerated-css-transition-with-easing-y-axis-above-1.html:40
> +    if (!UIHelper.isWebKit2())
> +        await new Promise(requestAnimationFrame);

A bit weird. Can you just await UIHelper.renderingUpdate()?
Comment 10 Antoine Quint 2020-08-28 09:43:18 PDT
(In reply to Simon Fraser (smfr) from comment #9)
> Comment on attachment 407461 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=407461&action=review
> 
> > Source/WebCore/animation/KeyframeEffect.cpp:1680
> > +        // If we are dealing with a CSS Transition and using a cubic bezier timing function, we set up
> > +        // the Animation object to reflect this which will allow the GraphicsLayerCA code to set the
> > +        // timing function on the single keyframe interval rather than on the animation at large,
> > +        // working around a Core Animation issue where setting a cubic bezier on a CAKeyframeAnimation's
> > +        // timingFunction property can lead to clipped animations should a value be above 1.
> 
> I think this comment could be reduced to:
> // Do blah to work around a limitation of Core Animation: webkit.org/b/215918

I'll try to tone those down to only capture the bits relevant to the immediate code.

> > LayoutTests/webanimations/accelerated-css-transition-with-easing-y-axis-above-1.html:40
> > +    if (!UIHelper.isWebKit2())
> > +        await new Promise(requestAnimationFrame);
> 
> A bit weird. Can you just await UIHelper.renderingUpdate()?

Will do.
Comment 11 Antoine Quint 2020-08-28 09:48:09 PDT
Committed r266280: <https://trac.webkit.org/changeset/266280>
Comment 12 Antoine Quint 2020-09-03 02:18:36 PDT
*** Bug 216107 has been marked as a duplicate of this bug. ***