Don't use ComputeDistance in nsTransitionManager when shortening reversals of partially running transitions, and shorten only transitions that are actually back to the current start point. (Bug 582379, bug 526784) r=bzbarsky a2.0=blocking

This commit is contained in:
L. David Baron 2010-11-10 07:49:53 -08:00
Родитель ac826bf5e5
Коммит f9a957cead
1 изменённых файлов: 83 добавлений и 64 удалений

Просмотреть файл

@ -75,6 +75,28 @@ struct ElementPropertyTransition
TimeDuration mDuration;
nsSMILKeySpline mTimingFunction;
// This is the start value to be used for a check for whether a
// transition is being reversed. Normally the same as mStartValue,
// except when this transition started as the reversal of another
// in-progress transition. Needed so we can handle two reverses in a
// row.
nsStyleAnimation::Value mStartForReversingTest;
// Likewise, the portion (in value space) of the "full" reversed
// transition that we're actually covering. For example, if a :hover
// effect has a transition that moves the element 10px to the right
// (by changing 'left' from 0px to 10px), and the mouse moves in to
// the element (starting the transition) but then moves out after the
// transition has advanced 4px, the second transition (from 10px/4px
// to 0px) will have mReversePortion of 0.4. (If the mouse then moves
// in again when the transition is back to 2px, the mReversePortion
// for the third transition (from 0px/2px to 10px) will be 0.8.
double mReversePortion;
// Compute the portion of the *value* space that we should be through
// at the given time. (The input to the transition timing function
// has time units, the output has value units.)
double ValuePortionFor(TimeStamp aRefreshTime) const;
PRBool IsRemovedSentinel() const
{
return mStartTime.IsNull();
@ -87,6 +109,34 @@ struct ElementPropertyTransition
}
};
double
ElementPropertyTransition::ValuePortionFor(TimeStamp aRefreshTime) const
{
// Set |timePortion| to the portion of the way we are through the time
// input to the transition's timing function (always within the range
// 0-1).
double duration = mDuration.ToSeconds();
NS_ABORT_IF_FALSE(duration >= 0.0, "negative duration forbidden");
double timePortion;
if (duration == 0.0) {
// When duration is zero, we can still have a transition when delay
// is nonzero. mStartTime already incorporates delay.
if (aRefreshTime >= mStartTime) {
timePortion = 0.0;
} else {
timePortion = 1.0;
}
} else {
timePortion = (aRefreshTime - mStartTime).ToSeconds() / duration;
if (timePortion < 0.0)
timePortion = 0.0; // use start value during transition-delay
if (timePortion > 1.0)
timePortion = 1.0; // we might be behind on flushing
}
return mTimingFunction.GetSplineValue(timePortion);
}
/**
* A style rule that maps property-nsStyleAnimation::Value pairs.
*/
@ -204,26 +254,7 @@ ElementTransitions::EnsureStyleRuleFor(TimeStamp aRefreshTime)
continue;
}
double duration = pt.mDuration.ToSeconds();
NS_ABORT_IF_FALSE(duration >= 0.0, "negative duration forbidden");
double timePortion;
if (duration == 0.0) {
if (aRefreshTime >= pt.mStartTime) {
timePortion = 0.0;
} else {
timePortion = 1.0;
}
} else {
timePortion = (aRefreshTime - pt.mStartTime).ToSeconds() /
pt.mDuration.ToSeconds();
if (timePortion < 0.0)
timePortion = 0.0; // use start value during transition-delay
if (timePortion > 1.0)
timePortion = 1.0; // we might be behind on flushing
}
double valuePortion =
pt.mTimingFunction.GetSplineValue(timePortion);
double valuePortion = pt.ValuePortionFor(aRefreshTime);
#ifdef DEBUG
PRBool ok =
#endif
@ -600,15 +631,21 @@ nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty,
return;
}
// When we interrupt a running transition, we want to reduce the
// duration of the new transition *if* the new transition would have
// been longer had it started from the endpoint of the currently
// running transition.
double durationFraction = 1.0;
TimeStamp mostRecentRefresh =
presContext->RefreshDriver()->MostRecentRefresh();
const nsTimingFunction &tf = aTransition.GetTimingFunction();
float delay = aTransition.GetDelay();
float duration = aTransition.GetDuration();
if (duration < 0.0) {
// The spec says a negative duration is treated as zero.
duration = 0.0;
}
pt.mStartForReversingTest = pt.mStartValue;
pt.mReversePortion = 1.0;
// We need to check two things if we have a currently running
// transition for this property: see durationFraction comment above
// and the endpoint check below.
// transition for this property.
if (currentIndex != nsTArray<ElementPropertyTransition>::NoIndex) {
const ElementPropertyTransition &oldPT =
aElementTransitions->mPropertyTransitions[currentIndex];
@ -621,49 +658,31 @@ nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty,
return;
}
double fullDistance, remainingDistance;
#ifdef DEBUG
PRBool ok =
#endif
nsStyleAnimation::ComputeDistance(aProperty, pt.mStartValue,
pt.mEndValue, fullDistance);
NS_ABORT_IF_FALSE(ok, "could not compute distance");
NS_ABORT_IF_FALSE(fullDistance >= 0.0, "distance must be positive");
// If the new transition reverses the old one, we'll need to handle
// the timing differently.
if (!oldPT.IsRemovedSentinel() &&
nsStyleAnimation::ComputeDistance(aProperty, oldPT.mEndValue,
pt.mEndValue, remainingDistance)) {
NS_ABORT_IF_FALSE(remainingDistance >= 0.0, "distance must be positive");
durationFraction = fullDistance / remainingDistance;
if (durationFraction > 1.0) {
durationFraction = 1.0;
}
oldPT.mStartForReversingTest == pt.mEndValue) {
// Compute the appropriate negative transition-delay such that right
// now we'd end up at the current position.
double valuePortion =
oldPT.ValuePortionFor(mostRecentRefresh) * oldPT.mReversePortion +
(1.0 - oldPT.mReversePortion);
// Negative delays are essentially part of the transition
// function, so reduce them along with the duration, but don't
// reduce positive delays.
if (delay < 0.0f)
delay *= valuePortion;
duration *= valuePortion;
pt.mStartForReversingTest = oldPT.mEndValue;
pt.mReversePortion = valuePortion;
}
}
nsRefreshDriver *rd = presContext->RefreshDriver();
pt.mProperty = aProperty;
float delay = aTransition.GetDelay();
float duration = aTransition.GetDuration();
if (duration < 0.0) {
// The spec says a negative duration is treated as zero.
duration = 0.0;
}
if (durationFraction != 1.0) {
// Negative delays are essentially part of the transition
// function, so reduce them along with the duration, but don't
// reduce positive delays. (See comment above about
// durationFraction.)
if (delay < 0.0f)
delay *= durationFraction;
duration *= durationFraction;
}
pt.mStartTime = rd->MostRecentRefresh() +
TimeDuration::FromMilliseconds(delay);
pt.mStartTime = mostRecentRefresh + TimeDuration::FromMilliseconds(delay);
pt.mDuration = TimeDuration::FromMilliseconds(duration);
const nsTimingFunction &tf = aTransition.GetTimingFunction();
pt.mTimingFunction.Init(tf.mX1, tf.mY1, tf.mX2, tf.mY2);
if (!aElementTransitions) {