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