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

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

Powered by Google App Engine
This is Rietveld 408576698