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

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: Enable by default 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 gfx::Transform ComputeTransform(EdgeEffect::Edge edge,
83 gfx::SizeF size, int height) {
84 switch (edge) {
85 default:
86 case EdgeEffect::EDGE_TOP:
87 return gfx::Transform(1, 0, 0, 1, 0, 0);
88 case EdgeEffect::EDGE_LEFT:
89 return gfx::Transform(0, 1, -1, 0,
90 (-size.width() + height) / 2 ,
91 (size.width() - height) / 2);
92 case EdgeEffect::EDGE_BOTTOM:
93 return gfx::Transform(-1, 0, 0, -1, 0, size.height() - height);
94 case EdgeEffect::EDGE_RIGHT:
95 return gfx::Transform(0, -1, 1, 0,
96 (-size.width() - height) / 2 + size.height(),
97 (size.width() - height) / 2);
98 };
99 }
100
101 void DisableLayer(cc::Layer* layer) {
102 if (!layer)
103 return;
104
105 layer->SetIsDrawable(false);
106 layer->SetTransform(gfx::Transform());
107 layer->SetOpacity(1.f);
108 }
109
110 void UpdateLayer(cc::Layer* layer,
111 EdgeEffect::Edge edge,
112 gfx::SizeF size,
113 int height,
114 float opacity) {
115 layer->SetIsDrawable(true);
116 layer->SetTransform(ComputeTransform(edge, size, height));
117 layer->SetBounds(gfx::Size(size.width(), height));
118 layer->SetOpacity(Clamp(opacity, 0.f, 1.f));
119 }
120
121 } // namespace
122
123 EdgeEffect::EdgeEffect(scoped_refptr<cc::Layer> edge,
124 scoped_refptr<cc::Layer> glow)
125 : edge_(edge)
126 , glow_(glow)
127 , edge_alpha_(0)
128 , edge_scale_y_(0)
129 , glow_alpha_(0)
130 , glow_scale_y_(0)
131 , edge_alpha_start_(0)
132 , edge_alpha_finish_(0)
133 , edge_scale_y_start_(0)
134 , edge_scale_y_finish_(0)
135 , glow_alpha_start_(0)
136 , glow_alpha_finish_(0)
137 , glow_scale_y_start_(0)
138 , glow_scale_y_finish_(0)
139 , state_(STATE_IDLE)
140 , pull_distance_(0) {
141 DCHECK(edge_);
142 DCHECK(glow_);
143 }
144
145 EdgeEffect::~EdgeEffect() { }
146
147 bool EdgeEffect::IsFinished() const {
148 return state_ == STATE_IDLE;
149 }
150
151 void EdgeEffect::Finish() {
152 DisableLayer(edge_);
153 DisableLayer(glow_);
154 pull_distance_ = 0;
155 state_ = STATE_IDLE;
156 }
157
158 void EdgeEffect::Pull(base::TimeTicks current_time, float delta_distance) {
159 if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) {
160 return;
161 }
162 if (state_ != STATE_PULL) {
163 glow_scale_y_ = kPullGlowBegin;
164 }
165 state_ = STATE_PULL;
166
167 start_time_ = current_time;
168 duration_ = base::TimeDelta::FromMilliseconds(kPullTime);
169
170 float abs_delta_distance = std::abs(delta_distance);
171 pull_distance_ += delta_distance;
172 float distance = std::abs(pull_distance_);
173
174 edge_alpha_ = edge_alpha_start_ = Clamp(distance, kPullEdgeBegin, kMaxAlpha);
175 edge_scale_y_ = edge_scale_y_start_
176 = Clamp(distance * kPullDistanceEdgeFactor, kHeldEdgeScaleY, 1.f);
177
178 glow_alpha_ = glow_alpha_start_ =
179 std::min(kMaxAlpha,
180 glow_alpha_ + abs_delta_distance * kPullDistanceAlphaGlowFactor);
181
182 float glow_change = abs_delta_distance;
183 if (delta_distance > 0 && pull_distance_ < 0)
184 glow_change = -glow_change;
185 if (pull_distance_ == 0)
186 glow_scale_y_ = 0;
187
188 // Do not allow glow to get larger than kMaxGlowHeight.
189 glow_scale_y_ = glow_scale_y_start_ =
190 Clamp(glow_scale_y_ + glow_change * kPullDistanceGlowFactor,
191 0.f, kMaxGlowHeight);
192
193 edge_alpha_finish_ = edge_alpha_;
194 edge_scale_y_finish_ = edge_scale_y_;
195 glow_alpha_finish_ = glow_alpha_;
196 glow_scale_y_finish_ = glow_scale_y_;
197 }
198
199 void EdgeEffect::Release(base::TimeTicks current_time) {
200 pull_distance_ = 0;
201
202 if (state_ != STATE_PULL && state_ != STATE_PULL_DECAY)
203 return;
204
205 state_ = STATE_RECEDE;
206 edge_alpha_start_ = edge_alpha_;
207 edge_scale_y_start_ = edge_scale_y_;
208 glow_alpha_start_ = glow_alpha_;
209 glow_scale_y_start_ = glow_scale_y_;
210
211 edge_alpha_finish_ = 0.f;
212 edge_scale_y_finish_ = 0.f;
213 glow_alpha_finish_ = 0.f;
214 glow_scale_y_finish_ = 0.f;
215
216 start_time_ = current_time;
217 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime);
218 }
219
220 void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) {
221 state_ = STATE_ABSORB;
222 velocity = std::max(kMinVelocity, std::abs(velocity));
223
224 start_time_ = current_time;
225 // This should never be less than 1 millisecond.
226 duration_ = base::TimeDelta::FromMilliseconds(0.1f + (velocity * 0.03f));
227
228 // The edge should always be at least partially visible, regardless
229 // of velocity.
230 edge_alpha_start_ = 0.f;
231 edge_scale_y_ = edge_scale_y_start_ = 0.f;
232 // The glow depends more on the velocity, and therefore starts out
233 // nearly invisible.
234 glow_alpha_start_ = 0.5f;
235 glow_scale_y_start_ = 0.f;
236
237 // Factor the velocity by 8. Testing on device shows this works best to
238 // reflect the strength of the user's scrolling.
239 edge_alpha_finish_ = Clamp(velocity * kVelocityEdgeFactor, 0.f, 1.f);
240 // Edge should never get larger than the size of its asset.
241 edge_scale_y_finish_ = Clamp(velocity * kVelocityEdgeFactor,
242 kHeldEdgeScaleY, 1.f);
243
244 // Growth for the size of the glow should be quadratic to properly
245 // respond
246 // to a user's scrolling speed. The faster the scrolling speed, the more
247 // intense the effect should be for both the size and the saturation.
248 glow_scale_y_finish_ =
249 std::min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
250 // Alpha should change for the glow as well as size.
251 glow_alpha_finish_ =
252 Clamp(glow_alpha_start_,
253 velocity * kVelocityGlowFactor * .00001f, kMaxAlpha);
254 }
255
256 bool EdgeEffect::Update(base::TimeTicks current_time) {
257 if (IsFinished())
258 return false;
259
260 const double dt = (current_time - start_time_).InMilliseconds();
261 const double t = std::min(dt / duration_.InMilliseconds(), 1.);
262 const float interp = static_cast<float>(Damp(t, 1.));
263
264 edge_alpha_ = Lerp(edge_alpha_start_, edge_alpha_finish_, interp);
265 edge_scale_y_ = Lerp(edge_scale_y_start_, edge_scale_y_finish_, interp);
266 glow_alpha_ = Lerp(glow_alpha_start_, glow_alpha_finish_, interp);
267 glow_scale_y_ = Lerp(glow_scale_y_start_, glow_scale_y_finish_, interp);
268
269 if (t >= 1.f - kEpsilon) {
270 switch (state_) {
271 case STATE_ABSORB:
272 state_ = STATE_RECEDE;
273 start_time_ = current_time;
274 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime);
275
276 edge_alpha_start_ = edge_alpha_;
277 edge_scale_y_start_ = edge_scale_y_;
278 glow_alpha_start_ = glow_alpha_;
279 glow_scale_y_start_ = glow_scale_y_;
280
281 // After absorb, the glow and edge should fade to nothing.
282 edge_alpha_finish_ = 0.f;
283 edge_scale_y_finish_ = 0.f;
284 glow_alpha_finish_ = 0.f;
285 glow_scale_y_finish_ = 0.f;
286 break;
287 case STATE_PULL:
288 state_ = STATE_PULL_DECAY;
289 start_time_ = current_time;
290 duration_ = base::TimeDelta::FromMilliseconds(kPullDecayTime);
291
292 edge_alpha_start_ = edge_alpha_;
293 edge_scale_y_start_ = edge_scale_y_;
294 glow_alpha_start_ = glow_alpha_;
295 glow_scale_y_start_ = glow_scale_y_;
296
297 // After pull, the glow and edge should fade to nothing.
298 edge_alpha_finish_ = 0.f;
299 edge_scale_y_finish_ = 0.f;
300 glow_alpha_finish_ = 0.f;
301 glow_scale_y_finish_ = 0.f;
302 break;
303 case STATE_PULL_DECAY:
304 {
305 // When receding, we want edge to decrease more slowly
306 // than the glow.
307 float factor = glow_scale_y_finish_ != 0 ?
308 1 / (glow_scale_y_finish_ * glow_scale_y_finish_) :
309 std::numeric_limits<float>::max();
310 edge_scale_y_ = edge_scale_y_start_ +
311 (edge_scale_y_finish_ - edge_scale_y_start_) * interp * factor;
312 state_ = STATE_RECEDE;
313 }
314 break;
315 case STATE_RECEDE:
316 Finish();
317 break;
318 default:
319 break;
320 }
321 }
322
323 if (state_ == STATE_RECEDE && glow_scale_y_ <= 0 && edge_scale_y_ <= 0)
324 Finish();
325
326 return !IsFinished();
327 }
328
329 void EdgeEffect::ApplyToLayers(gfx::SizeF size, Edge edge) {
330 if (IsFinished())
331 return;
332
333 const float dpi_scale =
334 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor();
335
336 float dummy_scale_x, dummy_scale_y;
337
338 // Glow
339 gfx::Size glow_image_bounds;
340 glow_->CalculateContentsScale(1.f, 1.f, 1.f, false,
341 &dummy_scale_x, &dummy_scale_y,
342 &glow_image_bounds);
343 const int glow_height = glow_image_bounds.height();
344 const int glow_bottom = static_cast<int>(std::min(
345 glow_height * glow_scale_y_ * kGlowImageAspectRatioInverse * 0.6f,
346 glow_height * kMaxGlowHeight) * dpi_scale + 0.5f);
347 UpdateLayer(glow_.get(), edge, size, glow_bottom, glow_alpha_);
348
349 // Edge
350 gfx::Size edge_image_bounds;
351 edge_->CalculateContentsScale(1.f, 1.f, 1.f, false,
352 &dummy_scale_x, &dummy_scale_y,
353 &edge_image_bounds);
354 const int edge_height = edge_image_bounds.height();
355 const int edge_bottom = static_cast<int>(
356 edge_height * edge_scale_y_ * dpi_scale);
357 UpdateLayer(edge_.get(), edge, size, edge_bottom, edge_alpha_);
358 }
359
360 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698