void ProgrammaticScrollAnimator::updateCompositorAnimations() { if (m_runState == RunState::PostAnimationCleanup) { // No special cleanup, simply reset animation state. We have this state // here because the state machine is shared with ScrollAnimator which // has to do some cleanup that requires the compositing state to be clean. return resetAnimationState(); } if (m_compositorAnimationId && m_runState != RunState::RunningOnCompositor) { // If the current run state is WaitingToSendToCompositor but we have a // non-zero compositor animation id, there's a currently running // compositor animation that needs to be removed here before the new // animation is added below. ASSERT(m_runState == RunState::WaitingToCancelOnCompositor || m_runState == RunState::WaitingToSendToCompositor); removeAnimation(); m_compositorAnimationId = 0; m_compositorAnimationGroupId = 0; if (m_runState == RunState::WaitingToCancelOnCompositor) { resetAnimationState(); return; } } if (m_runState == RunState::WaitingToSendToCompositor) { if (!m_compositorAnimationAttachedToElementId) reattachCompositorPlayerIfNeeded( getScrollableArea()->compositorAnimationTimeline()); bool sentToCompositor = false; if (!m_scrollableArea->shouldScrollOnMainThread()) { std::unique_ptr<CompositorAnimation> animation = CompositorAnimation::create( *m_animationCurve, CompositorTargetProperty::SCROLL_OFFSET, 0, 0); int animationId = animation->id(); int animationGroupId = animation->group(); if (addAnimation(std::move(animation))) { sentToCompositor = true; m_runState = RunState::RunningOnCompositor; m_compositorAnimationId = animationId; m_compositorAnimationGroupId = animationGroupId; } } if (!sentToCompositor) { m_runState = RunState::RunningOnMainThread; m_animationCurve->setInitialValue( compositorOffsetFromBlinkOffset(m_scrollableArea->getScrollOffset())); if (!m_scrollableArea->scheduleAnimation()) { notifyOffsetChanged(m_targetOffset); resetAnimationState(); } } } }
void ScrollAnimatorCompositorCoordinator::compositorAnimationFinished( int groupId) { if (m_compositorAnimationGroupId != groupId) return; m_compositorAnimationId = 0; m_compositorAnimationGroupId = 0; switch (m_runState) { case RunState::Idle: case RunState::PostAnimationCleanup: case RunState::RunningOnMainThread: ASSERT_NOT_REACHED(); break; case RunState::WaitingToSendToCompositor: break; case RunState::RunningOnCompositor: case RunState::RunningOnCompositorButNeedsUpdate: case RunState::RunningOnCompositorButNeedsTakeover: case RunState::WaitingToCancelOnCompositor: m_runState = RunState::PostAnimationCleanup; // Get serviced the next time compositor updates are allowed. if (scrollableArea()) scrollableArea()->registerForAnimation(); else resetAnimationState(); } }
void ScrollAnimatorCompositorCoordinator::cancelAnimation() { switch (m_runState) { case RunState::Idle: case RunState::WaitingToCancelOnCompositor: case RunState::PostAnimationCleanup: break; case RunState::RunningOnCompositorButNeedsTakeover: case RunState::WaitingToSendToCompositor: if (m_compositorAnimationId) { // We still have a previous animation running on the compositor. m_runState = RunState::WaitingToCancelOnCompositor; } else { resetAnimationState(); } break; case RunState::RunningOnMainThread: m_runState = RunState::PostAnimationCleanup; break; case RunState::RunningOnCompositorButNeedsUpdate: case RunState::RunningOnCompositor: m_runState = RunState::WaitingToCancelOnCompositor; // Get serviced the next time compositor updates are allowed. scrollableArea()->registerForAnimation(); } }
void ScrollAnimator::postAnimationCleanupAndReset() { // Remove the temporary main thread scrolling reason that was added while // main thread had scheduled an animation. removeMainThreadScrollingReason(); resetAnimationState(); }
void ScrollAnimator::tickAnimation(double monotonicTime) { if (m_runState != RunState::RunningOnMainThread) return; TRACE_EVENT0("blink", "ScrollAnimator::tickAnimation"); double elapsedTime = monotonicTime - m_startTime; bool isFinished = (elapsedTime > m_animationCurve->duration()); FloatPoint offset = isFinished ? m_animationCurve->targetValue() : m_animationCurve->getValue(elapsedTime); offset = FloatPoint(m_scrollableArea->clampScrollPosition(offset)); m_currentPosX = offset.x(); m_currentPosY = offset.y(); if (isFinished) resetAnimationState(); else scrollableArea()->scheduleAnimation(); TRACE_EVENT0("blink", "ScrollAnimator::notifyPositionChanged"); notifyPositionChanged(); }
void ScrollAnimator::scrollToOffsetWithoutAnimation( const ScrollOffset& offset) { m_currentOffset = offset; resetAnimationState(); notifyOffsetChanged(); }
void ProgrammaticScrollAnimator::animateToOffset(const ScrollOffset& offset) { if (m_runState == RunState::PostAnimationCleanup) resetAnimationState(); m_startTime = 0.0; m_targetOffset = offset; m_animationCurve = CompositorScrollOffsetAnimationCurve::create( compositorOffsetFromBlinkOffset(m_targetOffset), CompositorScrollOffsetAnimationCurve::ScrollDurationDeltaBased); m_scrollableArea->registerForAnimation(); if (!m_scrollableArea->scheduleAnimation()) { resetAnimationState(); notifyOffsetChanged(offset); } m_runState = RunState::WaitingToSendToCompositor; }
void ScrollAnimator::scrollToOffsetWithoutAnimation(const FloatPoint& offset) { m_currentPosX = offset.x(); m_currentPosY = offset.y(); resetAnimationState(); notifyPositionChanged(); }
void ProgrammaticScrollAnimator::updateCompositorAnimations() { if (m_compositorAnimationId && m_runState != RunState::RunningOnCompositor) { // If the current run state is WaitingToSendToCompositor but we have a // non-zero compositor animation id, there's a currently running // compositor animation that needs to be removed here before the new // animation is added below. ASSERT(m_runState == RunState::WaitingToCancelOnCompositor || m_runState == RunState::WaitingToSendToCompositor); removeAnimation(); m_compositorAnimationId = 0; m_compositorAnimationGroupId = 0; if (m_runState == RunState::WaitingToCancelOnCompositor) { resetAnimationState(); return; } } if (m_runState == RunState::WaitingToSendToCompositor) { bool sentToCompositor = false; if (!m_scrollableArea->shouldScrollOnMainThread()) { OwnPtr<WebCompositorAnimation> animation = adoptPtr(Platform::current()->compositorSupport()->createAnimation(*m_animationCurve, WebCompositorAnimation::TargetPropertyScrollOffset)); int animationId = animation->id(); int animationGroupId = animation->group(); if (addAnimation(animation.release())) { sentToCompositor = true; m_runState = RunState::RunningOnCompositor; m_compositorAnimationId = animationId; m_compositorAnimationGroupId = animationGroupId; } } if (!sentToCompositor) { m_runState = RunState::RunningOnMainThread; m_animationCurve->setInitialValue(FloatPoint(m_scrollableArea->scrollPosition())); if (!m_scrollableArea->scheduleAnimation()) { notifyPositionChanged(IntPoint(m_targetOffset.x(), m_targetOffset.y())); resetAnimationState(); } } } }
bool ScrollAnimator::registerAndScheduleAnimation() { getScrollableArea()->registerForAnimation(); if (!m_scrollableArea->scheduleAnimation()) { scrollToOffsetWithoutAnimation(m_targetOffset); resetAnimationState(); return false; } return true; }
void ProgrammaticScrollAnimator::tickAnimation(double monotonicTime) { if (m_runState != RunState::RunningOnMainThread) return; if (!m_startTime) m_startTime = monotonicTime; double elapsedTime = monotonicTime - m_startTime; bool isFinished = (elapsedTime > m_animationCurve->duration()); FloatPoint offset = m_animationCurve->getValue(elapsedTime); notifyPositionChanged(IntPoint(offset.x(), offset.y())); if (isFinished) { resetAnimationState(); } else if (!m_scrollableArea->scheduleAnimation()) { notifyPositionChanged(IntPoint(m_targetOffset.x(), m_targetOffset.y())); resetAnimationState(); } }
bool ScrollAnimator::willAnimateToOffset(const ScrollOffset& targetOffset) { if (m_runState == RunState::PostAnimationCleanup) resetAnimationState(); if (m_runState == RunState::WaitingToCancelOnCompositor || m_runState == RunState::WaitingToCancelOnCompositorButNewScroll) { ASSERT(m_animationCurve); m_targetOffset = targetOffset; if (registerAndScheduleAnimation()) m_runState = RunState::WaitingToCancelOnCompositorButNewScroll; return true; } if (m_animationCurve) { if ((targetOffset - m_targetOffset).isZero()) return true; m_targetOffset = targetOffset; ASSERT(m_runState == RunState::RunningOnMainThread || m_runState == RunState::RunningOnCompositor || m_runState == RunState::RunningOnCompositorButNeedsUpdate || m_runState == RunState::RunningOnCompositorButNeedsTakeover); // Running on the main thread, simply update the target offset instead // of sending to the compositor. if (m_runState == RunState::RunningOnMainThread) { m_animationCurve->updateTarget( m_timeFunction() - m_startTime, compositorOffsetFromBlinkOffset(targetOffset)); return true; } if (registerAndScheduleAnimation()) m_runState = RunState::RunningOnCompositorButNeedsUpdate; return true; } if ((targetOffset - currentOffset()).isZero()) return false; m_targetOffset = targetOffset; m_startTime = m_timeFunction(); if (registerAndScheduleAnimation()) m_runState = RunState::WaitingToSendToCompositor; return true; }
void ProgrammaticScrollAnimator::animateToOffset(FloatPoint offset) { m_startTime = 0.0; m_targetOffset = offset; m_animationCurve = adoptPtr(Platform::current()->compositorSupport()->createScrollOffsetAnimationCurve( m_targetOffset, WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut, WebScrollOffsetAnimationCurve::ScrollDurationDeltaBased)); m_scrollableArea->registerForAnimation(); if (!m_scrollableArea->scheduleAnimation()) { resetAnimationState(); notifyPositionChanged(IntPoint(offset.x(), offset.y())); } m_runState = RunState::WaitingToSendToCompositor; }
void ProgrammaticScrollAnimator::layerForCompositedScrollingDidChange(WebCompositorAnimationTimeline* timeline) { reattachCompositorPlayerIfNeeded(timeline); // If the composited scrolling layer is lost during a composited animation, // continue the animation on the main thread. if (m_runState == RunState::RunningOnCompositor && !m_scrollableArea->layerForScrolling()) { m_runState = RunState::RunningOnMainThread; m_compositorAnimationId = 0; m_compositorAnimationGroupId = 0; m_animationCurve->setInitialValue(FloatPoint(m_scrollableArea->scrollPosition())); m_scrollableArea->registerForAnimation(); if (!m_scrollableArea->scheduleAnimation()) { resetAnimationState(); notifyPositionChanged(IntPoint(m_targetOffset.x(), m_targetOffset.y())); } } }
void ProgrammaticScrollAnimator::tickAnimation(double monotonicTime) { if (m_runState != RunState::RunningOnMainThread) return; if (!m_startTime) m_startTime = monotonicTime; double elapsedTime = monotonicTime - m_startTime; bool isFinished = (elapsedTime > m_animationCurve->duration()); ScrollOffset offset = blinkOffsetFromCompositorOffset(m_animationCurve->getValue(elapsedTime)); notifyOffsetChanged(offset); if (isFinished) { m_runState = RunState::PostAnimationCleanup; } else if (!m_scrollableArea->scheduleAnimation()) { notifyOffsetChanged(offset); resetAnimationState(); } }
ScrollResult ScrollAnimator::userScroll(ScrollGranularity granularity, const ScrollOffset& delta) { if (!m_scrollableArea->scrollAnimatorEnabled()) return ScrollAnimatorBase::userScroll(granularity, delta); TRACE_EVENT0("blink", "ScrollAnimator::scroll"); if (granularity == ScrollByPrecisePixel) { // Cancel scroll animation because asked to instant scroll. if (hasRunningAnimation()) cancelAnimation(); return ScrollAnimatorBase::userScroll(granularity, delta); } bool needsPostAnimationCleanup = m_runState == RunState::PostAnimationCleanup; if (m_runState == RunState::PostAnimationCleanup) resetAnimationState(); ScrollOffset consumedDelta = computeDeltaToConsume(delta); ScrollOffset targetOffset = desiredTargetOffset(); targetOffset += consumedDelta; if (willAnimateToOffset(targetOffset)) { m_lastGranularity = granularity; // Report unused delta only if there is no animation running. See // comment below regarding scroll latching. // TODO(bokan): Need to standardize how ScrollAnimators report // unusedDelta. This differs from ScrollAnimatorMac currently. return ScrollResult(true, true, 0, 0); } // If the run state when this method was called was PostAnimationCleanup and // we're not starting an animation, stay in PostAnimationCleanup state so // that the main thread scrolling reason can be removed. if (needsPostAnimationCleanup) m_runState = RunState::PostAnimationCleanup; // Report unused delta only if there is no animation and we are not // starting one. This ensures we latch for the duration of the // animation rather than animating multiple scrollers at the same time. return ScrollResult(false, false, delta.width(), delta.height()); }
void ScrollAnimator::updateCompositorAnimations() { if (m_compositorAnimationId && m_runState != RunState::RunningOnCompositor && m_runState != RunState::RunningOnCompositorButNeedsUpdate) { // If the current run state is WaitingToSendToCompositor but we have a // non-zero compositor animation id, there's a currently running // compositor animation that needs to be removed here before the new // animation is added below. ASSERT(m_runState == RunState::WaitingToCancelOnCompositor || m_runState == RunState::WaitingToSendToCompositor); abortAnimation(); m_compositorAnimationId = 0; m_compositorAnimationGroupId = 0; if (m_runState == RunState::WaitingToCancelOnCompositor) { resetAnimationState(); return; } } if (m_runState == RunState::WaitingToSendToCompositor || m_runState == RunState::RunningOnCompositorButNeedsUpdate) { if (m_runState == RunState::RunningOnCompositorButNeedsUpdate) { // Abort the running animation before a new one with an updated // target is added. abortAnimation(); m_compositorAnimationId = 0; m_compositorAnimationGroupId = 0; m_animationCurve->updateTarget(m_timeFunction() - m_startTime, m_targetOffset); m_runState = RunState::WaitingToSendToCompositor; } if (!m_animationCurve) { m_animationCurve = adoptPtr(Platform::current()->compositorSupport() ->createScrollOffsetAnimationCurve( m_targetOffset, WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut, m_lastGranularity == ScrollByPixel ? WebScrollOffsetAnimationCurve::ScrollDurationInverseDelta : WebScrollOffsetAnimationCurve::ScrollDurationConstant)); m_animationCurve->setInitialValue(currentPosition()); } bool sentToCompositor = false; if (!m_scrollableArea->shouldScrollOnMainThread()) { OwnPtr<WebCompositorAnimation> animation = adoptPtr( Platform::current()->compositorSupport()->createAnimation( *m_animationCurve, WebCompositorAnimation::TargetPropertyScrollOffset)); // Being here means that either there is an animation that needs // to be sent to the compositor, or an animation that needs to // be updated (a new scroll event before the previous animation // is finished). In either case, the start time is when the // first animation was initiated. This re-targets the animation // using the current time on main thread. animation->setStartTime(m_startTime); int animationId = animation->id(); int animationGroupId = animation->group(); sentToCompositor = addAnimation(animation.release()); if (sentToCompositor) { m_runState = RunState::RunningOnCompositor; m_compositorAnimationId = animationId; m_compositorAnimationGroupId = animationGroupId; } } if (!sentToCompositor) { if (registerAndScheduleAnimation()) m_runState = RunState::RunningOnMainThread; } } }
ScrollResultOneDimensional ScrollAnimator::userScroll( ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float delta) { if (!m_scrollableArea->scrollAnimatorEnabled()) return ScrollAnimatorBase::userScroll(orientation, granularity, step, delta); TRACE_EVENT0("blink", "ScrollAnimator::scroll"); if (granularity == ScrollByPrecisePixel) { if (hasRunningAnimation()) { abortAnimation(); resetAnimationState(); } return ScrollAnimatorBase::userScroll(orientation, granularity, step, delta); } float usedPixelDelta = computeDeltaToConsume(orientation, step * delta); FloatPoint pixelDelta = (orientation == VerticalScrollbar ? FloatPoint(0, usedPixelDelta) : FloatPoint(usedPixelDelta, 0)); FloatPoint targetPos = desiredTargetPosition(); targetPos.moveBy(pixelDelta); if (m_animationCurve) { if ((targetPos - m_targetOffset).isZero()) { // Report unused delta only if there is no animation running. See // comment below regarding scroll latching. return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta */ 0); } m_targetOffset = targetPos; ASSERT(m_runState == RunState::RunningOnMainThread || m_runState == RunState::RunningOnCompositor || m_runState == RunState::RunningOnCompositorButNeedsUpdate); if (m_runState == RunState::RunningOnCompositor || m_runState == RunState::RunningOnCompositorButNeedsUpdate) { if (registerAndScheduleAnimation()) m_runState = RunState::RunningOnCompositorButNeedsUpdate; return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta */ 0); } // Running on the main thread, simply update the target offset instead // of sending to the compositor. m_animationCurve->updateTarget(m_timeFunction() - m_startTime, targetPos); return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta */ 0); } if ((targetPos - currentPosition()).isZero()) { // Report unused delta only if there is no animation and we are not // starting one. This ensures we latch for the duration of the // animation rather than animating multiple scrollers at the same time. return ScrollResultOneDimensional(/* didScroll */ false, delta); } m_targetOffset = targetPos; m_startTime = m_timeFunction(); m_lastGranularity = granularity; if (registerAndScheduleAnimation()) m_runState = RunState::WaitingToSendToCompositor; return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta */ 0); }