1 // Copyright 2012 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/input/page_scale_animation.h" 6 7 #include <math.h> 8 9 #include "base/logging.h" 10 #include "cc/animation/timing_function.h" 11 #include "ui/gfx/point_f.h" 12 #include "ui/gfx/rect_f.h" 13 #include "ui/gfx/vector2d_conversions.h" 14 15 namespace { 16 17 // This takes a viewport-relative vector and returns a vector whose values are 18 // between 0 and 1, representing the percentage position within the viewport. 19 gfx::Vector2dF NormalizeFromViewport(gfx::Vector2dF denormalized, 20 gfx::SizeF viewport_size) { 21 return gfx::ScaleVector2d(denormalized, 22 1.f / viewport_size.width(), 23 1.f / viewport_size.height()); 24 } 25 26 gfx::Vector2dF DenormalizeToViewport(gfx::Vector2dF normalized, 27 gfx::SizeF viewport_size) { 28 return gfx::ScaleVector2d(normalized, 29 viewport_size.width(), 30 viewport_size.height()); 31 } 32 33 gfx::Vector2dF InterpolateBetween(gfx::Vector2dF start, 34 gfx::Vector2dF end, 35 float interp) { 36 return start + gfx::ScaleVector2d(end - start, interp); 37 } 38 39 } // namespace 40 41 namespace cc { 42 43 scoped_ptr<PageScaleAnimation> PageScaleAnimation::Create( 44 gfx::Vector2dF start_scroll_offset, 45 float start_page_scale_factor, 46 gfx::SizeF viewport_size, 47 gfx::SizeF root_layer_size, 48 scoped_ptr<TimingFunction> timing_function) { 49 return make_scoped_ptr(new PageScaleAnimation(start_scroll_offset, 50 start_page_scale_factor, 51 viewport_size, 52 root_layer_size, 53 timing_function.Pass())); 54 } 55 56 PageScaleAnimation::PageScaleAnimation( 57 gfx::Vector2dF start_scroll_offset, 58 float start_page_scale_factor, 59 gfx::SizeF viewport_size, 60 gfx::SizeF root_layer_size, 61 scoped_ptr<TimingFunction> timing_function) 62 : start_page_scale_factor_(start_page_scale_factor), 63 target_page_scale_factor_(0.f), 64 start_scroll_offset_(start_scroll_offset), 65 start_anchor_(), 66 target_anchor_(), 67 viewport_size_(viewport_size), 68 root_layer_size_(root_layer_size), 69 start_time_(-1.0), 70 duration_(0.0), 71 timing_function_(timing_function.Pass()) {} 72 73 PageScaleAnimation::~PageScaleAnimation() {} 74 75 void PageScaleAnimation::ZoomTo(gfx::Vector2dF target_scroll_offset, 76 float target_page_scale_factor, 77 double duration) { 78 target_page_scale_factor_ = target_page_scale_factor; 79 target_scroll_offset_ = target_scroll_offset; 80 ClampTargetScrollOffset(); 81 duration_ = duration; 82 83 if (start_page_scale_factor_ == target_page_scale_factor) { 84 start_anchor_ = start_scroll_offset_; 85 target_anchor_ = target_scroll_offset; 86 return; 87 } 88 89 // For uniform-looking zooming, infer an anchor from the start and target 90 // viewport rects. 91 InferTargetAnchorFromScrollOffsets(); 92 start_anchor_ = target_anchor_; 93 } 94 95 void PageScaleAnimation::ZoomWithAnchor(gfx::Vector2dF anchor, 96 float target_page_scale_factor, 97 double duration) { 98 start_anchor_ = anchor; 99 target_page_scale_factor_ = target_page_scale_factor; 100 duration_ = duration; 101 102 // We start zooming out from the anchor tapped by the user. But if 103 // the target scale is impossible to attain without hitting the root layer 104 // edges, then infer an anchor that doesn't collide with the edges. 105 // We will interpolate between the two anchors during the animation. 106 InferTargetScrollOffsetFromStartAnchor(); 107 ClampTargetScrollOffset(); 108 109 if (start_page_scale_factor_ == target_page_scale_factor_) { 110 target_anchor_ = start_anchor_; 111 return; 112 } 113 InferTargetAnchorFromScrollOffsets(); 114 } 115 116 void PageScaleAnimation::InferTargetScrollOffsetFromStartAnchor() { 117 gfx::Vector2dF normalized = NormalizeFromViewport( 118 start_anchor_ - start_scroll_offset_, StartViewportSize()); 119 target_scroll_offset_ = 120 start_anchor_ - DenormalizeToViewport(normalized, TargetViewportSize()); 121 } 122 123 void PageScaleAnimation::InferTargetAnchorFromScrollOffsets() { 124 // The anchor is the point which is at the same normalized relative position 125 // within both start viewport rect and target viewport rect. For example, a 126 // zoom-in double-tap to a perfectly centered rect will have normalized 127 // anchor (0.5, 0.5), while one to a rect touching the bottom-right of the 128 // screen will have normalized anchor (1.0, 1.0). In other words, it obeys 129 // the equations: 130 // anchor = start_size * normalized + start_offset 131 // anchor = target_size * normalized + target_offset 132 // where both anchor and normalized begin as unknowns. Solving 133 // for the normalized, we get the following: 134 float width_scale = 135 1.f / (TargetViewportSize().width() - StartViewportSize().width()); 136 float height_scale = 137 1.f / (TargetViewportSize().height() - StartViewportSize().height()); 138 gfx::Vector2dF normalized = gfx::ScaleVector2d( 139 start_scroll_offset_ - target_scroll_offset_, width_scale, height_scale); 140 target_anchor_ = 141 target_scroll_offset_ + DenormalizeToViewport(normalized, 142 TargetViewportSize()); 143 } 144 145 void PageScaleAnimation::ClampTargetScrollOffset() { 146 gfx::Vector2dF max_scroll_offset = 147 gfx::RectF(root_layer_size_).bottom_right() - 148 gfx::RectF(TargetViewportSize()).bottom_right(); 149 target_scroll_offset_.SetToMax(gfx::Vector2dF()); 150 target_scroll_offset_.SetToMin(max_scroll_offset); 151 } 152 153 gfx::SizeF PageScaleAnimation::StartViewportSize() const { 154 return gfx::ScaleSize(viewport_size_, 1.f / start_page_scale_factor_); 155 } 156 157 gfx::SizeF PageScaleAnimation::TargetViewportSize() const { 158 return gfx::ScaleSize(viewport_size_, 1.f / target_page_scale_factor_); 159 } 160 161 gfx::SizeF PageScaleAnimation::ViewportSizeAt(float interp) const { 162 return gfx::ScaleSize(viewport_size_, 1.f / PageScaleFactorAt(interp)); 163 } 164 165 bool PageScaleAnimation::IsAnimationStarted() const { 166 return start_time_ >= 0; 167 } 168 169 void PageScaleAnimation::StartAnimation(double time) { 170 DCHECK_GT(0, start_time_); 171 start_time_ = time; 172 } 173 174 gfx::Vector2dF PageScaleAnimation::ScrollOffsetAtTime(double time) const { 175 DCHECK_GE(start_time_, 0); 176 return ScrollOffsetAt(InterpAtTime(time)); 177 } 178 179 float PageScaleAnimation::PageScaleFactorAtTime(double time) const { 180 DCHECK_GE(start_time_, 0); 181 return PageScaleFactorAt(InterpAtTime(time)); 182 } 183 184 bool PageScaleAnimation::IsAnimationCompleteAtTime(double time) const { 185 DCHECK_GE(start_time_, 0); 186 return time >= end_time(); 187 } 188 189 float PageScaleAnimation::InterpAtTime(double time) const { 190 DCHECK_GE(start_time_, 0); 191 DCHECK_GE(time, start_time_); 192 if (IsAnimationCompleteAtTime(time)) 193 return 1.f; 194 195 const double normalized_time = (time - start_time_) / duration_; 196 return timing_function_->GetValue(normalized_time); 197 } 198 199 gfx::Vector2dF PageScaleAnimation::ScrollOffsetAt(float interp) const { 200 if (interp <= 0.f) 201 return start_scroll_offset_; 202 if (interp >= 1.f) 203 return target_scroll_offset_; 204 205 return AnchorAt(interp) - ViewportRelativeAnchorAt(interp); 206 } 207 208 gfx::Vector2dF PageScaleAnimation::AnchorAt(float interp) const { 209 // Interpolate from start to target anchor in absolute space. 210 return InterpolateBetween(start_anchor_, target_anchor_, interp); 211 } 212 213 gfx::Vector2dF PageScaleAnimation::ViewportRelativeAnchorAt( 214 float interp) const { 215 // Interpolate from start to target anchor in normalized space. 216 gfx::Vector2dF start_normalized = 217 NormalizeFromViewport(start_anchor_ - start_scroll_offset_, 218 StartViewportSize()); 219 gfx::Vector2dF target_normalized = 220 NormalizeFromViewport(target_anchor_ - target_scroll_offset_, 221 TargetViewportSize()); 222 gfx::Vector2dF interp_normalized = 223 InterpolateBetween(start_normalized, target_normalized, interp); 224 225 return DenormalizeToViewport(interp_normalized, ViewportSizeAt(interp)); 226 } 227 228 float PageScaleAnimation::PageScaleFactorAt(float interp) const { 229 if (interp <= 0.f) 230 return start_page_scale_factor_; 231 if (interp >= 1.f) 232 return target_page_scale_factor_; 233 234 // Linearly interpolate the magnitude in log scale. 235 float diff = target_page_scale_factor_ / start_page_scale_factor_; 236 float log_diff = log(diff); 237 log_diff *= interp; 238 diff = exp(log_diff); 239 return start_page_scale_factor_ * diff; 240 } 241 242 } // namespace cc 243