MacViews: Get c/b/ui/views/tabs to build on Mac
[chromium-blink-merge.git] / third_party / polymer / components-chromium / paper-ripple / raw-extracted.js
blob95faeb62cea319513a65288919cc0d3a4e5e5137
3 //
4 // INK EQUATIONS
5 //
7 // Animation constants.
8 var globalSpeed = 1;
9 var waveOpacityDecayVelocity = 0.8 / globalSpeed; // opacity per second.
10 var waveInitialOpacity = 0.25;
11 var waveLingerOnTouchUp = 0.2;
12 var waveMaxRadius = 150;
14 // TODOs:
15 // - rather than max distance to corner, use hypotenuos(sp) (diag)
16 // - use quadratic for the fall off, move fast at the beginning,
17 // - on cancel, immediately fade out, reverse the direction
19 function waveRadiusFn(touchDownMs, touchUpMs, ww, hh) {
20 // Convert from ms to s.
21 var touchDown = touchDownMs / 1000;
22 var touchUp = touchUpMs / 1000;
23 var totalElapsed = touchDown + touchUp;
24 var waveRadius = Math.min(Math.max(ww, hh), waveMaxRadius) * 1.1 + 5;
25 var dduration = 1.1 - .2 * (waveRadius / waveMaxRadius);
26 var tt = (totalElapsed / dduration);
28 var ssize = waveRadius * (1 - Math.pow(80, -tt));
29 return Math.abs(ssize);
32 function waveOpacityFn(td, tu) {
33 // Convert from ms to s.
34 var touchDown = td / 1000;
35 var touchUp = tu / 1000;
36 var totalElapsed = touchDown + touchUp;
38 if (tu <= 0) { // before touch up
39 return waveInitialOpacity;
41 return Math.max(0, waveInitialOpacity - touchUp * waveOpacityDecayVelocity);
44 function waveOuterOpacityFn(td, tu) {
45 // Convert from ms to s.
46 var touchDown = td / 1000;
47 var touchUp = tu / 1000;
49 // Linear increase in background opacity, capped at the opacity
50 // of the wavefront (waveOpacity).
51 var outerOpacity = touchDown * 0.3;
52 var waveOpacity = waveOpacityFn(td, tu);
53 return Math.max(0, Math.min(outerOpacity, waveOpacity));
57 function waveGravityToCenterPercentageFn(td, tu, r) {
58 // Convert from ms to s.
59 var touchDown = td / 1000;
60 var touchUp = tu / 1000;
61 var totalElapsed = touchDown + touchUp;
63 return Math.min(1.0, touchUp * 6);
67 // Determines whether the wave should be completely removed.
68 function waveDidFinish(wave, radius) {
69 var waveOpacity = waveOpacityFn(wave.tDown, wave.tUp);
70 // Does not linger any more.
71 // var lingerTimeMs = waveLingerOnTouchUp * 1000;
73 // If the wave opacity is 0 and the radius exceeds the bounds
74 // of the element, then this is finished.
75 if (waveOpacity < 0.01 && radius >= wave.maxRadius) {
76 return true;
78 return false;
82 // DRAWING
85 function animateIcon() {
86 var el = document.getElementById('button_toolbar0');
87 el.classList.add('animate');
88 setTimeout(function(){
89 el.classList.remove('animate');
90 el.classList.toggle('selected');
91 }, 500);
95 function drawRipple(canvas, x, y, radius, innerColor, outerColor, innerColorAlpha, outerColorAlpha) {
96 var ctx = canvas.getContext('2d');
97 if (outerColor) {
98 ctx.fillStyle = outerColor;
99 ctx.fillRect(0,0,canvas.width, canvas.height);
102 ctx.beginPath();
103 ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
104 ctx.fillStyle = innerColor;
105 ctx.fill();
108 function drawLabel(canvas, label, fontSize, color, alignment) {
109 var ctx = canvas.getContext('2d');
110 ctx.font= fontSize + 'px Helvetica';
112 var metrics = ctx.measureText(label);
113 var width = metrics.width;
114 var height = metrics.height;
115 ctx.fillStyle = color;
117 var xPos = (canvas.width/2 - width)/2;
119 if (alignment === 'left') { xPos = 16; }
121 ctx.fillText(label, xPos, canvas.height/2 - (canvas.height/2 - fontSize +2) / 2);
125 // BUTTON SETUP
128 function createWave(elem) {
129 var elementStyle = window.getComputedStyle(elem);
130 var fgColor = elementStyle.color;
132 var wave = {
133 waveColor: fgColor,
134 maxRadius: 0,
135 isMouseDown: false,
136 mouseDownStart: 0.0,
137 mouseUpStart: 0.0,
138 tDown: 0,
139 tUp: 0
141 return wave;
144 function removeWaveFromScope(scope, wave) {
145 if (scope.waves) {
146 var pos = scope.waves.indexOf(wave);
147 scope.waves.splice(pos, 1);
152 function setUpPaperByClass( classname ) {
153 var elems = document.querySelectorAll( classname );
154 [].forEach.call( elems, function( el ) {
155 setUpPaper(el);
159 function setUpPaper(elem) {
160 var pixelDensity = 2;
162 var elementStyle = window.getComputedStyle(elem);
163 var fgColor = elementStyle.color;
164 var bgColor = elementStyle.backgroundColor;
165 elem.width = elem.clientWidth;
166 elem.setAttribute('width', elem.clientWidth * pixelDensity + "px");
167 elem.setAttribute('height', elem.clientHeight * pixelDensity + "px");
169 var isButton = elem.classList.contains( 'button' ) || elem.classList.contains( 'button_floating' ) | elem.classList.contains( 'button_menu' );
170 var isToolbarButton = elem.classList.contains( 'button_toolbar' );
172 elem.getContext('2d').scale(pixelDensity, pixelDensity)
174 var scope = {
175 backgroundFill: true,
176 element: elem,
177 label: 'Button',
178 waves: [],
182 scope.label = elem.getAttribute('value') || elementStyle.content;
183 scope.labelFontSize = elementStyle.fontSize.split("px")[0];
185 drawLabel(elem, scope.label, scope.labelFontSize, fgColor, elem.style.textAlign);
189 // RENDER FOR EACH FRAME
191 var onFrame = function() {
192 var shouldRenderNextFrame = false;
194 // Clear the canvas
195 var ctx = elem.getContext('2d');
196 ctx.clearRect(0, 0, elem.width, elem.height);
198 var deleteTheseWaves = [];
199 // The oldest wave's touch down duration
200 var longestTouchDownDuration = 0;
201 var longestTouchUpDuration = 0;
202 // Save the last known wave color
203 var lastWaveColor = null;
205 for (var i = 0; i < scope.waves.length; i++) {
206 var wave = scope.waves[i];
208 if (wave.mouseDownStart > 0) {
209 wave.tDown = now() - wave.mouseDownStart;
211 if (wave.mouseUpStart > 0) {
212 wave.tUp = now() - wave.mouseUpStart;
215 // Determine how long the touch has been up or down.
216 var tUp = wave.tUp;
217 var tDown = wave.tDown;
218 longestTouchDownDuration = Math.max(longestTouchDownDuration, tDown);
219 longestTouchUpDuration = Math.max(longestTouchUpDuration, tUp);
221 // Obtain the instantenous size and alpha of the ripple.
222 var radius = waveRadiusFn(tDown, tUp, elem.width, elem.height);
223 var waveAlpha = waveOpacityFn(tDown, tUp);
224 var waveColor = cssColorWithAlpha(wave.waveColor, waveAlpha);
225 lastWaveColor = wave.waveColor;
227 // Position of the ripple.
228 var x = wave.startPosition.x;
229 var y = wave.startPosition.y;
231 // Ripple gravitational pull to the center of the canvas.
232 if (wave.endPosition) {
234 var translateFraction = waveGravityToCenterPercentageFn(tDown, tUp, wave.maxRadius);
236 // This translates from the origin to the center of the view based on the max dimension of
237 var translateFraction = Math.min(1, radius / wave.containerSize * 2 / Math.sqrt(2) );
239 x += translateFraction * (wave.endPosition.x - wave.startPosition.x);
240 y += translateFraction * (wave.endPosition.y - wave.startPosition.y);
243 // If we do a background fill fade too, work out the correct color.
244 var bgFillColor = null;
245 if (scope.backgroundFill) {
246 var bgFillAlpha = waveOuterOpacityFn(tDown, tUp);
247 bgFillColor = cssColorWithAlpha(wave.waveColor, bgFillAlpha);
250 // Draw the ripple.
251 drawRipple(elem, x, y, radius, waveColor, bgFillColor);
253 // Determine whether there is any more rendering to be done.
254 var shouldRenderWaveAgain = !waveDidFinish(wave, radius);
255 shouldRenderNextFrame = shouldRenderNextFrame || shouldRenderWaveAgain;
256 if (!shouldRenderWaveAgain) {
257 deleteTheseWaves.push(wave);
261 if (shouldRenderNextFrame) {
262 window.requestAnimationFrame(onFrame);
263 } else {
264 // If there is nothing to draw, clear any drawn waves now because
265 // we're not going to get another requestAnimationFrame any more.
266 var ctx = elem.getContext('2d');
267 ctx.clearRect(0, 0, elem.width, elem.height);
270 // Draw the label at the very last point so it is on top of everything.
271 drawLabel(elem, scope.label, scope.labelFontSize, fgColor, elem.style.textAlign);
273 for (var i = 0; i < deleteTheseWaves.length; ++i) {
274 var wave = deleteTheseWaves[i];
275 removeWaveFromScope(scope, wave);
280 // MOUSE DOWN HANDLER
283 elem.addEventListener('mousedown', function(e) {
284 var wave = createWave(e.target);
285 var elem = scope.element;
287 wave.isMouseDown = true;
288 wave.tDown = 0.0;
289 wave.tUp = 0.0;
290 wave.mouseUpStart = 0.0;
291 wave.mouseDownStart = now();
293 var width = e.target.width / 2; // Retina canvas
294 var height = e.target.height / 2;
295 var touchX = e.clientX - e.target.offsetLeft - e.target.offsetParent.offsetLeft;
296 var touchY = e.clientY - e.target.offsetTop - e.target.offsetParent.offsetTop;
297 wave.startPosition = {x:touchX, y:touchY};
299 if (elem.classList.contains("recenteringTouch")) {
300 wave.endPosition = {x: width / 2, y: height / 2};
301 wave.slideDistance = dist(wave.startPosition, wave.endPosition);
303 wave.containerSize = Math.max(width, height);
304 wave.maxRadius = distanceFromPointToFurthestCorner(wave.startPosition, {w: width, h: height});
305 elem.classList.add("activated");
306 scope.waves.push(wave);
307 window.requestAnimationFrame(onFrame);
308 return false;
312 // MOUSE UP HANDLER
315 elem.addEventListener('mouseup', function(e) {
316 elem.classList.remove("activated");
318 for (var i = 0; i < scope.waves.length; i++) {
319 // Declare the next wave that has mouse down to be mouse'ed up.
320 var wave = scope.waves[i];
321 if (wave.isMouseDown) {
322 wave.isMouseDown = false
323 wave.mouseUpStart = now();
324 wave.mouseDownStart = 0;
325 wave.tUp = 0.0;
326 break;
329 return false;
332 elem.addEventListener('mouseout', function(e) {
333 elem.classList.remove("activated");
335 for (var i = 0; i < scope.waves.length; i++) {
336 // Declare the next wave that has mouse down to be mouse'ed up.
337 var wave = scope.waves[i];
338 if (wave.isMouseDown) {
339 wave.isMouseDown = false
340 wave.mouseUpStart = now();
341 wave.mouseDownStart = 0;
342 wave.tUp = 0.0;
343 wave.cancelled = true;
344 break;
347 return false;
350 return scope;
353 // Shortcuts.
354 var pow = Math.pow;
355 var now = function() { return new Date().getTime(); };
357 // Quad beizer where t is between 0 and 1.
358 function quadBezier(t, p0, p1, p2, p3) {
359 return pow(1 - t, 3) * p0 +
360 3 * pow(1 - t, 2) * t * p1 +
361 (1 - t) * pow(t, 2) * p2 +
362 pow(t, 3) * p3;
365 function easeIn(t) {
366 return quadBezier(t, 0.4, 0.0, 1, 1);
369 function cssColorWithAlpha(cssColor, alpha) {
370 var parts = cssColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
371 if (typeof alpha == 'undefined') {
372 alpha = 1;
374 if (!parts) {
375 return 'rgba(255, 255, 255, ' + alpha + ')';
377 return 'rgba(' + parts[1] + ', ' + parts[2] + ', ' + parts[3] + ', ' + alpha + ')';
380 function dist(p1, p2) {
381 return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
384 function distanceFromPointToFurthestCorner(point, size) {
385 var tl_d = dist(point, {x: 0, y: 0});
386 var tr_d = dist(point, {x: size.w, y: 0});
387 var bl_d = dist(point, {x: 0, y: size.h});
388 var br_d = dist(point, {x: size.w, y: size.h});
389 return Math.max(Math.max(tl_d, tr_d), Math.max(bl_d, br_d));
393 function toggleDialog() {
394 var el = document.getElementById('dialog');
395 el.classList.toggle("visible");
398 function toggleMenu() {
399 var el = document.getElementById('menu');
400 el.classList.toggle("visible");
404 // Initialize
406 function init() {
407 setUpPaperByClass( '.paper' );
410 window.addEventListener('DOMContentLoaded', init, false);