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::IsActive() const {
98 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
99 if (!edge_effects_
[i
]->IsFinished())
105 float OverscrollGlow::GetVisibleAlpha() const {
107 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
108 if (!edge_effects_
[i
]->IsFinished())
109 max_alpha
= std::max(max_alpha
, edge_effects_
[i
]->GetAlpha());
111 return std::min(max_alpha
, 1.f
);
114 bool OverscrollGlow::OnOverscrolled(base::TimeTicks current_time
,
115 gfx::Vector2dF accumulated_overscroll
,
116 gfx::Vector2dF overscroll_delta
,
117 gfx::Vector2dF velocity
,
118 gfx::Vector2dF displacement
) {
119 // The size of the glow determines the relative effect of the inputs; an
120 // empty-sized effect is effectively disabled.
121 if (viewport_size_
.IsEmpty())
124 // Ignore sufficiently small values that won't meaningfuly affect animation.
125 overscroll_delta
= ZeroSmallComponents(overscroll_delta
);
126 if (overscroll_delta
.IsZero()) {
128 Release(current_time
);
129 return CheckNeedsAnimate();
132 if (!InitializeIfNecessary())
135 gfx::Vector2dF old_overscroll
= accumulated_overscroll
- overscroll_delta
;
136 bool x_overscroll_started
=
137 !IsApproxZero(overscroll_delta
.x()) && IsApproxZero(old_overscroll
.x());
138 bool y_overscroll_started
=
139 !IsApproxZero(overscroll_delta
.y()) && IsApproxZero(old_overscroll
.y());
141 velocity
= ZeroSmallComponents(velocity
);
142 if (!velocity
.IsZero())
143 Absorb(current_time
, velocity
, x_overscroll_started
, y_overscroll_started
);
145 Pull(current_time
, overscroll_delta
, displacement
);
147 return CheckNeedsAnimate();
150 bool OverscrollGlow::Animate(base::TimeTicks current_time
,
151 cc::Layer
* parent_layer
) {
152 DCHECK(parent_layer
);
153 if (!CheckNeedsAnimate())
156 UpdateLayerAttachment(parent_layer
);
158 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
159 if (edge_effects_
[i
]->Update(current_time
)) {
160 Edge edge
= static_cast<Edge
>(i
);
161 edge_effects_
[i
]->ApplyToLayers(
162 ComputeSize(edge
, viewport_size_
),
163 ComputeTransform(edge
, viewport_size_
, edge_offsets_
[i
]));
167 return CheckNeedsAnimate();
170 void OverscrollGlow::UpdateDisplay(
171 const gfx::SizeF
& viewport_size
,
172 const gfx::SizeF
& content_size
,
173 const gfx::Vector2dF
& content_scroll_offset
) {
174 viewport_size_
= viewport_size
;
175 edge_offsets_
[OverscrollGlow::EDGE_TOP
] = -content_scroll_offset
.y();
176 edge_offsets_
[OverscrollGlow::EDGE_LEFT
] = -content_scroll_offset
.x();
177 edge_offsets_
[OverscrollGlow::EDGE_BOTTOM
] = content_size
.height() -
178 content_scroll_offset
.y() -
179 viewport_size
.height();
180 edge_offsets_
[OverscrollGlow::EDGE_RIGHT
] =
181 content_size
.width() - content_scroll_offset
.x() - viewport_size
.width();
184 bool OverscrollGlow::CheckNeedsAnimate() {
197 void OverscrollGlow::UpdateLayerAttachment(cc::Layer
* parent
) {
199 if (!root_layer_
.get())
202 if (!CheckNeedsAnimate())
205 if (root_layer_
->parent() != parent
)
206 parent
->AddChild(root_layer_
);
208 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
)
209 edge_effects_
[i
]->SetParent(root_layer_
.get());
212 void OverscrollGlow::Detach() {
213 if (root_layer_
.get())
214 root_layer_
->RemoveFromParent();
217 bool OverscrollGlow::InitializeIfNecessary() {
221 DCHECK(!root_layer_
.get());
222 root_layer_
= cc::Layer::Create();
223 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
224 edge_effects_
[i
] = client_
->CreateEdgeEffect();
225 DCHECK(edge_effects_
[i
]);
232 void OverscrollGlow::Pull(base::TimeTicks current_time
,
233 const gfx::Vector2dF
& overscroll_delta
,
234 const gfx::Vector2dF
& overscroll_location
) {
235 DCHECK(initialized_
);
236 DCHECK(!overscroll_delta
.IsZero());
237 DCHECK(!viewport_size_
.IsEmpty());
238 const float inv_width
= 1.f
/ viewport_size_
.width();
239 const float inv_height
= 1.f
/ viewport_size_
.height();
241 gfx::Vector2dF overscroll_pull
=
242 gfx::ScaleVector2d(overscroll_delta
, inv_width
, inv_height
);
243 const float edge_pull
[EDGE_COUNT
] = {
244 min(overscroll_pull
.y(), 0.f
), // Top
245 min(overscroll_pull
.x(), 0.f
), // Left
246 max(overscroll_pull
.y(), 0.f
), // Bottom
247 max(overscroll_pull
.x(), 0.f
) // Right
250 gfx::Vector2dF displacement
=
251 gfx::ScaleVector2d(overscroll_location
, inv_width
, inv_height
);
252 displacement
.set_x(max(0.f
, min(1.f
, displacement
.x())));
253 displacement
.set_y(max(0.f
, min(1.f
, displacement
.y())));
254 const float edge_displacement
[EDGE_COUNT
] = {
255 1.f
- displacement
.x(), // Top
256 displacement
.y(), // Left
257 displacement
.x(), // Bottom
258 1.f
- displacement
.y() // Right
261 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
265 edge_effects_
[i
]->Pull(
266 current_time
, std::abs(edge_pull
[i
]), edge_displacement
[i
]);
267 GetOppositeEdge(i
)->Release(current_time
);
271 void OverscrollGlow::Absorb(base::TimeTicks current_time
,
272 const gfx::Vector2dF
& velocity
,
273 bool x_overscroll_started
,
274 bool y_overscroll_started
) {
275 DCHECK(initialized_
);
276 DCHECK(!velocity
.IsZero());
278 // Only trigger on initial overscroll at a non-zero velocity
279 const float overscroll_velocities
[EDGE_COUNT
] = {
280 y_overscroll_started
? min(velocity
.y(), 0.f
) : 0, // Top
281 x_overscroll_started
? min(velocity
.x(), 0.f
) : 0, // Left
282 y_overscroll_started
? max(velocity
.y(), 0.f
) : 0, // Bottom
283 x_overscroll_started
? max(velocity
.x(), 0.f
) : 0 // Right
286 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
287 if (!overscroll_velocities
[i
])
290 edge_effects_
[i
]->Absorb(current_time
, std::abs(overscroll_velocities
[i
]));
291 GetOppositeEdge(i
)->Release(current_time
);
295 void OverscrollGlow::Release(base::TimeTicks current_time
) {
296 DCHECK(initialized_
);
297 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
)
298 edge_effects_
[i
]->Release(current_time
);
301 EdgeEffectBase
* OverscrollGlow::GetOppositeEdge(int edge_index
) {
302 DCHECK(initialized_
);
303 return edge_effects_
[(edge_index
+ 2) % EDGE_COUNT
].get();
306 } // namespace content