Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(173)

Side by Side Diff: content/browser/android/edge_effect.cc

Issue 14268004: Add overscroll edge effect animations for Android. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase + CR. Avoid round-trip animation requests on animate(). Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "content/browser/android/edge_effect.h"
6
7 #include "cc/layers/layer.h"
8 #include "ui/gfx/screen.h"
9
10 namespace content {
11
12 namespace {
13
14 enum State {
15 STATE_IDLE = 0,
16 STATE_PULL,
17 STATE_ABSORB,
18 STATE_RECEDE,
19 STATE_PULL_DECAY
20 };
21
22 // Time it will take the effect to fully recede in ms
23 const int kRecedeTime = 1000;
24
25 // Time it will take before a pulled glow begins receding in ms
26 const int kPullTime = 167;
27
28 // Time it will take in ms for a pulled glow to decay before release
29 const int kPullDecayTime = 1000;
30
31 const float kMaxAlpha = 1.f;
32 const float kHeldEdgeScaleY = .5f;
33
34 const float kMaxGlowHeight = 4.f;
35
36 // Note: The Android version computes the aspect ratio from the source texture;
37 // because we use rescaled images, this is precomputed from the original Android
38 // textures.
39 const float kGlowImageAspectRatioInverse = 0.25f;
40
41 const float kPullGlowBegin = 1.f;
42 const float kPullEdgeBegin = 0.6f;
43
44 // Minimum velocity that will be absorbed
45 const float kMinVelocity = 100.f;
46
47 const float kEpsilon = 0.001f;
48
49 // How much dragging should effect the height of the edge image.
50 // Number determined by user testing.
51 const int kPullDistanceEdgeFactor = 7;
52
53 // How much dragging should effect the height of the glow image.
54 // Number determined by user testing.
55 const int kPullDistanceGlowFactor = 7;
56 const float kPullDistanceAlphaGlowFactor = 1.1f;
57
58 const int kVelocityEdgeFactor = 8;
59 const int kVelocityGlowFactor = 16;
60
61 template <typename T>
62 T Lerp(T a, T b, T t) {
63 return a + (b - a) * t;
64 }
65
66 template <typename T>
67 T Clamp(T value, T low, T high) {
68 return value < low ? low : (value > high ? high : value);
69 }
70
71 template <typename T>
72 T Damp(T input, T factor) {
73 T result;
74 if (factor == 1) {
75 result = 1 - (1 - input) * (1 - input);
76 } else {
77 result = 1 - std::pow(1 - input, 2 * factor);
78 }
79 return result;
80 }
81
82 void AttachLayer(scoped_refptr<cc::Layer>& child, cc::Layer* parent) {
83 if (!parent || !child)
84 return;
85
86 if (child->parent() != parent)
87 parent->AddChild(child);
88 }
89
90 void DetachLayer(scoped_refptr<cc::Layer>& layer) {
91 if (!layer)
92 return;
93
94 layer->SetIsDrawable(false);
95 layer->RemoveFromParent();
96 }
97
98 } // namespace
99
100 EdgeEffect::EdgeEffect(scoped_refptr<cc::Layer> edge,
101 scoped_refptr<cc::Layer> glow)
102 : edge_(edge)
103 , glow_(glow)
104 , edge_alpha_(0)
105 , edge_scale_y_(0)
106 , glow_alpha_(0)
107 , glow_scale_y_(0)
108 , edge_alpha_start_(0)
109 , edge_alpha_finish_(0)
110 , edge_scale_y_start_(0)
111 , edge_scale_y_finish_(0)
112 , glow_alpha_start_(0)
113 , glow_alpha_finish_(0)
114 , glow_scale_y_start_(0)
115 , glow_scale_y_finish_(0)
116 , state_(STATE_IDLE)
117 , pull_distance_(0) {
118 DCHECK(edge_);
119 DCHECK(glow_);
120 }
121
122 EdgeEffect::~EdgeEffect() {
123 Finish();
124 }
125
126 bool EdgeEffect::IsFinished() const {
127 return state_ == STATE_IDLE;
128 }
129
130 void EdgeEffect::Finish() {
131 Detach();
132 pull_distance_ = 0;
133 state_ = STATE_IDLE;
134 }
135
136 void EdgeEffect::Pull(base::TimeTicks current_time, float delta_distance) {
137 if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) {
138 return;
139 }
140 if (state_ != STATE_PULL) {
141 glow_scale_y_ = kPullGlowBegin;
142 }
143 state_ = STATE_PULL;
144
145 start_time_ = current_time;
146 duration_ = base::TimeDelta::FromMilliseconds(kPullTime);
147
148 float abs_delta_distance = std::abs(delta_distance);
149 pull_distance_ += delta_distance;
150 float distance = std::abs(pull_distance_);
151
152 edge_alpha_ = edge_alpha_start_ = Clamp(distance, kPullEdgeBegin, kMaxAlpha);
153 edge_scale_y_ = edge_scale_y_start_
154 = Clamp(distance * kPullDistanceEdgeFactor, kHeldEdgeScaleY, 1.f);
155
156 glow_alpha_ = glow_alpha_start_
157 = std::min(kMaxAlpha,
no sievers 2013/05/13 18:38:24 nit: move '=' to line above
jdduke (slow) 2013/05/13 20:44:24 Done.
158 glow_alpha_ +
159 abs_delta_distance * kPullDistanceAlphaGlowFactor);
160
161 float glow_change = abs_delta_distance;
162 if (delta_distance > 0 && pull_distance_ < 0)
163 glow_change = -glow_change;
164 if (pull_distance_ == 0)
165 glow_scale_y_ = 0;
166
167 // Do not allow glow to get larger than kMaxGlowHeight.
168 glow_scale_y_ = glow_scale_y_start_
169 = Clamp(glow_scale_y_ + glow_change * kPullDistanceGlowFactor,
no sievers 2013/05/13 18:38:24 nit: move '=' to line above
jdduke (slow) 2013/05/13 20:44:24 Done.
170 0.f, kMaxGlowHeight);
171
172 edge_alpha_finish_ = edge_alpha_;
173 edge_scale_y_finish_ = edge_scale_y_;
174 glow_alpha_finish_ = glow_alpha_;
175 glow_scale_y_finish_ = glow_scale_y_;
176 }
177
178 void EdgeEffect::Release(base::TimeTicks current_time) {
179 pull_distance_ = 0;
180
181 if (state_ != STATE_PULL && state_ != STATE_PULL_DECAY)
182 return;
183
184 state_ = STATE_RECEDE;
185 edge_alpha_start_ = edge_alpha_;
186 edge_scale_y_start_ = edge_scale_y_;
187 glow_alpha_start_ = glow_alpha_;
188 glow_scale_y_start_ = glow_scale_y_;
189
190 edge_alpha_finish_ = 0.f;
191 edge_scale_y_finish_ = 0.f;
192 glow_alpha_finish_ = 0.f;
193 glow_scale_y_finish_ = 0.f;
194
195 start_time_ = current_time;
196 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime);
197 }
198
199 void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) {
200 state_ = STATE_ABSORB;
201 velocity = std::max(kMinVelocity, std::abs(velocity));
202
203 start_time_ = current_time;
204 // This should never be less than 1 millisecond.
205 duration_ = base::TimeDelta::FromMilliseconds(0.1f + (velocity * 0.03f));
206
207 // The edge should always be at least partially visible, regardless
208 // of velocity.
209 edge_alpha_start_ = 0.f;
210 edge_scale_y_ = edge_scale_y_start_ = 0.f;
211 // The glow depends more on the velocity, and therefore starts out
212 // nearly invisible.
213 glow_alpha_start_ = 0.5f;
214 glow_scale_y_start_ = 0.f;
215
216 // Factor the velocity by 8. Testing on device shows this works best to
217 // reflect the strength of the user's scrolling.
218 edge_alpha_finish_ = Clamp(velocity * kVelocityEdgeFactor, 0.f, 1.f);
219 // Edge should never get larger than the size of its asset.
220 edge_scale_y_finish_ = Clamp(velocity * kVelocityEdgeFactor,
221 kHeldEdgeScaleY, 1.f);
222
223 // Growth for the size of the glow should be quadratic to properly
224 // respond
225 // to a user's scrolling speed. The faster the scrolling speed, the more
226 // intense the effect should be for both the size and the saturation.
227 glow_scale_y_finish_
228 = std::min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
no sievers 2013/05/13 18:38:24 nit: move '=' to line above
jdduke (slow) 2013/05/13 20:44:24 Done.
229 // Alpha should change for the glow as well as size.
230 glow_alpha_finish_
231 = Clamp(glow_alpha_start_,
no sievers 2013/05/13 18:38:24 nit: move '=' to line above
jdduke (slow) 2013/05/13 20:44:24 Done.
232 velocity * kVelocityGlowFactor * .00001f, kMaxAlpha);
233 }
234
235 bool EdgeEffect::Update(base::TimeTicks current_time) {
236 if (IsFinished())
237 return false;
238
239 const double dt = (current_time - start_time_).InMilliseconds();
240 const double t = std::min(dt / duration_.InMilliseconds(), 1.);
241 const float interp = static_cast<float>(Damp(t, 1.));
242
243 edge_alpha_ = Lerp(edge_alpha_start_, edge_alpha_finish_, interp);
244 edge_scale_y_ = Lerp(edge_scale_y_start_, edge_scale_y_finish_, interp);
245 glow_alpha_ = Lerp(glow_alpha_start_, glow_alpha_finish_, interp);
246 glow_scale_y_ = Lerp(glow_scale_y_start_, glow_scale_y_finish_, interp);
247
248 if (t >= 1.f - kEpsilon) {
249 switch (state_) {
250 case STATE_ABSORB:
251 state_ = STATE_RECEDE;
252 start_time_ = current_time;
253 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime);
254
255 edge_alpha_start_ = edge_alpha_;
256 edge_scale_y_start_ = edge_scale_y_;
257 glow_alpha_start_ = glow_alpha_;
258 glow_scale_y_start_ = glow_scale_y_;
259
260 // After absorb, the glow and edge should fade to nothing.
261 edge_alpha_finish_ = 0.f;
262 edge_scale_y_finish_ = 0.f;
263 glow_alpha_finish_ = 0.f;
264 glow_scale_y_finish_ = 0.f;
265 break;
266 case STATE_PULL:
267 state_ = STATE_PULL_DECAY;
268 start_time_ = current_time;
269 duration_ = base::TimeDelta::FromMilliseconds(kPullDecayTime);
270
271 edge_alpha_start_ = edge_alpha_;
272 edge_scale_y_start_ = edge_scale_y_;
273 glow_alpha_start_ = glow_alpha_;
274 glow_scale_y_start_ = glow_scale_y_;
275
276 // After pull, the glow and edge should fade to nothing.
277 edge_alpha_finish_ = 0.f;
278 edge_scale_y_finish_ = 0.f;
279 glow_alpha_finish_ = 0.f;
280 glow_scale_y_finish_ = 0.f;
281 break;
282 case STATE_PULL_DECAY:
283 {
284 // When receding, we want edge to decrease more slowly
285 // than the glow.
286 float factor = glow_scale_y_finish_ != 0 ?
287 1 / (glow_scale_y_finish_ * glow_scale_y_finish_) :
288 std::numeric_limits<float>::max();
289 edge_scale_y_ = edge_scale_y_start_ +
290 (edge_scale_y_finish_ - edge_scale_y_start_) * interp * factor;
291 state_ = STATE_RECEDE;
292 }
293 break;
294 case STATE_RECEDE:
295 Finish();
296 break;
297 default:
298 break;
299 }
300 }
301
302 if (state_ == STATE_RECEDE && glow_scale_y_ <= 0 && edge_scale_y_ <= 0)
303 Finish();
304
305 return !IsFinished();
306 }
307
308 namespace {
309
no sievers 2013/05/13 18:38:24 nit: maybe move this into the existing anonymous n
jdduke (slow) 2013/05/13 20:44:24 Done.
310 gfx::Transform ComputeTransform(EdgeEffect::Edge edge,
311 gfx::SizeF size, int height) {
312 switch (edge) {
313 default:
314 case EdgeEffect::EDGE_TOP:
315 return gfx::Transform(1, 0, 0, 1, 0, 0);
316 case EdgeEffect::EDGE_LEFT:
317 return gfx::Transform(0, 1, -1, 0,
318 (-size.width() + height) / 2 ,
319 (size.width() - height) / 2);
320 case EdgeEffect::EDGE_BOTTOM:
321 return gfx::Transform(-1, 0, 0, -1, 0, size.height() - height);
322 case EdgeEffect::EDGE_RIGHT:
323 return gfx::Transform(0, -1, 1, 0,
324 (-size.width() - height) / 2 + size.height(),
325 (size.width() - height) / 2);
326 };
327 }
328
329 void UpdateLayer(cc::Layer* layer,
330 EdgeEffect::Edge edge,
331 gfx::SizeF size,
332 int height,
333 float opacity) {
334 layer->SetIsDrawable(true);
335 layer->SetTransform(ComputeTransform(edge, size, height));
336 layer->SetBounds(gfx::Size(size.width(), height));
337 layer->SetOpacity(Clamp(opacity, 0.f, 1.f));
338 }
339
340 } // namespace
341
342 void EdgeEffect::Draw(cc::Layer* parent, gfx::SizeF size, Edge edge) {
343 if (IsFinished())
344 return;
345
346 Attach(parent);
347
348 const float dpi_scale =
349 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor();
350
351 // Glow
352 {
no sievers 2013/05/13 18:38:24 nit: why the stack frames here?
jdduke (slow) 2013/05/13 20:44:24 No good reason other than logical separation, remo
353 gfx::Size glow_image_bounds;
354 float dummy_scale_x, dummy_scale_y;
355 glow_->CalculateContentsScale(1.f, 1.f, 1.f, false,
356 &dummy_scale_x, &dummy_scale_y,
357 &glow_image_bounds);
358 const int glow_height = glow_image_bounds.height();
359 const int glow_bottom = static_cast<int>(std::min(
360 glow_height * glow_scale_y_ * kGlowImageAspectRatioInverse * 0.6f,
361 glow_height * kMaxGlowHeight) * dpi_scale + 0.5f);
362 UpdateLayer(glow_.get(), edge, size, glow_bottom, glow_alpha_);
363 }
364
365 // Edge
366 {
367 gfx::Size edge_image_bounds;
368 float dummy_scale_x, dummy_scale_y;
369 edge_->CalculateContentsScale(1.f, 1.f, 1.f, false,
370 &dummy_scale_x, &dummy_scale_y,
371 &edge_image_bounds);
372 const int edge_height = edge_image_bounds.height();
373 const int edge_bottom = static_cast<int>(
374 edge_height * edge_scale_y_ * dpi_scale);
375 UpdateLayer(edge_.get(), edge, size, edge_bottom, edge_alpha_);
376 }
377 }
378
379 void EdgeEffect::Attach(cc::Layer* parent) {
380 DCHECK(parent);
381 AttachLayer(edge_, parent);
382 AttachLayer(glow_, parent);
383 }
384
385 void EdgeEffect::Detach() {
386 DetachLayer(edge_);
387 DetachLayer(glow_);
388 }
389
390 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698