| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2011, Google Inc. All rights reserved. | 2 * Copyright (c) 2011, Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #include "config.h" | 31 #include "config.h" |
| 32 #include "platform/scroll/ScrollAnimator.h" | 32 #include "platform/scroll/ScrollAnimator.h" |
| 33 | 33 |
| 34 #include "platform/TraceEvent.h" | 34 #include "platform/TraceEvent.h" |
| 35 #include "platform/graphics/GraphicsLayer.h" |
| 35 #include "platform/scroll/ScrollableArea.h" | 36 #include "platform/scroll/ScrollableArea.h" |
| 36 #include "public/platform/Platform.h" | 37 #include "public/platform/Platform.h" |
| 38 #include "public/platform/WebCompositorAnimation.h" |
| 37 #include "public/platform/WebCompositorSupport.h" | 39 #include "public/platform/WebCompositorSupport.h" |
| 38 #include "wtf/CurrentTime.h" | 40 #include "wtf/CurrentTime.h" |
| 39 #include "wtf/PassRefPtr.h" | 41 #include "wtf/PassRefPtr.h" |
| 40 #include <algorithm> | |
| 41 | 42 |
| 42 namespace blink { | 43 namespace blink { |
| 43 | 44 |
| 44 PassOwnPtrWillBeRawPtr<ScrollAnimatorBase> ScrollAnimatorBase::create(Scrollable
Area* scrollableArea) | 45 PassOwnPtrWillBeRawPtr<ScrollAnimatorBase> ScrollAnimatorBase::create(Scrollable
Area* scrollableArea) |
| 45 { | 46 { |
| 46 if (scrollableArea && scrollableArea->scrollAnimatorEnabled()) | 47 if (scrollableArea && scrollableArea->scrollAnimatorEnabled()) |
| 47 return adoptPtrWillBeNoop(new ScrollAnimator(scrollableArea)); | 48 return adoptPtrWillBeNoop(new ScrollAnimator(scrollableArea)); |
| 48 return adoptPtrWillBeNoop(new ScrollAnimatorBase(scrollableArea)); | 49 return adoptPtrWillBeNoop(new ScrollAnimatorBase(scrollableArea)); |
| 49 } | 50 } |
| 50 | 51 |
| 51 ScrollAnimator::ScrollAnimator(ScrollableArea* scrollableArea, WTF::TimeFunction
timeFunction) | 52 ScrollAnimator::ScrollAnimator(ScrollableArea* scrollableArea, WTF::TimeFunction
timeFunction) |
| 52 : ScrollAnimatorBase(scrollableArea) | 53 : ScrollAnimatorBase(scrollableArea) |
| 54 , m_lastTickTime(0.0) |
| 53 , m_timeFunction(timeFunction) | 55 , m_timeFunction(timeFunction) |
| 54 { | 56 { |
| 55 } | 57 } |
| 56 | 58 |
| 57 ScrollAnimator::~ScrollAnimator() | 59 ScrollAnimator::~ScrollAnimator() |
| 58 { | 60 { |
| 59 cancelAnimations(); | |
| 60 } | 61 } |
| 61 | 62 |
| 62 FloatPoint ScrollAnimator::desiredTargetPosition() const | 63 FloatPoint ScrollAnimator::desiredTargetPosition() const |
| 63 { | 64 { |
| 64 return m_animationCurve ? FloatPoint(m_animationCurve->targetValue()) : curr
entPosition(); | 65 return m_animationCurve ? m_targetOffset : currentPosition(); |
| 65 } | 66 } |
| 66 | 67 |
| 67 float ScrollAnimator::computeDeltaToConsume(ScrollbarOrientation orientation, fl
oat pixelDelta) const | 68 float ScrollAnimator::computeDeltaToConsume( |
| 69 ScrollbarOrientation orientation, float pixelDelta) const |
| 68 { | 70 { |
| 69 FloatPoint pos = desiredTargetPosition(); | 71 FloatPoint pos = desiredTargetPosition(); |
| 70 float currentPos = (orientation == HorizontalScrollbar) ? pos.x() : pos.y(); | 72 float currentPos = (orientation == HorizontalScrollbar) ? pos.x() : pos.y(); |
| 71 float newPos = clampScrollPosition(orientation, currentPos + pixelDelta); | 73 float newPos = clampScrollPosition(orientation, currentPos + pixelDelta); |
| 72 return (currentPos == newPos) ? 0.0f : (newPos - currentPos); | 74 return (currentPos == newPos) ? 0.0f : (newPos - currentPos); |
| 73 } | 75 } |
| 74 | 76 |
| 75 ScrollResultOneDimensional ScrollAnimator::userScroll(ScrollbarOrientation orien
tation, ScrollGranularity granularity, float step, float delta) | 77 void ScrollAnimator::resetAnimationState() |
| 78 { |
| 79 ScrollAnimatorCompositorCoordinator::resetAnimationState(); |
| 80 if (m_animationCurve) |
| 81 m_animationCurve.clear(); |
| 82 m_startTime = 0.0; |
| 83 } |
| 84 |
| 85 ScrollResultOneDimensional ScrollAnimator::userScroll( |
| 86 ScrollbarOrientation orientation, ScrollGranularity granularity, float step,
float delta) |
| 76 { | 87 { |
| 77 if (!m_scrollableArea->scrollAnimatorEnabled()) | 88 if (!m_scrollableArea->scrollAnimatorEnabled()) |
| 78 return ScrollAnimatorBase::userScroll(orientation, granularity, step, de
lta); | 89 return ScrollAnimatorBase::userScroll(orientation, granularity, step, de
lta); |
| 79 | 90 |
| 80 TRACE_EVENT0("blink", "ScrollAnimator::scroll"); | 91 TRACE_EVENT0("blink", "ScrollAnimator::scroll"); |
| 81 | 92 |
| 82 if (granularity == ScrollByPrecisePixel) | 93 if (granularity == ScrollByPrecisePixel) |
| 83 return ScrollAnimatorBase::userScroll(orientation, granularity, step, de
lta); | 94 return ScrollAnimatorBase::userScroll(orientation, granularity, step, de
lta); |
| 84 | 95 |
| 85 float usedPixelDelta = computeDeltaToConsume(orientation, step * delta); | 96 float usedPixelDelta = computeDeltaToConsume(orientation, step * delta); |
| 86 FloatPoint pixelDelta = (orientation == VerticalScrollbar ? FloatPoint(0, us
edPixelDelta) : FloatPoint(usedPixelDelta, 0)); | 97 FloatPoint pixelDelta = (orientation == VerticalScrollbar |
| 98 ? FloatPoint(0, usedPixelDelta) : FloatPoint(usedPixelDelta, 0)); |
| 87 | 99 |
| 88 FloatPoint targetPos = desiredTargetPosition(); | 100 FloatPoint targetPos = desiredTargetPosition(); |
| 89 targetPos.moveBy(pixelDelta); | 101 targetPos.moveBy(pixelDelta); |
| 90 | 102 |
| 91 if (m_animationCurve) { | 103 if (m_animationCurve) { |
| 92 if (!(targetPos - m_animationCurve->targetValue()).isZero()) | 104 if ((targetPos - m_targetOffset).isZero()) { |
| 93 m_animationCurve->updateTarget(m_timeFunction() - m_startTime, targe
tPos); | 105 // Report unused delta only if there is no animation running. See |
| 106 // comment below regarding scroll latching. |
| 107 return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScr
ollDelta */ 0); |
| 108 } |
| 109 |
| 110 m_targetOffset = targetPos; |
| 111 ASSERT(m_runState == RunState::RunningOnMainThread |
| 112 || m_runState == RunState::RunningOnCompositor |
| 113 || m_runState == RunState::RunningOnCompositorButNeedsUpdate); |
| 114 |
| 115 if (m_runState == RunState::RunningOnCompositor |
| 116 || m_runState == RunState::RunningOnCompositorButNeedsUpdate) { |
| 117 m_runState = RunState::RunningOnCompositorButNeedsUpdate; |
| 118 return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScr
ollDelta */ 0); |
| 119 } |
| 120 |
| 121 // Running on the main thread, simply update the target offset instead |
| 122 // of sending to the compositor. |
| 123 m_animationCurve->updateTarget(m_timeFunction() - m_startTime, targetPos
); |
| 94 return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollD
elta */ 0); | 124 return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollD
elta */ 0); |
| 95 } | 125 } |
| 96 | 126 |
| 97 if ((targetPos - currentPosition()).isZero()) { | 127 if ((targetPos - currentPosition()).isZero()) { |
| 98 // Report unused delta only if there is no animation and we are not | 128 // Report unused delta only if there is no animation and we are not |
| 99 // starting one. This ensures we latch for the duration of the | 129 // starting one. This ensures we latch for the duration of the |
| 100 // animation rather than animating multiple scrollers at the same time. | 130 // animation rather than animating multiple scrollers at the same time. |
| 101 return ScrollResultOneDimensional(/* didScroll */ false, delta); | 131 return ScrollResultOneDimensional(/* didScroll */ false, delta); |
| 102 } | 132 } |
| 103 | 133 |
| 104 m_animationCurve = adoptPtr(Platform::current()->compositorSupport()->create
ScrollOffsetAnimationCurve( | 134 m_targetOffset = targetPos; |
| 105 targetPos, | |
| 106 WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut, | |
| 107 WebScrollOffsetAnimationCurve::ScrollDurationConstant)); | |
| 108 | |
| 109 m_animationCurve->setInitialValue(currentPosition()); | |
| 110 m_startTime = m_timeFunction(); | 135 m_startTime = m_timeFunction(); |
| 111 | 136 |
| 112 scrollableArea()->registerForAnimation(); | 137 scrollableArea()->registerForAnimation(); |
| 113 animationTimerFired(); | 138 if (!m_scrollableArea->scheduleAnimation()) { |
| 139 scrollToOffsetWithoutAnimation(targetPos); |
| 140 resetAnimationState(); |
| 141 return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollD
elta */ 0); |
| 142 } |
| 143 |
| 144 m_runState = RunState::WaitingToSendToCompositor; |
| 114 return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta
*/ 0); | 145 return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta
*/ 0); |
| 115 } | 146 } |
| 116 | 147 |
| 117 void ScrollAnimator::scrollToOffsetWithoutAnimation(const FloatPoint& offset) | 148 void ScrollAnimator::scrollToOffsetWithoutAnimation(const FloatPoint& offset) |
| 118 { | 149 { |
| 119 m_currentPosX = offset.x(); | 150 m_currentPosX = offset.x(); |
| 120 m_currentPosY = offset.y(); | 151 m_currentPosY = offset.y(); |
| 121 | 152 |
| 122 cancelAnimations(); | 153 resetAnimationState(); |
| 123 notifyPositionChanged(); | 154 notifyPositionChanged(); |
| 124 } | 155 } |
| 125 | 156 |
| 126 void ScrollAnimator::cancelAnimations() | 157 void ScrollAnimator::tickAnimation(double monotonicTime) |
| 127 { | 158 { |
| 128 if (m_animationCurve) | 159 m_lastTickTime = monotonicTime; |
| 129 m_animationCurve.clear(); | |
| 130 } | |
| 131 | 160 |
| 132 void ScrollAnimator::serviceScrollAnimations() | 161 if (m_runState != RunState::RunningOnMainThread) |
| 133 { | 162 return; |
| 134 if (hasRunningAnimation()) | |
| 135 animationTimerFired(); | |
| 136 } | |
| 137 | 163 |
| 138 bool ScrollAnimator::hasRunningAnimation() const | 164 TRACE_EVENT0("blink", "ScrollAnimator::tickAnimation"); |
| 139 { | 165 double elapsedTime = monotonicTime - m_startTime; |
| 140 return m_animationCurve; | |
| 141 } | |
| 142 | |
| 143 void ScrollAnimator::animationTimerFired() | |
| 144 { | |
| 145 TRACE_EVENT0("blink", "ScrollAnimator::animationTimerFired"); | |
| 146 double elapsedTime = m_timeFunction() - m_startTime; | |
| 147 | 166 |
| 148 bool isFinished = (elapsedTime > m_animationCurve->duration()); | 167 bool isFinished = (elapsedTime > m_animationCurve->duration()); |
| 149 FloatPoint offset = isFinished ? m_animationCurve->targetValue() : m_animati
onCurve->getValue(elapsedTime); | 168 FloatPoint offset = isFinished ? m_animationCurve->targetValue() |
| 169 : m_animationCurve->getValue(elapsedTime); |
| 150 | 170 |
| 151 offset = FloatPoint(m_scrollableArea->clampScrollPosition(offset)); | 171 offset = FloatPoint(m_scrollableArea->clampScrollPosition(offset)); |
| 152 | 172 |
| 153 m_currentPosX = offset.x(); | 173 m_currentPosX = offset.x(); |
| 154 m_currentPosY = offset.y(); | 174 m_currentPosY = offset.y(); |
| 155 | 175 |
| 156 if (isFinished) | 176 if (isFinished) |
| 157 m_animationCurve.clear(); | 177 resetAnimationState(); |
| 158 else | 178 else |
| 159 scrollableArea()->scheduleAnimation(); | 179 scrollableArea()->scheduleAnimation(); |
| 160 | 180 |
| 161 TRACE_EVENT0("blink", "ScrollAnimator::notifyPositionChanged"); | 181 TRACE_EVENT0("blink", "ScrollAnimator::notifyPositionChanged"); |
| 162 notifyPositionChanged(); | 182 notifyPositionChanged(); |
| 163 } | 183 } |
| 164 | 184 |
| 185 void ScrollAnimator::updateCompositorAnimations() |
| 186 { |
| 187 if (m_compositorAnimationId && m_runState != RunState::RunningOnCompositor |
| 188 && m_runState != RunState::RunningOnCompositorButNeedsUpdate) { |
| 189 // If the current run state is WaitingToSendToCompositor but we have a |
| 190 // non-zero compositor animation id, there's a currently running |
| 191 // compositor animation that needs to be removed here before the new |
| 192 // animation is added below. |
| 193 ASSERT(m_runState == RunState::WaitingToCancelOnCompositor |
| 194 || m_runState == RunState::WaitingToSendToCompositor); |
| 195 |
| 196 abortAnimation(); |
| 197 |
| 198 m_compositorAnimationId = 0; |
| 199 m_compositorAnimationGroupId = 0; |
| 200 if (m_runState == RunState::WaitingToCancelOnCompositor) { |
| 201 resetAnimationState(); |
| 202 return; |
| 203 } |
| 204 } |
| 205 |
| 206 if (m_runState == RunState::WaitingToSendToCompositor |
| 207 || m_runState == RunState::RunningOnCompositorButNeedsUpdate) { |
| 208 if (m_runState == RunState::RunningOnCompositorButNeedsUpdate) { |
| 209 // Abort the running animation before a new one with an updated |
| 210 // target is added. |
| 211 abortAnimation(); |
| 212 |
| 213 m_compositorAnimationId = 0; |
| 214 m_compositorAnimationGroupId = 0; |
| 215 |
| 216 m_animationCurve->updateTarget(m_lastTickTime - m_startTime, |
| 217 m_targetOffset); |
| 218 m_runState = RunState::WaitingToSendToCompositor; |
| 219 } |
| 220 |
| 221 if (!m_animationCurve) { |
| 222 m_animationCurve = adoptPtr(Platform::current()->compositorSupport() |
| 223 ->createScrollOffsetAnimationCurve( |
| 224 m_targetOffset, |
| 225 WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut, |
| 226 WebScrollOffsetAnimationCurve::ScrollDurationConstant)); |
| 227 m_animationCurve->setInitialValue(currentPosition()); |
| 228 } |
| 229 |
| 230 bool sentToCompositor = false; |
| 231 if (GraphicsLayer* layer = m_scrollableArea->layerForScrolling()) { |
| 232 ASSERT(layer->scrollableArea() == m_scrollableArea); |
| 233 if (!layer->platformLayer()->shouldScrollOnMainThread()) { |
| 234 OwnPtr<WebCompositorAnimation> animation = adoptPtr( |
| 235 Platform::current()->compositorSupport()->createAnimation( |
| 236 *m_animationCurve, |
| 237 WebCompositorAnimation::TargetPropertyScrollOffset)); |
| 238 // Being here means that either there is an animation that needs |
| 239 // to be sent to the compositor, or an animation that needs to |
| 240 // be updated (a new scroll event before the previous animation |
| 241 // is finished). In either case, the start time is when the |
| 242 // first animation was initiated. This re-targets the animation |
| 243 // using the current time on main thread. |
| 244 animation->setStartTime(m_startTime); |
| 245 |
| 246 int animationId = animation->id(); |
| 247 int animationGroupId = animation->group(); |
| 248 |
| 249 sentToCompositor = addAnimation(animation.release()); |
| 250 if (sentToCompositor) { |
| 251 m_runState = RunState::RunningOnCompositor; |
| 252 m_compositorAnimationId = animationId; |
| 253 m_compositorAnimationGroupId = animationGroupId; |
| 254 } |
| 255 } |
| 256 } |
| 257 |
| 258 if (!sentToCompositor) { |
| 259 m_runState = RunState::RunningOnMainThread; |
| 260 if (!m_scrollableArea->scheduleAnimation()) { |
| 261 scrollToOffsetWithoutAnimation(m_targetOffset); |
| 262 resetAnimationState(); |
| 263 } |
| 264 } |
| 265 } |
| 266 } |
| 267 |
| 268 void ScrollAnimator::notifyCompositorAnimationFinished(int groupId) |
| 269 { |
| 270 ScrollAnimatorCompositorCoordinator::compositorAnimationFinished(groupId); |
| 271 } |
| 272 |
| 273 void ScrollAnimator::cancelAnimation() |
| 274 { |
| 275 ScrollAnimatorCompositorCoordinator::cancelAnimation(); |
| 276 } |
| 277 |
| 278 void ScrollAnimator::layerForCompositedScrollingDidChange( |
| 279 WebCompositorAnimationTimeline* timeline) |
| 280 { |
| 281 reattachCompositorPlayerIfNeeded(timeline); |
| 282 } |
| 283 |
| 165 DEFINE_TRACE(ScrollAnimator) | 284 DEFINE_TRACE(ScrollAnimator) |
| 166 { | 285 { |
| 167 ScrollAnimatorBase::trace(visitor); | 286 ScrollAnimatorBase::trace(visitor); |
| 168 } | 287 } |
| 169 | 288 |
| 170 } // namespace blink | 289 } // namespace blink |
| OLD | NEW |