page_scale_animation.cc 8.8 KB

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