Bug 276121 - When animating perspective-origin where one of the values is a custom property (var()), the animation is not applied
Summary: When animating perspective-origin where one of the values is a custom propert...
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: CSS (show other bugs)
Version: Safari 17
Hardware: All Unspecified
: P2 Normal
Assignee: Nobody
URL:
Keywords: BrowserCompat, InRadar
Depends on:
Blocks:
 
Reported: 2024-07-02 04:14 PDT by Tom Bigelajzen
Modified: 2024-07-07 23:43 PDT (History)
6 users (show)

See Also:


Attachments
Demo showing the bug (2.62 KB, text/html)
2024-07-02 04:18 PDT, Tom Bigelajzen
no flags Details
Reduction (439 bytes, text/html)
2024-07-04 07:17 PDT, Antoine Quint
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Tom Bigelajzen 2024-07-02 04:14:49 PDT
When applying a `perspective-origin` to an element, where at least one of its components is a CSS custom property, and animating the composed value with the custom property in the keyframes the animations does not apply.

This happens where all keyframes are custom properties, and with mixed. Found one exception where the start is custom and the end is not or vice versa, but it might have some logical explanation.

This fails:
```
@keyframes perspective-var {
  from {
    perspective-origin: 50% var(--perspective-y-start);
  }
  to {
    perspective-origin: 50% var(--perspective-y-end);
  }
}
```

This also fails:
```
@keyframes perspective-mixed {
  0% {
    perspective-origin: 50% var(--perspective-y-start);
  }
  50% {
    perspective-origin: 50% 50%;
  }
  100% {
    perspective-origin: 50% var(--perspective-y-end);
  }
}
```

But This works:
```
@keyframes perspective-mixed {
  from {
    perspective-origin: 50% 0;
  }
  to {
    perspective-origin: 50% var(--perspective-y-end);
  }
}
```

Fails on Safari Desktop and iOS
Works on Chrome and Firefox
On Firefox animating perspective-origin does not work as expected without will-change: transform, with it it works like in Chrome.
Comment 1 Tom Bigelajzen 2024-07-02 04:18:13 PDT
Created attachment 471788 [details]
Demo showing the bug
Comment 2 Yehonatan Daniv 2024-07-02 09:00:02 PDT
It seems that the custom property doesn't get resolved correctly inside the @keyframes rule and the values keeps the initial 50% value.

If we change the x values inline the animation still plays on the x axis, but the y axis is still failing in the same way - every custom property stays as initial.
Comment 3 Antoine Quint 2024-07-04 00:36:36 PDT
The `perspective-origin` is implemented as a shorthand for the `perspective-origin-x` and `perspective-origin-y` properties. While this is not standard, this is how things stand now.

Interestingly, this does not happen when the longhand `perspective-origin-y` is animated. This could be a problem with shorthands in general, or specifically with this one.
Comment 4 Antoine Quint 2024-07-04 07:12:11 PDT
This is purely a CSS issue, not related to animations per se. This is due to this code in `consumePositionCoordinates()`:

    auto value2 = consumePositionComponent(range, parserMode, unitless, negativePercentagePolicy);
    if (!value2)
        return positionFromOneValue(*value1);

So if we fail to parse a second value for `perspective-origin`, we treat the value as a single value property instead of determining we failed to parse that second value because it's a variable.

If we returned `nullptr` here instead, the caller `CSSPropertyParser::consumePerspectiveOrigin()` would return `false` which would propagate that return value up to `CSSPropertyParser::parseValueStart()` which would do the right thing per this comment:

        // Variable references will fail to parse here and will fall out to the variable ref parser below.

I'll note that `consumePositionCoordinates()` has this FIXME, although I'm not sure whether it is relevant:

// FIXME: This may consume from the range upon failure. The background
// shorthand works around it, but we should just fix it here.
Comment 5 Antoine Quint 2024-07-04 07:13:14 PDT
Putting the `var()` as the first value processes things correctly since `consumePositionCoordinates()` return `nullptr` in that case, evidence that if we returned `nullptr` when the second value failed to parse we'd have the expected behavior.
Comment 6 Antoine Quint 2024-07-04 07:17:42 PDT
Created attachment 471814 [details]
Reduction
Comment 7 Radar WebKit Bug Importer 2024-07-07 23:43:28 PDT
<rdar://problem/131288246>