bool ScrollAnimatorNone::PerAxisData::updateDataFromParameters(float step, float multiplier, float scrollableSize, double currentTime, Parameters* parameters) { float delta = step * multiplier; if (!m_startTime || !delta || (delta < 0) != (m_desiredPosition - *m_currentPosition < 0)) { m_desiredPosition = *m_currentPosition; m_startTime = 0; } float newPosition = m_desiredPosition + delta; if (newPosition < 0 || newPosition > scrollableSize) newPosition = max(min(newPosition, scrollableSize), 0.0f); if (newPosition == m_desiredPosition) return false; m_desiredPosition = newPosition; if (!m_startTime) { m_attackTime = parameters->m_attackTime; m_attackCurve = parameters->m_attackCurve; } m_animationTime = parameters->m_animationTime; m_releaseTime = parameters->m_releaseTime; m_releaseCurve = parameters->m_releaseCurve; // Prioritize our way out of over constraint. if (m_attackTime + m_releaseTime > m_animationTime) { if (m_releaseTime > m_animationTime) m_releaseTime = m_animationTime; m_attackTime = m_animationTime - m_releaseTime; } if (!m_startTime) { // FIXME: This should be the time from the event that got us here. m_startTime = currentTime - kTickTime / 2; m_startPosition = *m_currentPosition; m_lastAnimationTime = m_startTime; } m_startVelocity = m_currentVelocity; double remainingDelta = m_desiredPosition - *m_currentPosition; double attackAreaLeft = 0; double deltaTime = m_lastAnimationTime - m_startTime; double attackTimeLeft = max(0., m_attackTime - deltaTime); double timeLeft = m_animationTime - deltaTime; double minTimeLeft = m_releaseTime + min(parameters->m_repeatMinimumSustainTime, m_animationTime - m_releaseTime - attackTimeLeft); if (timeLeft < minTimeLeft) { m_animationTime = deltaTime + minTimeLeft; timeLeft = minTimeLeft; } if (parameters->m_maximumCoastTime > (parameters->m_repeatMinimumSustainTime + parameters->m_releaseTime)) { double targetMaxCoastVelocity = m_visibleLength * .25 * kFrameRate; // This needs to be as minimal as possible while not being intrusive to page up/down. double minCoastDelta = m_visibleLength; if (fabs(remainingDelta) > minCoastDelta) { double maxCoastDelta = parameters->m_maximumCoastTime * targetMaxCoastVelocity; double coastFactor = min(1., (fabs(remainingDelta) - minCoastDelta) / (maxCoastDelta - minCoastDelta)); // We could play with the curve here - linear seems a little soft. Initial testing makes me want to feed into the sustain time more aggressively. double coastMinTimeLeft = min(parameters->m_maximumCoastTime, minTimeLeft + coastCurve(parameters->m_coastTimeCurve, coastFactor) * (parameters->m_maximumCoastTime - minTimeLeft)); double additionalTime = max(0., coastMinTimeLeft - minTimeLeft); if (additionalTime) { double additionalReleaseTime = min(additionalTime, parameters->m_releaseTime / (parameters->m_releaseTime + parameters->m_repeatMinimumSustainTime) * additionalTime); m_releaseTime = parameters->m_releaseTime + additionalReleaseTime; m_animationTime = deltaTime + coastMinTimeLeft; timeLeft = coastMinTimeLeft; } } } double releaseTimeLeft = min(timeLeft, m_releaseTime); double sustainTimeLeft = max(0., timeLeft - releaseTimeLeft - attackTimeLeft); if (attackTimeLeft) { double attackSpot = deltaTime / m_attackTime; attackAreaLeft = attackArea(m_attackCurve, attackSpot, 1) * m_attackTime; } double releaseSpot = (m_releaseTime - releaseTimeLeft) / m_releaseTime; double releaseAreaLeft = releaseArea(m_releaseCurve, releaseSpot, 1) * m_releaseTime; m_desiredVelocity = remainingDelta / (attackAreaLeft + sustainTimeLeft + releaseAreaLeft); m_releasePosition = m_desiredPosition - m_desiredVelocity * releaseAreaLeft; if (attackAreaLeft) m_attackPosition = m_startPosition + m_desiredVelocity * attackAreaLeft; else m_attackPosition = m_releasePosition - (m_animationTime - m_releaseTime - m_attackTime) * m_desiredVelocity; if (sustainTimeLeft) { double roundOff = m_releasePosition - ((attackAreaLeft ? m_attackPosition : *m_currentPosition) + m_desiredVelocity * sustainTimeLeft); m_desiredVelocity += roundOff / sustainTimeLeft; } return true; }
bool ScrollAnimationSmooth::updatePerAxisData(PerAxisData& data, ScrollGranularity granularity, float delta, float minScrollPosition, float maxScrollPosition) { if (!data.startTime || !delta || (delta < 0) != (data.desiredPosition - data.currentPosition < 0)) { data.desiredPosition = data.currentPosition; data.startTime = 0; } float newPosition = data.desiredPosition + delta; newPosition = std::max(std::min(newPosition, maxScrollPosition), minScrollPosition); if (newPosition == data.desiredPosition) return false; double animationTime, repeatMinimumSustainTime, attackTime, releaseTime, maximumCoastTime; Curve coastTimeCurve; getAnimationParametersForGranularity(granularity, animationTime, repeatMinimumSustainTime, attackTime, releaseTime, coastTimeCurve, maximumCoastTime); data.desiredPosition = newPosition; if (!data.startTime) data.attackTime = attackTime; data.animationTime = animationTime; data.releaseTime = releaseTime; // Prioritize our way out of over constraint. if (data.attackTime + data.releaseTime > data.animationTime) { if (data.releaseTime > data.animationTime) data.releaseTime = data.animationTime; data.attackTime = data.animationTime - data.releaseTime; } if (!data.startTime) { // FIXME: This should be the time from the event that got us here. data.startTime = monotonicallyIncreasingTime() - tickTime / 2; data.startPosition = data.currentPosition; data.lastAnimationTime = data.startTime; } data.startVelocity = data.currentVelocity; double remainingDelta = data.desiredPosition - data.currentPosition; double attackAreaLeft = 0; double deltaTime = data.lastAnimationTime - data.startTime; double attackTimeLeft = std::max(0., data.attackTime - deltaTime); double timeLeft = data.animationTime - deltaTime; double minTimeLeft = data.releaseTime + std::min(repeatMinimumSustainTime, data.animationTime - data.releaseTime - attackTimeLeft); if (timeLeft < minTimeLeft) { data.animationTime = deltaTime + minTimeLeft; timeLeft = minTimeLeft; } if (maximumCoastTime > (repeatMinimumSustainTime + releaseTime)) { double targetMaxCoastVelocity = data.visibleLength * .25 * frameRate; // This needs to be as minimal as possible while not being intrusive to page up/down. double minCoastDelta = data.visibleLength; if (fabs(remainingDelta) > minCoastDelta) { double maxCoastDelta = maximumCoastTime * targetMaxCoastVelocity; double coastFactor = std::min(1., (fabs(remainingDelta) - minCoastDelta) / (maxCoastDelta - minCoastDelta)); // We could play with the curve here - linear seems a little soft. Initial testing makes me want to feed into the sustain time more aggressively. double coastMinTimeLeft = std::min(maximumCoastTime, minTimeLeft + coastCurve(coastTimeCurve, coastFactor) * (maximumCoastTime - minTimeLeft)); if (double additionalTime = std::max(0., coastMinTimeLeft - minTimeLeft)) { double additionalReleaseTime = std::min(additionalTime, releaseTime / (releaseTime + repeatMinimumSustainTime) * additionalTime); data.releaseTime = releaseTime + additionalReleaseTime; data.animationTime = deltaTime + coastMinTimeLeft; timeLeft = coastMinTimeLeft; } } } double releaseTimeLeft = std::min(timeLeft, data.releaseTime); double sustainTimeLeft = std::max(0., timeLeft - releaseTimeLeft - attackTimeLeft); if (attackTimeLeft) { double attackSpot = deltaTime / data.attackTime; attackAreaLeft = attackArea(Curve::Cubic, attackSpot, 1) * data.attackTime; } double releaseSpot = (data.releaseTime - releaseTimeLeft) / data.releaseTime; double releaseAreaLeft = releaseArea(Curve::Cubic, releaseSpot, 1) * data.releaseTime; data.desiredVelocity = remainingDelta / (attackAreaLeft + sustainTimeLeft + releaseAreaLeft); data.releasePosition = data.desiredPosition - data.desiredVelocity * releaseAreaLeft; if (attackAreaLeft) data.attackPosition = data.startPosition + data.desiredVelocity * attackAreaLeft; else data.attackPosition = data.releasePosition - (data.animationTime - data.releaseTime - data.attackTime) * data.desiredVelocity; if (sustainTimeLeft) { double roundOff = data.releasePosition - ((attackAreaLeft ? data.attackPosition : data.currentPosition) + data.desiredVelocity * sustainTimeLeft); data.desiredVelocity += roundOff / sustainTimeLeft; } return true; }