Changeset 251543 in webkit


Ignore:
Timestamp:
Oct 24, 2019 9:35:08 AM (4 years ago)
Author:
graouts@webkit.org
Message:

[Web Animations] Only process CSS properties affected by a given CSS transition
https://bugs.webkit.org/show_bug.cgi?id=203238

Reviewed by Simon Fraser.

When the "transition" property, or one of its longhands, would change we would iterate over every known CSS property to identify whether the before and after
styles showed reasons to create, modify or cancel a backing CSSTransition object. However, we only need to do this in the case where "transition-property" is
"all", ie. where the Animation object has its animationMode() set to Animation::AnimateAll. In all other cases, we only need to iterate over the CSS properties
specified by "transition-property".

We now compile a list of CSS properties to iterate over by looking at all properties listed in the "transition-property" for the before and after styles.

  • animation/AnimationTimeline.cpp:

(WebCore::compileTransitionPropertiesInStyle):
(WebCore::AnimationTimeline::updateCSSTransitionsForElementAndProperty):
(WebCore::AnimationTimeline::updateCSSTransitionsForElement):

  • animation/AnimationTimeline.h:
Location:
trunk/Source/WebCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r251542 r251543  
     12019-10-24  Antoine Quint  <graouts@apple.com>
     2
     3        [Web Animations] Only process CSS properties affected by a given CSS transition
     4        https://bugs.webkit.org/show_bug.cgi?id=203238
     5
     6        Reviewed by Simon Fraser.
     7
     8        When the "transition" property, or one of its longhands, would change we would iterate over every known CSS property to identify whether the before and after
     9        styles showed reasons to create, modify or cancel a backing CSSTransition object. However, we only need to do this in the case where "transition-property" is
     10        "all", ie. where the Animation object has its animationMode() set to Animation::AnimateAll. In all other cases, we only need to iterate over the CSS properties
     11        specified by "transition-property".
     12
     13        We now compile a list of CSS properties to iterate over by looking at all properties listed in the "transition-property" for the before and after styles.
     14
     15        * animation/AnimationTimeline.cpp:
     16        (WebCore::compileTransitionPropertiesInStyle):
     17        (WebCore::AnimationTimeline::updateCSSTransitionsForElementAndProperty):
     18        (WebCore::AnimationTimeline::updateCSSTransitionsForElement):
     19        * animation/AnimationTimeline.h:
     20
    1212019-10-24  youenn fablet  <youenn@apple.com>
    222
  • trunk/Source/WebCore/animation/AnimationTimeline.cpp

    r250626 r251543  
    350350}
    351351
     352static void compileTransitionPropertiesInStyle(const RenderStyle& style, HashSet<CSSPropertyID>& transitionProperties, bool& transitionPropertiesContainAll)
     353{
     354    if (transitionPropertiesContainAll)
     355        return;
     356
     357    auto* transitions = style.transitions();
     358    if (!transitions)
     359        return;
     360
     361    for (size_t i = 0; i < transitions->size(); ++i) {
     362        const auto& animation = transitions->animation(i);
     363        auto mode = animation.animationMode();
     364        if (mode == Animation::AnimateSingleProperty) {
     365            auto property = animation.property();
     366            if (isShorthandCSSProperty(property)) {
     367                auto shorthand = shorthandForProperty(property);
     368                for (size_t j = 0; j < shorthand.length(); ++j)
     369                    transitionProperties.add(shorthand.properties()[j]);
     370            } else
     371                transitionProperties.add(property);
     372        } else if (mode == Animation::AnimateAll) {
     373            transitionPropertiesContainAll = true;
     374            return;
     375        }
     376    }
     377}
     378
     379void AnimationTimeline::updateCSSTransitionsForElementAndProperty(Element& element, CSSPropertyID property, const RenderStyle& currentStyle, const RenderStyle& afterChangeStyle, AnimationTimeline::PropertyToTransitionMap& runningTransitionsByProperty, PropertyToTransitionMap& completedTransitionsByProperty, const MonotonicTime generationTime)
     380{
     381    const Animation* matchingBackingAnimation = nullptr;
     382    if (auto* transitions = afterChangeStyle.transitions()) {
     383        for (size_t i = 0; i < transitions->size(); ++i) {
     384            auto& backingAnimation = transitions->animation(i);
     385            if (transitionMatchesProperty(backingAnimation, property))
     386                matchingBackingAnimation = &backingAnimation;
     387        }
     388    }
     389
     390    // https://drafts.csswg.org/css-transitions-1/#before-change-style
     391    // Define the before-change style as the computed values of all properties on the element as of the previous style change event, except with
     392    // any styles derived from declarative animations such as CSS Transitions, CSS Animations, and SMIL Animations updated to the current time.
     393    auto existingAnimation = cssAnimationForElementAndProperty(element, property);
     394    const auto& beforeChangeStyle = existingAnimation ? downcast<CSSAnimation>(existingAnimation.get())->unanimatedStyle() : currentStyle;
     395
     396    if (!runningTransitionsByProperty.contains(property)
     397        && !CSSPropertyAnimation::propertiesEqual(property, &beforeChangeStyle, &afterChangeStyle)
     398        && CSSPropertyAnimation::canPropertyBeInterpolated(property, &beforeChangeStyle, &afterChangeStyle)
     399        && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty)
     400        && matchingBackingAnimation && transitionCombinedDuration(matchingBackingAnimation) > 0) {
     401        // 1. If all of the following are true:
     402        //   - the element does not have a running transition for the property,
     403        //   - the before-change style is different from and can be interpolated with the after-change style for that property,
     404        //   - the element does not have a completed transition for the property or the end value of the completed transition is different from the after-change style for the property,
     405        //   - there is a matching transition-property value, and
     406        //   - the combined duration is greater than 0s,
     407
     408        // then implementations must remove the completed transition (if present) from the set of completed transitions
     409        completedTransitionsByProperty.remove(property);
     410
     411        // and start a transition whose:
     412        //   - start time is the time of the style change event plus the matching transition delay,
     413        //   - end time is the start time plus the matching transition duration,
     414        //   - start value is the value of the transitioning property in the before-change style,
     415        //   - end value is the value of the transitioning property in the after-change style,
     416        //   - reversing-adjusted start value is the same as the start value, and
     417        //   - reversing shortening factor is 1.
     418        auto delay = Seconds(matchingBackingAnimation->delay());
     419        auto duration = Seconds(matchingBackingAnimation->duration());
     420        auto& reversingAdjustedStartStyle = beforeChangeStyle;
     421        auto reversingShorteningFactor = 1;
     422        runningTransitionsByProperty.set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &beforeChangeStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
     423    } else if (completedTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty)) {
     424        // 2. Otherwise, if the element has a completed transition for the property and the end value of the completed transition is different from
     425        //    the after-change style for the property, then implementations must remove the completed transition from the set of completed transitions.
     426        completedTransitionsByProperty.remove(property);
     427    }
     428
     429    bool hasRunningTransition = runningTransitionsByProperty.contains(property);
     430    if ((hasRunningTransition || completedTransitionsByProperty.contains(property)) && !matchingBackingAnimation) {
     431        // 3. If the element has a running transition or completed transition for the property, and there is not a matching transition-property
     432        //    value, then implementations must cancel the running transition or remove the completed transition from the set of completed transitions.
     433        if (hasRunningTransition)
     434            runningTransitionsByProperty.take(property)->cancel();
     435        else
     436            completedTransitionsByProperty.remove(property);
     437    }
     438
     439    if (matchingBackingAnimation && runningTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, runningTransitionsByProperty)) {
     440        auto previouslyRunningTransition = runningTransitionsByProperty.take(property);
     441        auto& previouslyRunningTransitionCurrentStyle = previouslyRunningTransition->currentStyle();
     442        // 4. If the element has a running transition for the property, there is a matching transition-property value, and the end value of the running
     443        //    transition is not equal to the value of the property in the after-change style, then:
     444        if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle) || !CSSPropertyAnimation::canPropertyBeInterpolated(property, &currentStyle, &afterChangeStyle)) {
     445            // 1. If the current value of the property in the running transition is equal to the value of the property in the after-change style,
     446            //    or if these two values cannot be interpolated, then implementations must cancel the running transition.
     447            cancelDeclarativeAnimation(*previouslyRunningTransition);
     448        } else if (transitionCombinedDuration(matchingBackingAnimation) <= 0.0 || !CSSPropertyAnimation::canPropertyBeInterpolated(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle)) {
     449            // 2. Otherwise, if the combined duration is less than or equal to 0s, or if the current value of the property in the running transition
     450            //    cannot be interpolated with the value of the property in the after-change style, then implementations must cancel the running transition.
     451            cancelDeclarativeAnimation(*previouslyRunningTransition);
     452        } else if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransition->reversingAdjustedStartStyle(), &afterChangeStyle)) {
     453            // 3. Otherwise, if the reversing-adjusted start value of the running transition is the same as the value of the property in the after-change
     454            //    style (see the section on reversing of transitions for why these case exists), implementations must cancel the running transition
     455            cancelDeclarativeAnimation(*previouslyRunningTransition);
     456
     457            // and start a new transition whose:
     458            //   - reversing-adjusted start value is the end value of the running transition,
     459            //   - reversing shortening factor is the absolute value, clamped to the range [0, 1], of the sum of:
     460            //       1. the output of the timing function of the old transition at the time of the style change event, times the reversing shortening factor of the old transition
     461            //       2. 1 minus the reversing shortening factor of the old transition.
     462            //   - start time is the time of the style change event plus:
     463            //       1. if the matching transition delay is nonnegative, the matching transition delay, or
     464            //       2. if the matching transition delay is negative, the product of the new transition’s reversing shortening factor and the matching transition delay,
     465            //   - end time is the start time plus the product of the matching transition duration and the new transition’s reversing shortening factor,
     466            //   - start value is the current value of the property in the running transition,
     467            //   - end value is the value of the property in the after-change style
     468            auto& reversingAdjustedStartStyle = previouslyRunningTransition->targetStyle();
     469            double transformedProgress = 1;
     470            if (auto* effect = previouslyRunningTransition->effect()) {
     471                if (auto computedTimingProgress = effect->getComputedTiming().progress)
     472                    transformedProgress = *computedTimingProgress;
     473            }
     474            auto reversingShorteningFactor = std::max(std::min(((transformedProgress * previouslyRunningTransition->reversingShorteningFactor()) + (1 - previouslyRunningTransition->reversingShorteningFactor())), 1.0), 0.0);
     475            auto delay = matchingBackingAnimation->delay() < 0 ? Seconds(matchingBackingAnimation->delay()) * reversingShorteningFactor : Seconds(matchingBackingAnimation->delay());
     476            auto duration = Seconds(matchingBackingAnimation->duration()) * reversingShorteningFactor;
     477
     478            ensureRunningTransitionsByProperty(element).set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
     479        } else {
     480            // 4. Otherwise, implementations must cancel the running transition
     481            cancelDeclarativeAnimation(*previouslyRunningTransition);
     482
     483            // and start a new transition whose:
     484            //   - start time is the time of the style change event plus the matching transition delay,
     485            //   - end time is the start time plus the matching transition duration,
     486            //   - start value is the current value of the property in the running transition,
     487            //   - end value is the value of the property in the after-change style,
     488            //   - reversing-adjusted start value is the same as the start value, and
     489            //   - reversing shortening factor is 1.
     490            auto delay = Seconds(matchingBackingAnimation->delay());
     491            auto duration = Seconds(matchingBackingAnimation->duration());
     492            auto& reversingAdjustedStartStyle = currentStyle;
     493            auto reversingShorteningFactor = 1;
     494            ensureRunningTransitionsByProperty(element).set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
     495        }
     496    }
     497}
     498
    352499void AnimationTimeline::updateCSSTransitionsForElement(Element& element, const RenderStyle& currentStyle, const RenderStyle& afterChangeStyle)
    353500{
     
    372519    auto generationTime = MonotonicTime::now();
    373520
    374     auto numberOfProperties = CSSPropertyAnimation::getNumProperties();
    375     for (int propertyIndex = 0; propertyIndex < numberOfProperties; ++propertyIndex) {
    376         Optional<bool> isShorthand;
    377         auto property = CSSPropertyAnimation::getPropertyAtIndex(propertyIndex, isShorthand);
    378         if (isShorthand && *isShorthand)
    379             continue;
    380 
    381         const Animation* matchingBackingAnimation = nullptr;
    382         if (auto* transitions = afterChangeStyle.transitions()) {
    383             for (size_t i = 0; i < transitions->size(); ++i) {
    384                 auto& backingAnimation = transitions->animation(i);
    385                 if (transitionMatchesProperty(backingAnimation, property))
    386                     matchingBackingAnimation = &backingAnimation;
    387             }
    388         }
    389 
    390         // https://drafts.csswg.org/css-transitions-1/#before-change-style
    391         // Define the before-change style as the computed values of all properties on the element as of the previous style change event, except with
    392         // any styles derived from declarative animations such as CSS Transitions, CSS Animations, and SMIL Animations updated to the current time.
    393         auto existingAnimation = cssAnimationForElementAndProperty(element, property);
    394         const auto& beforeChangeStyle = existingAnimation ? downcast<CSSAnimation>(existingAnimation.get())->unanimatedStyle() : currentStyle;
    395 
    396         if (!runningTransitionsByProperty.contains(property)
    397             && !CSSPropertyAnimation::propertiesEqual(property, &beforeChangeStyle, &afterChangeStyle)
    398             && CSSPropertyAnimation::canPropertyBeInterpolated(property, &beforeChangeStyle, &afterChangeStyle)
    399             && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty)
    400             && matchingBackingAnimation && transitionCombinedDuration(matchingBackingAnimation) > 0) {
    401             // 1. If all of the following are true:
    402             //   - the element does not have a running transition for the property,
    403             //   - the before-change style is different from and can be interpolated with the after-change style for that property,
    404             //   - the element does not have a completed transition for the property or the end value of the completed transition is different from the after-change style for the property,
    405             //   - there is a matching transition-property value, and
    406             //   - the combined duration is greater than 0s,
    407 
    408             // then implementations must remove the completed transition (if present) from the set of completed transitions
    409             completedTransitionsByProperty.remove(property);
    410 
    411             // and start a transition whose:
    412             //   - start time is the time of the style change event plus the matching transition delay,
    413             //   - end time is the start time plus the matching transition duration,
    414             //   - start value is the value of the transitioning property in the before-change style,
    415             //   - end value is the value of the transitioning property in the after-change style,
    416             //   - reversing-adjusted start value is the same as the start value, and
    417             //   - reversing shortening factor is 1.
    418             auto delay = Seconds(matchingBackingAnimation->delay());
    419             auto duration = Seconds(matchingBackingAnimation->duration());
    420             auto& reversingAdjustedStartStyle = beforeChangeStyle;
    421             auto reversingShorteningFactor = 1;
    422             runningTransitionsByProperty.set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &beforeChangeStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
    423         } else if (completedTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, completedTransitionsByProperty)) {
    424             // 2. Otherwise, if the element has a completed transition for the property and the end value of the completed transition is different from
    425             //    the after-change style for the property, then implementations must remove the completed transition from the set of completed transitions.
    426             completedTransitionsByProperty.remove(property);
    427         }
    428 
    429         bool hasRunningTransition = runningTransitionsByProperty.contains(property);
    430         if ((hasRunningTransition || completedTransitionsByProperty.contains(property)) && !matchingBackingAnimation) {
    431             // 3. If the element has a running transition or completed transition for the property, and there is not a matching transition-property
    432             //    value, then implementations must cancel the running transition or remove the completed transition from the set of completed transitions.
    433             if (hasRunningTransition)
    434                 runningTransitionsByProperty.take(property)->cancel();
    435             else
    436                 completedTransitionsByProperty.remove(property);
    437         }
    438 
    439         if (matchingBackingAnimation && runningTransitionsByProperty.contains(property) && !propertyInStyleMatchesValueForTransitionInMap(property, afterChangeStyle, runningTransitionsByProperty)) {
    440             auto previouslyRunningTransition = runningTransitionsByProperty.take(property);
    441             auto& previouslyRunningTransitionCurrentStyle = previouslyRunningTransition->currentStyle();
    442             // 4. If the element has a running transition for the property, there is a matching transition-property value, and the end value of the running
    443             //    transition is not equal to the value of the property in the after-change style, then:
    444             if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle) || !CSSPropertyAnimation::canPropertyBeInterpolated(property, &currentStyle, &afterChangeStyle)) {
    445                 // 1. If the current value of the property in the running transition is equal to the value of the property in the after-change style,
    446                 //    or if these two values cannot be interpolated, then implementations must cancel the running transition.
    447                 cancelDeclarativeAnimation(*previouslyRunningTransition);
    448             } else if (transitionCombinedDuration(matchingBackingAnimation) <= 0.0 || !CSSPropertyAnimation::canPropertyBeInterpolated(property, &previouslyRunningTransitionCurrentStyle, &afterChangeStyle)) {
    449                 // 2. Otherwise, if the combined duration is less than or equal to 0s, or if the current value of the property in the running transition
    450                 //    cannot be interpolated with the value of the property in the after-change style, then implementations must cancel the running transition.
    451                 cancelDeclarativeAnimation(*previouslyRunningTransition);
    452             } else if (CSSPropertyAnimation::propertiesEqual(property, &previouslyRunningTransition->reversingAdjustedStartStyle(), &afterChangeStyle)) {
    453                 // 3. Otherwise, if the reversing-adjusted start value of the running transition is the same as the value of the property in the after-change
    454                 //    style (see the section on reversing of transitions for why these case exists), implementations must cancel the running transition
    455                 cancelDeclarativeAnimation(*previouslyRunningTransition);
    456 
    457                 // and start a new transition whose:
    458                 //   - reversing-adjusted start value is the end value of the running transition,
    459                 //   - reversing shortening factor is the absolute value, clamped to the range [0, 1], of the sum of:
    460                 //       1. the output of the timing function of the old transition at the time of the style change event, times the reversing shortening factor of the old transition
    461                 //       2. 1 minus the reversing shortening factor of the old transition.
    462                 //   - start time is the time of the style change event plus:
    463                 //       1. if the matching transition delay is nonnegative, the matching transition delay, or
    464                 //       2. if the matching transition delay is negative, the product of the new transition’s reversing shortening factor and the matching transition delay,
    465                 //   - end time is the start time plus the product of the matching transition duration and the new transition’s reversing shortening factor,
    466                 //   - start value is the current value of the property in the running transition,
    467                 //   - end value is the value of the property in the after-change style
    468                 auto& reversingAdjustedStartStyle = previouslyRunningTransition->targetStyle();
    469                 double transformedProgress = 1;
    470                 if (auto* effect = previouslyRunningTransition->effect()) {
    471                     if (auto computedTimingProgress = effect->getComputedTiming().progress)
    472                         transformedProgress = *computedTimingProgress;
    473                 }
    474                 auto reversingShorteningFactor = std::max(std::min(((transformedProgress * previouslyRunningTransition->reversingShorteningFactor()) + (1 - previouslyRunningTransition->reversingShorteningFactor())), 1.0), 0.0);
    475                 auto delay = matchingBackingAnimation->delay() < 0 ? Seconds(matchingBackingAnimation->delay()) * reversingShorteningFactor : Seconds(matchingBackingAnimation->delay());
    476                 auto duration = Seconds(matchingBackingAnimation->duration()) * reversingShorteningFactor;
    477 
    478                 ensureRunningTransitionsByProperty(element).set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
    479             } else {
    480                 // 4. Otherwise, implementations must cancel the running transition
    481                 cancelDeclarativeAnimation(*previouslyRunningTransition);
    482 
    483                 // and start a new transition whose:
    484                 //   - start time is the time of the style change event plus the matching transition delay,
    485                 //   - end time is the start time plus the matching transition duration,
    486                 //   - start value is the current value of the property in the running transition,
    487                 //   - end value is the value of the property in the after-change style,
    488                 //   - reversing-adjusted start value is the same as the start value, and
    489                 //   - reversing shortening factor is 1.
    490                 auto delay = Seconds(matchingBackingAnimation->delay());
    491                 auto duration = Seconds(matchingBackingAnimation->duration());
    492                 auto& reversingAdjustedStartStyle = currentStyle;
    493                 auto reversingShorteningFactor = 1;
    494                 ensureRunningTransitionsByProperty(element).set(property, CSSTransition::create(element, property, generationTime, *matchingBackingAnimation, &previouslyRunningTransitionCurrentStyle, afterChangeStyle, delay, duration, reversingAdjustedStartStyle, reversingShorteningFactor));
    495             }
    496         }
    497     }
     521    // First, let's compile the list of all CSS properties found in the current style and the after-change style.
     522    bool transitionPropertiesContainAll = false;
     523    HashSet<CSSPropertyID> transitionProperties;
     524    compileTransitionPropertiesInStyle(currentStyle, transitionProperties, transitionPropertiesContainAll);
     525    compileTransitionPropertiesInStyle(afterChangeStyle, transitionProperties, transitionPropertiesContainAll);
     526
     527    if (transitionPropertiesContainAll) {
     528        auto numberOfProperties = CSSPropertyAnimation::getNumProperties();
     529        for (int propertyIndex = 0; propertyIndex < numberOfProperties; ++propertyIndex) {
     530            Optional<bool> isShorthand;
     531            auto property = CSSPropertyAnimation::getPropertyAtIndex(propertyIndex, isShorthand);
     532            if (isShorthand && *isShorthand)
     533                continue;
     534            updateCSSTransitionsForElementAndProperty(element, property, currentStyle, afterChangeStyle, runningTransitionsByProperty, completedTransitionsByProperty, generationTime);
     535        }
     536        return;
     537    }
     538
     539    for (auto property : transitionProperties)
     540        updateCSSTransitionsForElementAndProperty(element, property, currentStyle, afterChangeStyle, runningTransitionsByProperty, completedTransitionsByProperty, generationTime);
    498541}
    499542
  • trunk/Source/WebCore/animation/AnimationTimeline.h

    r243263 r251543  
    8585    RefPtr<WebAnimation> cssAnimationForElementAndProperty(Element&, CSSPropertyID);
    8686    PropertyToTransitionMap& ensureRunningTransitionsByProperty(Element&);
     87    void updateCSSTransitionsForElementAndProperty(Element&, CSSPropertyID, const RenderStyle& currentStyle, const RenderStyle& afterChangeStyle, PropertyToTransitionMap&, PropertyToTransitionMap&, const MonotonicTime);
    8788    void cancelDeclarativeAnimation(DeclarativeAnimation&);
    8889
Note: See TracChangeset for help on using the changeset viewer.