/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This function expands the shorthands and "all" keyword specified in // transition-property, and then execute |aHandler| on the expanded longhand. // |aHandler| should be a lamda function which accepts NonCustomCSSPropertyId. template <typename T> staticvoid ExpandTransitionProperty(const StyleTransitionProperty& aProperty,
T aHandler) { switch (aProperty.tag) { case StyleTransitionProperty::Tag::Unsupported: break; case StyleTransitionProperty::Tag::Custom: { auto property =
CSSPropertyId::FromCustomName(aProperty.AsCustom().AsAtom());
aHandler(property); break;
} case StyleTransitionProperty::Tag::NonCustom: {
NonCustomCSSPropertyId id =
NonCustomCSSPropertyId(aProperty.AsNonCustom()._0); if (nsCSSProps::IsShorthand(id)) {
CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subprop, id,
CSSEnabledState::ForAllContent) {
CSSPropertyId property(*subprop);
aHandler(property);
}
} else {
CSSPropertyId property(id);
aHandler(property);
} break;
}
}
}
// Per http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html // I'll consider only the transitions from the number of items in // 'transition-property' on down, and later ones will override earlier // ones (tracked using |propertiesChecked|). bool startedAny = false;
AnimatedPropertyIDSet propertiesChecked; for (uint32_t i = aStyle.mTransitionPropertyCount; i--;) { constfloat delay = aStyle.GetTransitionDelay(i).ToMilliseconds();
// The spec says a negative duration is treated as zero. constfloat duration =
std::max(aStyle.GetTransitionDuration(i).ToMilliseconds(), 0.0f);
// If the combined duration of this transition is 0 or less we won't start a // transition, we can avoid even looking at transition-property if we're the // last one. if (i == 0 && delay + duration <= 0.0f) { continue;
}
constauto behavior = aStyle.GetTransitionBehavior(i);
ExpandTransitionProperty(
aStyle.GetTransitionProperty(i), [&](const CSSPropertyId& aProperty) { // We might have something to transition. See if // any of the properties in question changed and // are animatable.
startedAny |= ConsiderInitiatingTransition(
aProperty, aStyle, i, delay, duration, behavior, aElement,
aPseudoRequest, aElementTransitions, aOldStyle, aNewStyle,
propertiesChecked);
});
}
// Stop any transitions for properties that are no longer in // 'transition-property', including finished transitions. // Also stop any transitions (and remove any finished transitions) // for properties that just changed (and are still in the set of // properties to transition), but for which we didn't just start the // transition. This can happen delay and duration are both zero, or // because the new value is not interpolable. if (aElementTransitions) { constbool checkProperties = !aStyle.GetTransitionProperty(0).IsAll();
AnimatedPropertyIDSet allTransitionProperties; if (checkProperties) { for (uint32_t i = aStyle.mTransitionPropertyCount; i-- != 0;) {
ExpandTransitionProperty(aStyle.GetTransitionProperty(i),
[&](const CSSPropertyId& aProperty) {
allTransitionProperties.AddProperty(
aProperty.ToPhysical(aNewStyle));
});
}
}
OwningCSSTransitionPtrArray& animations = aElementTransitions->mAnimations;
size_t i = animations.Length();
MOZ_ASSERT(i != 0, "empty transitions list?");
AnimationValue currentValue; do {
--i;
CSSTransition* anim = animations[i]; const CSSPropertyId& property = anim->TransitionProperty(); if ( // Properties no longer in `transition-property`.
(checkProperties && !allTransitionProperties.HasProperty(property)) || // Properties whose computed values changed but for which we did not // start a new transition (because delay and duration are both zero, // or because the new value is not interpolable); a new transition // would have anim->ToValue() matching currentValue.
!Servo_ComputedValues_TransitionValueMatches(
&aNewStyle, &property, anim->ToValue().mServo.get())) { // Stop the transition.
DoCancelTransition(aElement, aPseudoRequest, aElementTransitions, i);
}
} while (i != 0);
}
if (!aTransition.HasCurrentEffect()) { return result;
}
// Transition needs to be running on the same timeline. if (aTransition.GetTimeline() != aTimelineToMatch) { return result;
}
auto startTime = aTransition.GetStartTime(); if (startTime.IsNull() && !aTransition.GetPendingReadyTime().IsNull()) {
startTime =
aTimelineToMatch->ToTimelineTime(aTransition.GetPendingReadyTime());
}
if (startTime.IsNull()) { return result;
}
// The transition needs to have a keyframe effect. const KeyframeEffect* keyframeEffect =
aTransition.GetEffect() ? aTransition.GetEffect()->AsKeyframeEffect()
: nullptr; if (!keyframeEffect) { return result;
}
// The keyframe effect needs to be a simple transition of the original // transition property (i.e. not replaced with something else). if (keyframeEffect->Properties().Length() != 1 ||
keyframeEffect->Properties()[0].mSegments.Length() != 1 ||
keyframeEffect->Properties()[0].mProperty !=
aTransition.TransitionProperty()) { return result;
}
// A later item in transition-property already specified a transition for // this property, so we ignore this one. // // See http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html . if (aPropertiesChecked.HasProperty(property)) { returnfalse;
}
// For compositor animations, |aOldStyle| may have out-of-date transition // rules, and it may be equal to the |endValue| of a reversing transition by // accidentally. This causes Servo_ComputedValues_ShouldTransition() to return // an incorrect result. Therefore, we have to recompute the current value if // this transition is running on the compositor, to make sure we create the // transition properly. Here, we pre-compute the progress and collect the // necessary info, so Servo_ComputedValues_ShouldTransition() could compute // the current value if needed. // FIXME: Bug 1634945. We should use the last value from the compositor as the // current value.
Maybe<ReplacedTransitionProperties> replacedTransitionProperties;
Maybe<double> progress; if (oldTransition) { // If this new transition is replacing an existing transition, we store // select parameters from the replaced transition so that later, once all // scripts have run, we can update the start value of the transition using // TimeStamp::Now(). This allows us to avoid a large jump when starting a // new transition when the main thread lags behind the compositor. // // Note: We compute this before calling // Servo_ComputedValues_ShouldTransition() so we can reuse it for computing // the current value and setting the replaced transition properties later in // this function. const dom::DocumentTimeline* timeline = aElement->OwnerDoc()->Timeline();
replacedTransitionProperties =
GetReplacedTransitionProperties(*oldTransition, timeline);
progress = replacedTransitionProperties.andThen(
[&](const ReplacedTransitionProperties& aProperties) { const dom::AnimationTimeline* timeline = oldTransition->GetTimeline();
MOZ_ASSERT(timeline); return CSSTransition::ComputeTransformedProgress(*timeline,
aProperties);
});
}
AnimationValue startValue, endValue; const StyleShouldTransitionResult result =
Servo_ComputedValues_ShouldTransition(
&aOldStyle, &aNewStyle, &property, aBehavior,
oldTransition ? oldTransition->ToValue().mServo.get() : nullptr,
replacedTransitionProperties
? replacedTransitionProperties->mFromValue.mServo.get()
: nullptr, // Note: It's possible to replace the keyframes by Web Animations API, // so we have to pass the mToValue from the keyframe segment, to make // sure this value is aligned with mFromValue.
replacedTransitionProperties
? replacedTransitionProperties->mToValue.mServo.get()
: nullptr,
progress.ptrOr(nullptr), &startValue.mServo, &endValue.mServo);
// If we got a style change that changed the value to the endpoint // of the currently running transition, we don't want to interrupt // its timing function. // This needs to be before the !shouldAnimate && haveCurrentTransition // case below because we might be close enough to the end of the // transition that the current value rounds to the final value. In // this case, we'll end up with shouldAnimate as false (because // there's no value change), but we need to return early here rather // than cancel the running transition because shouldAnimate is false! // // Likewise, if we got a style change that changed the value to the // endpoint of our finished transition, we also don't want to start // a new transition for the reasons described in // https://lists.w3.org/Archives/Public/www-style/2015Jan/0444.html . if (result.old_transition_value_matches) { // GetAnimationRule already called RestyleForAnimation. returnfalse;
}
if (!result.should_animate) { if (oldTransition) { // We're in the middle of a transition, and just got a non-transition // style change to something that we can't animate. This might happen // because we got a non-transition style change changing to the current // in-progress value (which is particularly easy to cause when we're // currently in the 'transition-delay'). It also might happen because we // just got a style change to a value that can't be interpolated.
DoCancelTransition(aElement, aPseudoRequest, aElementTransitions,
currentIndex);
} returnfalse;
}
// If the new transition reverses an existing one, we'll need to // handle the timing differently. if (oldTransition && oldTransition->HasCurrentEffect() &&
oldTransition->StartForReversingTest() == endValue) { // Compute the appropriate negative transition-delay such that right // now we'd end up at the current position. double valuePortion =
oldTransition->CurrentValuePortion() * oldTransition->ReversePortion() +
(1.0 - oldTransition->ReversePortion()); // A timing function with negative y1 (or y2!) might make // valuePortion negative. In this case, we still want to apply our // reversing logic based on relative distances, not make duration // negative. if (valuePortion < 0.0) {
valuePortion = -valuePortion;
} // A timing function with y2 (or y1!) greater than one might // advance past its terminal value. It's probably a good idea to // clamp valuePortion to be at most one to preserve the invariant // that a transition will complete within at most its specified // time. if (valuePortion > 1.0) {
valuePortion = 1.0;
}
// Negative delays are essentially part of the transition // function, so reduce them along with the duration, but don't // reduce positive delays. if (aDelay < 0.0f && std::isfinite(aDelay)) {
aDelay *= valuePortion;
}
if (std::isfinite(aDuration)) {
aDuration *= valuePortion;
}
OwningCSSTransitionPtrArray& transitions = aElementTransitions->mAnimations; #ifdef DEBUG for (size_t i = 0, i_end = transitions.Length(); i < i_end; ++i) {
MOZ_ASSERT(
i == currentIndex || transitions[i]->TransitionProperty() != property, "duplicate transitions for property");
} #endif if (oldTransition) { if (replacedTransitionProperties) {
transition->SetReplacedTransition(
std::move(replacedTransitionProperties.ref()));
}
transitions[currentIndex]->CancelFromStyle(PostRestyleMode::IfNeeded);
oldTransition = nullptr; // Clear pointer so it doesn't dangle
transitions[currentIndex] = transition;
} else { // XXX(Bug 1631371) Check if this should use a fallible operation as it // pretended earlier.
transitions.AppendElement(transition);
}
if (auto* effectSet = EffectSet::Get(aElement, aPseudoRequest)) {
effectSet->UpdateAnimationGeneration(mPresContext);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.