1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "cc/animation/scrollbar_animation_controller_thinning.h" 6 7 #include <algorithm> 8 9 #include "base/time/time.h" 10 #include "cc/layers/layer_impl.h" 11 #include "cc/layers/scrollbar_layer_impl_base.h" 12 13 namespace { 14 const float kIdleThicknessScale = 0.4f; 15 const float kIdleOpacity = 0.7f; 16 const float kDefaultMouseMoveDistanceToTriggerAnimation = 25.f; 17 const int kDefaultAnimationDelay = 500; 18 const int kDefaultAnimationDuration = 300; 19 } 20 21 namespace cc { 22 23 scoped_ptr<ScrollbarAnimationControllerThinning> 24 ScrollbarAnimationControllerThinning::Create(LayerImpl* scroll_layer) { 25 return make_scoped_ptr(new ScrollbarAnimationControllerThinning( 26 scroll_layer, 27 base::TimeDelta::FromMilliseconds(kDefaultAnimationDelay), 28 base::TimeDelta::FromMilliseconds(kDefaultAnimationDuration))); 29 } 30 31 scoped_ptr<ScrollbarAnimationControllerThinning> 32 ScrollbarAnimationControllerThinning::CreateForTest(LayerImpl* scroll_layer, 33 base::TimeDelta animation_delay, base::TimeDelta animation_duration) { 34 return make_scoped_ptr(new ScrollbarAnimationControllerThinning( 35 scroll_layer, animation_delay, animation_duration)); 36 } 37 38 ScrollbarAnimationControllerThinning::ScrollbarAnimationControllerThinning( 39 LayerImpl* scroll_layer, 40 base::TimeDelta animation_delay, 41 base::TimeDelta animation_duration) 42 : ScrollbarAnimationController(), 43 scroll_layer_(scroll_layer), 44 mouse_is_over_scrollbar_(false), 45 mouse_is_near_scrollbar_(false), 46 thickness_change_(NONE), 47 opacity_change_(NONE), 48 should_delay_animation_(false), 49 animation_delay_(animation_delay), 50 animation_duration_(animation_duration), 51 mouse_move_distance_to_trigger_animation_( 52 kDefaultMouseMoveDistanceToTriggerAnimation) { 53 ApplyOpacityAndThumbThicknessScale(kIdleOpacity, kIdleThicknessScale); 54 } 55 56 ScrollbarAnimationControllerThinning::~ScrollbarAnimationControllerThinning() { 57 } 58 59 bool ScrollbarAnimationControllerThinning::IsAnimating() const { 60 return !last_awaken_time_.is_null(); 61 } 62 63 base::TimeDelta ScrollbarAnimationControllerThinning::DelayBeforeStart( 64 base::TimeTicks now) const { 65 if (!should_delay_animation_) 66 return base::TimeDelta(); 67 if (now > last_awaken_time_ + animation_delay_) 68 return base::TimeDelta(); 69 return animation_delay_ - (now - last_awaken_time_); 70 } 71 72 bool ScrollbarAnimationControllerThinning::Animate(base::TimeTicks now) { 73 float progress = AnimationProgressAtTime(now); 74 float opacity = OpacityAtAnimationProgress(progress); 75 float thumb_thickness_scale = ThumbThicknessScaleAtAnimationProgress( 76 progress); 77 ApplyOpacityAndThumbThicknessScale(opacity, thumb_thickness_scale); 78 if (progress == 1.f) { 79 opacity_change_ = NONE; 80 thickness_change_ = NONE; 81 last_awaken_time_ = base::TimeTicks(); 82 } 83 return IsAnimating() && DelayBeforeStart(now) == base::TimeDelta(); 84 } 85 86 void ScrollbarAnimationControllerThinning::DidScrollGestureBegin() { 87 } 88 89 void ScrollbarAnimationControllerThinning::DidScrollGestureEnd( 90 base::TimeTicks now) { 91 } 92 93 void ScrollbarAnimationControllerThinning::DidMouseMoveOffScrollbar( 94 base::TimeTicks now) { 95 mouse_is_over_scrollbar_ = false; 96 mouse_is_near_scrollbar_ = false; 97 last_awaken_time_ = now; 98 should_delay_animation_ = false; 99 opacity_change_ = DECREASE; 100 thickness_change_ = DECREASE; 101 } 102 103 bool ScrollbarAnimationControllerThinning::DidScrollUpdate( 104 base::TimeTicks now) { 105 ApplyOpacityAndThumbThicknessScale( 106 1, mouse_is_near_scrollbar_ ? 1.f : kIdleThicknessScale); 107 108 last_awaken_time_ = now; 109 should_delay_animation_ = true; 110 if (!mouse_is_over_scrollbar_) 111 opacity_change_ = DECREASE; 112 return true; 113 } 114 115 bool ScrollbarAnimationControllerThinning::DidMouseMoveNear( 116 base::TimeTicks now, float distance) { 117 bool mouse_is_over_scrollbar = distance == 0.0; 118 bool mouse_is_near_scrollbar = 119 distance < mouse_move_distance_to_trigger_animation_; 120 121 if (mouse_is_over_scrollbar == mouse_is_over_scrollbar_ && 122 mouse_is_near_scrollbar == mouse_is_near_scrollbar_) 123 return false; 124 125 if (mouse_is_over_scrollbar_ != mouse_is_over_scrollbar) { 126 mouse_is_over_scrollbar_ = mouse_is_over_scrollbar; 127 opacity_change_ = mouse_is_over_scrollbar_ ? INCREASE : DECREASE; 128 } 129 130 if (mouse_is_near_scrollbar_ != mouse_is_near_scrollbar) { 131 mouse_is_near_scrollbar_ = mouse_is_near_scrollbar; 132 thickness_change_ = mouse_is_near_scrollbar_ ? INCREASE : DECREASE; 133 } 134 135 last_awaken_time_ = now; 136 should_delay_animation_ = false; 137 return true; 138 } 139 140 float ScrollbarAnimationControllerThinning::AnimationProgressAtTime( 141 base::TimeTicks now) { 142 if (last_awaken_time_.is_null()) 143 return 1; 144 145 base::TimeDelta delta = now - last_awaken_time_; 146 if (should_delay_animation_) 147 delta -= animation_delay_; 148 float progress = delta.InSecondsF() / animation_duration_.InSecondsF(); 149 return std::max(std::min(progress, 1.f), 0.f); 150 } 151 152 float ScrollbarAnimationControllerThinning::OpacityAtAnimationProgress( 153 float progress) { 154 if (opacity_change_ == NONE) 155 return mouse_is_over_scrollbar_ ? 1.f : kIdleOpacity; 156 float factor = opacity_change_ == INCREASE ? progress : (1.f - progress); 157 float ret = ((1.f - kIdleOpacity) * factor) + kIdleOpacity; 158 return ret; 159 } 160 161 float 162 ScrollbarAnimationControllerThinning::ThumbThicknessScaleAtAnimationProgress( 163 float progress) { 164 if (thickness_change_ == NONE) 165 return mouse_is_near_scrollbar_ ? 1.f : kIdleThicknessScale; 166 float factor = thickness_change_ == INCREASE ? progress : (1.f - progress); 167 return ((1.f - kIdleThicknessScale) * factor) + kIdleThicknessScale; 168 } 169 170 float ScrollbarAnimationControllerThinning::AdjustScale( 171 float new_value, 172 float current_value, 173 AnimationChange animation_change) { 174 if (animation_change == INCREASE && current_value > new_value) 175 return current_value; 176 if (animation_change == DECREASE && current_value < new_value) 177 return current_value; 178 return new_value; 179 } 180 181 void ScrollbarAnimationControllerThinning::ApplyOpacityAndThumbThicknessScale( 182 float opacity, float thumb_thickness_scale) { 183 ScrollbarLayerImplBase* horizontal_scrollbar = 184 scroll_layer_->horizontal_scrollbar_layer(); 185 if (horizontal_scrollbar) { 186 horizontal_scrollbar->SetOpacity( 187 AdjustScale(opacity, horizontal_scrollbar->opacity(), opacity_change_)); 188 horizontal_scrollbar->SetThumbThicknessScaleFactor( 189 AdjustScale( 190 thumb_thickness_scale, 191 horizontal_scrollbar->thumb_thickness_scale_factor(), 192 thickness_change_)); 193 } 194 195 ScrollbarLayerImplBase* vertical_scrollbar = 196 scroll_layer_->vertical_scrollbar_layer(); 197 if (vertical_scrollbar) { 198 vertical_scrollbar->SetOpacity( 199 AdjustScale(opacity, vertical_scrollbar->opacity(), opacity_change_)); 200 vertical_scrollbar->SetThumbThicknessScaleFactor( 201 AdjustScale( 202 thumb_thickness_scale, 203 vertical_scrollbar->thumb_thickness_scale_factor(), 204 thickness_change_)); 205 } 206 } 207 208 } // namespace cc 209