1 // Copyright (c) 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.
5 #include "content/browser/android/overscroll_glow.h"
7 #include "cc/layers/layer.h"
8 #include "content/browser/android/edge_effect_base.h"
17 const float kEpsilon
= 1e-3f
;
19 bool IsApproxZero(float value
) {
20 return std::abs(value
) < kEpsilon
;
23 gfx::Vector2dF
ZeroSmallComponents(gfx::Vector2dF vector
) {
24 if (IsApproxZero(vector
.x()))
26 if (IsApproxZero(vector
.y()))
31 gfx::Transform
ComputeTransform(OverscrollGlow::Edge edge
,
32 const gfx::SizeF
& window_size
,
34 // Transforms assume the edge layers are anchored to their *top center point*.
36 case OverscrollGlow::EDGE_TOP
:
37 return gfx::Transform(1, 0, 0, 1, 0, offset
);
38 case OverscrollGlow::EDGE_LEFT
:
39 return gfx::Transform(0,
43 -window_size
.height() / 2.f
+ offset
,
44 window_size
.height() / 2.f
);
45 case OverscrollGlow::EDGE_BOTTOM
:
46 return gfx::Transform(-1, 0, 0, -1, 0, window_size
.height() + offset
);
47 case OverscrollGlow::EDGE_RIGHT
:
48 return gfx::Transform(
53 -window_size
.height() / 2.f
+ window_size
.width() + offset
,
54 window_size
.height() / 2.f
);
56 NOTREACHED() << "Invalid edge: " << edge
;
57 return gfx::Transform();
61 gfx::SizeF
ComputeSize(OverscrollGlow::Edge edge
,
62 const gfx::SizeF
& window_size
) {
64 case OverscrollGlow::EDGE_TOP
:
65 case OverscrollGlow::EDGE_BOTTOM
:
67 case OverscrollGlow::EDGE_LEFT
:
68 case OverscrollGlow::EDGE_RIGHT
:
69 return gfx::SizeF(window_size
.height(), window_size
.width());
71 NOTREACHED() << "Invalid edge: " << edge
;
78 OverscrollGlow::OverscrollGlow(OverscrollGlowClient
* client
)
79 : client_(client
), edge_offsets_(), initialized_(false) {
83 OverscrollGlow::~OverscrollGlow() {
87 void OverscrollGlow::Reset() {
91 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
)
92 edge_effects_
[i
]->Finish();
95 bool OverscrollGlow::OnOverscrolled(base::TimeTicks current_time
,
96 gfx::Vector2dF accumulated_overscroll
,
97 gfx::Vector2dF overscroll_delta
,
98 gfx::Vector2dF velocity
,
99 gfx::Vector2dF displacement
) {
100 // The size of the glow determines the relative effect of the inputs; an
101 // empty-sized effect is effectively disabled.
102 if (viewport_size_
.IsEmpty())
105 // Ignore sufficiently small values that won't meaningfuly affect animation.
106 overscroll_delta
= ZeroSmallComponents(overscroll_delta
);
107 if (overscroll_delta
.IsZero()) {
109 Release(current_time
);
110 return CheckNeedsAnimate();
113 if (!InitializeIfNecessary())
116 gfx::Vector2dF old_overscroll
= accumulated_overscroll
- overscroll_delta
;
117 bool x_overscroll_started
=
118 !IsApproxZero(overscroll_delta
.x()) && IsApproxZero(old_overscroll
.x());
119 bool y_overscroll_started
=
120 !IsApproxZero(overscroll_delta
.y()) && IsApproxZero(old_overscroll
.y());
122 velocity
= ZeroSmallComponents(velocity
);
123 if (!velocity
.IsZero())
124 Absorb(current_time
, velocity
, x_overscroll_started
, y_overscroll_started
);
126 Pull(current_time
, overscroll_delta
, displacement
);
128 return CheckNeedsAnimate();
131 bool OverscrollGlow::Animate(base::TimeTicks current_time
,
132 cc::Layer
* parent_layer
) {
133 DCHECK(parent_layer
);
134 if (!CheckNeedsAnimate())
137 UpdateLayerAttachment(parent_layer
);
139 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
140 if (edge_effects_
[i
]->Update(current_time
)) {
141 Edge edge
= static_cast<Edge
>(i
);
142 edge_effects_
[i
]->ApplyToLayers(
143 ComputeSize(edge
, viewport_size_
),
144 ComputeTransform(edge
, viewport_size_
, edge_offsets_
[i
]));
148 return CheckNeedsAnimate();
151 void OverscrollGlow::UpdateDisplay(
152 const gfx::SizeF
& viewport_size
,
153 const gfx::SizeF
& content_size
,
154 const gfx::Vector2dF
& content_scroll_offset
) {
155 viewport_size_
= viewport_size
;
156 edge_offsets_
[OverscrollGlow::EDGE_TOP
] = -content_scroll_offset
.y();
157 edge_offsets_
[OverscrollGlow::EDGE_LEFT
] = -content_scroll_offset
.x();
158 edge_offsets_
[OverscrollGlow::EDGE_BOTTOM
] = content_size
.height() -
159 content_scroll_offset
.y() -
160 viewport_size
.height();
161 edge_offsets_
[OverscrollGlow::EDGE_RIGHT
] =
162 content_size
.width() - content_scroll_offset
.x() - viewport_size
.width();
165 bool OverscrollGlow::CheckNeedsAnimate() {
171 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
172 if (!edge_effects_
[i
]->IsFinished())
180 void OverscrollGlow::UpdateLayerAttachment(cc::Layer
* parent
) {
182 if (!root_layer_
.get())
185 if (!CheckNeedsAnimate())
188 if (root_layer_
->parent() != parent
)
189 parent
->AddChild(root_layer_
);
191 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
)
192 edge_effects_
[i
]->SetParent(root_layer_
.get());
195 void OverscrollGlow::Detach() {
196 if (root_layer_
.get())
197 root_layer_
->RemoveFromParent();
200 bool OverscrollGlow::InitializeIfNecessary() {
204 DCHECK(!root_layer_
.get());
205 root_layer_
= cc::Layer::Create();
206 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
207 edge_effects_
[i
] = client_
->CreateEdgeEffect();
208 DCHECK(edge_effects_
[i
]);
215 void OverscrollGlow::Pull(base::TimeTicks current_time
,
216 const gfx::Vector2dF
& overscroll_delta
,
217 const gfx::Vector2dF
& overscroll_location
) {
218 DCHECK(initialized_
);
219 DCHECK(!overscroll_delta
.IsZero());
220 DCHECK(!viewport_size_
.IsEmpty());
221 const float inv_width
= 1.f
/ viewport_size_
.width();
222 const float inv_height
= 1.f
/ viewport_size_
.height();
224 gfx::Vector2dF overscroll_pull
=
225 gfx::ScaleVector2d(overscroll_delta
, inv_width
, inv_height
);
226 const float edge_pull
[EDGE_COUNT
] = {
227 min(overscroll_pull
.y(), 0.f
), // Top
228 min(overscroll_pull
.x(), 0.f
), // Left
229 max(overscroll_pull
.y(), 0.f
), // Bottom
230 max(overscroll_pull
.x(), 0.f
) // Right
233 gfx::Vector2dF displacement
=
234 gfx::ScaleVector2d(overscroll_location
, inv_width
, inv_height
);
235 displacement
.set_x(max(0.f
, min(1.f
, displacement
.x())));
236 displacement
.set_y(max(0.f
, min(1.f
, displacement
.y())));
237 const float edge_displacement
[EDGE_COUNT
] = {
238 1.f
- displacement
.x(), // Top
239 displacement
.y(), // Left
240 displacement
.x(), // Bottom
241 1.f
- displacement
.y() // Right
244 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
248 edge_effects_
[i
]->Pull(
249 current_time
, std::abs(edge_pull
[i
]), edge_displacement
[i
]);
250 GetOppositeEdge(i
)->Release(current_time
);
254 void OverscrollGlow::Absorb(base::TimeTicks current_time
,
255 const gfx::Vector2dF
& velocity
,
256 bool x_overscroll_started
,
257 bool y_overscroll_started
) {
258 DCHECK(initialized_
);
259 DCHECK(!velocity
.IsZero());
261 // Only trigger on initial overscroll at a non-zero velocity
262 const float overscroll_velocities
[EDGE_COUNT
] = {
263 y_overscroll_started
? min(velocity
.y(), 0.f
) : 0, // Top
264 x_overscroll_started
? min(velocity
.x(), 0.f
) : 0, // Left
265 y_overscroll_started
? max(velocity
.y(), 0.f
) : 0, // Bottom
266 x_overscroll_started
? max(velocity
.x(), 0.f
) : 0 // Right
269 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
270 if (!overscroll_velocities
[i
])
273 edge_effects_
[i
]->Absorb(current_time
, std::abs(overscroll_velocities
[i
]));
274 GetOppositeEdge(i
)->Release(current_time
);
278 void OverscrollGlow::Release(base::TimeTicks current_time
) {
279 DCHECK(initialized_
);
280 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
)
281 edge_effects_
[i
]->Release(current_time
);
284 EdgeEffectBase
* OverscrollGlow::GetOppositeEdge(int edge_index
) {
285 DCHECK(initialized_
);
286 return edge_effects_
[(edge_index
+ 2) % EDGE_COUNT
].get();
289 } // namespace content