Skip to content
Permalink
Browse files
[web-animations] add basic interpolation support for <length> custom …
…properties

https://bugs.webkit.org/show_bug.cgi?id=249312

Reviewed by Antti Koivisto.

Add very basic interpolation support for custom properties starting with only with
the <length> type and only dealing with non-additive and non-accumulative animations
with explicit keyframe values.

We add a new WPT test for <length> that draws the blueprint for the remaining work.

* LayoutTests/imported/w3c/web-platform-tests/css/css-animations/animation-important-001-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/animation/custom-property-animation-length-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/animation/custom-property-animation-length.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/font-size-animation-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/registered-property-revert-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/resources/utils.js:
* Source/WebCore/animation/CSSPropertyAnimation.cpp:
(WebCore::blendSyntaxValues):
(WebCore::blendedCSSCustomPropertyValue):
(WebCore::CSSPropertyAnimation::blendCustomProperty):
* Source/WebCore/animation/CSSPropertyAnimation.h:
* Source/WebCore/animation/KeyframeEffect.cpp:
(WebCore::KeyframeEffect::setAnimatedPropertiesInStyle):

Canonical link: https://commits.webkit.org/257906@main
  • Loading branch information
graouts committed Dec 15, 2022
1 parent 2639c5d commit bcd8cc0c0c83b0f2ddb78977a843650168bb138f
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 13 deletions.
@@ -5,5 +5,5 @@ FAIL Non-overriden interpolations are observable assert_equals: expected "rgb(0,
FAIL Important rules override animations (::before) assert_equals: expected "rgb(0, 128, 0)" but got "rgb(15, 15, 15)"
PASS Important rules do not override animations on :visited as seen from JS
FAIL Standard property animations appearing via setKeyframes do not override important declarations assert_equals: expected "rgb(255, 255, 255)" but got "rgb(15, 15, 15)"
FAIL Custom property animations appearing via setKeyframes do not override important declarations assert_equals: expected "150px" but got "200px"
FAIL Custom property animations appearing via setKeyframes do not override important declarations assert_equals: expected "10px" but got "150px"

@@ -0,0 +1,7 @@

PASS Animating a custom property of type <length>
FAIL Animating a custom property of type <length> with a single keyframe assert_equals: expected "150px" but got "100px"
FAIL Animating a custom property of type <length> with additivity assert_equals: expected "350px" but got "250px"
FAIL Animating a custom property of type <length> with a single keyframe and additivity assert_equals: expected "250px" but got "100px"
FAIL Animating a custom property of type <length> with iterationComposite assert_equals: expected "250px" but got "50px"

@@ -0,0 +1,57 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/utils.js"></script>
<div id="target"></div>
<script>

animation_test({
syntax: "<length>",
inherits: false,
initialValue: "0px"
}, {
keyframes: ["100px", "200px"],
expected: "150px"
}, 'Animating a custom property of type <length>');

animation_test({
syntax: "<length>",
inherits: false,
initialValue: "100px"
}, {
keyframes: "200px",
expected: "150px"
}, 'Animating a custom property of type <length> with a single keyframe');

animation_test({
syntax: "<length>",
inherits: false,
initialValue: "100px"
}, {
composite: "add",
keyframes: ["200px", "300px"],
expected: "350px"
}, 'Animating a custom property of type <length> with additivity');

animation_test({
syntax: "<length>",
inherits: false,
initialValue: "100px"
}, {
composite: "add",
keyframes: "300px",
expected: "250px"
}, 'Animating a custom property of type <length> with a single keyframe and additivity');

animation_test({
syntax: "<length>",
inherits: false,
initialValue: "100px"
}, {
iterationComposite: "accumulate",
keyframes: ["0px", "100px"],
expected: "250px"
}, 'Animating a custom property of type <length> with iterationComposite');

</script>
@@ -1,3 +1,3 @@

FAIL Animating font-size handled identically for standard and custom properties assert_equals: expected "400px" but got "250px"
PASS Animating font-size handled identically for standard and custom properties

@@ -1,6 +1,6 @@

PASS Inherited registered custom property can be reverted
PASS Non-inherited registered custom property can be reverted
FAIL Non-inherited registered custom property can be reverted in animation assert_equals: expected "50px" but got "100px"
FAIL Inherited registered custom property can be reverted in animation assert_equals: expected "50px" but got "100px"
PASS Non-inherited registered custom property can be reverted in animation
PASS Inherited registered custom property can be reverted in animation

@@ -124,3 +124,26 @@ function test_with_at_property(desc, fn, description) {
function test_with_style_node(text, fn, description) {
test(() => with_style_node(text, fn), description);
}

function animation_test(property, values, description) {
const name = generate_name();
property.name = name;
CSS.registerProperty(property);

test(() => {
const duration = 1000;
const keyframes = {};
keyframes[name] = values.keyframes;

const iterations = 3;
const composite = values.composite || "replace";
const iterationComposite = values.iterationComposite || "replace";
const animation = target.animate(keyframes, { composite, iterationComposite, iterations, duration });
animation.pause();
// We seek to the middle of the third iteration which will allow to test cases where
// iterationComposite is set to something other than "replace".
animation.currentTime = duration * 2.5;

assert_equals(getComputedStyle(target).getPropertyValue(name), values.expected);
}, description);
};
@@ -31,6 +31,7 @@
#include "CSSPropertyAnimation.h"

#include "AnimationUtilities.h"
#include "CSSCustomPropertyValue.h"
#include "CSSPrimitiveValue.h"
#include "CSSPropertyBlendingClient.h"
#include "CSSPropertyNames.h"
@@ -3910,13 +3911,44 @@ void CSSPropertyAnimation::blendProperties(const CSSPropertyBlendingClient* clie
}
}

void CSSPropertyAnimation::blendCustomProperty(const AtomString& customProperty, RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, double progress)
static std::optional<CSSCustomPropertyValue::SyntaxValue> blendSyntaxValues(const CSSCustomPropertyValue::SyntaxValue& from, const CSSCustomPropertyValue::SyntaxValue& to, const CSSPropertyBlendingContext& blendingContext)
{
const auto& source = progress < 0.5 ? from : to;
if (auto nonInheritedValue = source.nonInheritedCustomProperties().get(customProperty))
destination.setNonInheritedCustomPropertyValue(customProperty, CSSCustomPropertyValue::create(*nonInheritedValue));
else if (auto inheritedValue = source.inheritedCustomProperties().get(customProperty))
destination.setInheritedCustomPropertyValue(customProperty, CSSCustomPropertyValue::create(*inheritedValue));
if (std::holds_alternative<Length>(from) && std::holds_alternative<Length>(to))
return blendFunc(std::get<Length>(from), std::get<Length>(to), blendingContext);

return std::nullopt;
}

static Ref<CSSCustomPropertyValue> blendedCSSCustomPropertyValue(const CSSCustomPropertyValue& from, const CSSCustomPropertyValue& to, const CSSPropertyBlendingContext& blendingContext)
{
if (std::holds_alternative<CSSCustomPropertyValue::SyntaxValue>(from.value()) && std::holds_alternative<CSSCustomPropertyValue::SyntaxValue>(to.value())) {
auto& fromSyntaxValue = std::get<CSSCustomPropertyValue::SyntaxValue>(from.value());
auto& toSyntaxValue = std::get<CSSCustomPropertyValue::SyntaxValue>(to.value());
if (auto blendedSyntaxValue = blendSyntaxValues(fromSyntaxValue, toSyntaxValue, blendingContext))
return CSSCustomPropertyValue::createForSyntaxValue(from.name(), WTFMove(*blendedSyntaxValue));
}

// Use a discrete interpolation for all other cases.
return CSSCustomPropertyValue::create(blendingContext.progress < 0.5 ? from : to);
}

void CSSPropertyAnimation::blendCustomProperty(const CSSPropertyBlendingClient& client, const AtomString& customProperty, RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, double progress, CompositeOperation compositeOperation, IterationCompositeOperation iterationCompositeOperation, double currentIteration)
{
CSSPropertyBlendingContext blendingContext { progress, false, compositeOperation, &client, iterationCompositeOperation, currentIteration };

{
auto* fromValue = from.nonInheritedCustomProperties().get(customProperty);
auto* toValue = to.nonInheritedCustomProperties().get(customProperty);
if (fromValue && toValue)
destination.setNonInheritedCustomPropertyValue(customProperty, blendedCSSCustomPropertyValue(*fromValue, *toValue, blendingContext));
}

{
auto* fromValue = from.inheritedCustomProperties().get(customProperty);
auto* toValue = to.inheritedCustomProperties().get(customProperty);
if (fromValue && toValue)
destination.setInheritedCustomPropertyValue(customProperty, blendedCSSCustomPropertyValue(*fromValue, *toValue, blendingContext));
}
}

bool CSSPropertyAnimation::isPropertyAnimatable(CSSPropertyID property)
@@ -50,7 +50,7 @@ class CSSPropertyAnimation {
static int getNumProperties();

static void blendProperties(const CSSPropertyBlendingClient*, CSSPropertyID, RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, double progress, CompositeOperation, IterationCompositeOperation = IterationCompositeOperation::Replace, double currentIteration = 0);
static void blendCustomProperty(const AtomString&, RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, double progress);
static void blendCustomProperty(const CSSPropertyBlendingClient&, const AtomString&, RenderStyle& destination, const RenderStyle& from, const RenderStyle& to, double progress, CompositeOperation, IterationCompositeOperation = IterationCompositeOperation::Replace, double currentIteration = 0);
};

} // namespace WebCore
@@ -1569,7 +1569,7 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub
if (intervalEndpoints.size() == 1) {
WTF::switchOn(property,
[&] (CSSPropertyID propertyId) { CSSPropertyAnimation::blendProperties(this, propertyId, targetStyle, startKeyframeStyle, startKeyframeStyle, 0, CompositeOperation::Replace); },
[&] (AtomString customProperty) { CSSPropertyAnimation::blendCustomProperty(customProperty, targetStyle, startKeyframeStyle, startKeyframeStyle, 0); }
[&] (AtomString customProperty) { CSSPropertyAnimation::blendCustomProperty(*this, customProperty, targetStyle, startKeyframeStyle, startKeyframeStyle, 0, CompositeOperation::Replace); }
);
return;
}
@@ -1601,7 +1601,7 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub
currentIteration = usedBlendingForAccumulativeIteration ? 0 : currentIteration;
CSSPropertyAnimation::blendProperties(this, propertyId, targetStyle, startKeyframeStyle, endKeyframeStyle, transformedDistance, CompositeOperation::Replace, iterationCompositeOperation, currentIteration);
},
[&] (AtomString customProperty) { CSSPropertyAnimation::blendCustomProperty(customProperty, targetStyle, startKeyframeStyle, endKeyframeStyle, transformedDistance); }
[&] (AtomString customProperty) { CSSPropertyAnimation::blendCustomProperty(*this, customProperty, targetStyle, startKeyframeStyle, endKeyframeStyle, transformedDistance, CompositeOperation::Replace); }
);
};

0 comments on commit bcd8cc0

Please sign in to comment.