Skip to content

Instantly share code, notes, and snippets.

@thomaswilburn
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomaswilburn/b4f0f3b9dcbe512ee45a to your computer and use it in GitHub Desktop.
Save thomaswilburn/b4f0f3b9dcbe512ee45a to your computer and use it in GitHub Desktop.
Sample code report for WEB150
/**
* Originally taken from:
* https://raw.githubusercontent.com/GoogleChrome/devsummit/7ba921827a7cd080e31291275c0eda394ec21c9d/src/static/scripts/components/button.js
*
* This code enables the "material ripple" animation on buttons for the Chrome Dev Summit
* site (https://developer.chrome.com/devsummit/). It finds all buttons on the page, and
* adds an animation to them that plays on click. Button elements tend to look like this:
*
* <button data-embed="..." class="paper-button session__fab">
* <div class="session__fab-inner">
* <div class="button__ripple"></div>
* </div>
* </button>
*
* Executive summary:
*
* Paper button elements contain an inner "ripple" effect that plays on click, where a
* circle expands out from the center of a mouse click. To create this, a circular, semi-
* transparent element is positioned at the click and scaled down. Animations are then
* enabled on the button, and the transform is updated, causing the element to expand
* out to its original size.
*
* Because this module maintains a single animation state at any time, and ignores any
* clicks while that animation is running, it's basically only good for a single button
* press, and doesn't really support multitouch in any way. A future improvement for
* the module would be to use a closure or a state object linked to individual elements,
* so that multiple ripple animations can run simultaneously. This is not a huge
* burden on the CPU, frankly, since these animations are run completely via CSS.
*
*/
/**
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
CDS.Button = (function() {
"use strict";
// First, find all buttons on the page, and set up some global state variables.
var buttons = document.querySelectorAll('.paper-button');
var button, bound, x, y, ripple, size, transformString;
var frameCount = 0;
//For each paper button...
for (var b = 0; b < buttons.length; b++) {
//Find the size of the button from its outer bounding rectangle (set from CSS) and double it
button = buttons[b];
bound = button.getBoundingClientRect();
size = Math.max(bound.width, bound.height) * 2;
//if there's a ripple element, locate that for animation
ripple = button.querySelector('.button__ripple');
if (!ripple)
continue;
//Set the ripple to be twice the size of the button (see above)
ripple.style.width = size + 'px';
ripple.style.height = size + 'px';
//add the onClick listener
button.addEventListener('click', onClick);
}
function onClick(evt) {
//If the animation is already running, do nothing.
if (frameCount > 0)
return;
//If the element has a Youtube video attribute (data-embed), embed and play it.
if (evt.currentTarget.dataset && evt.currentTarget.dataset.embed)
CDS.VideoEmbedder.embed(evt.currentTarget);
//If the element has a data-url attribute, navigate to that URL.
if (evt.currentTarget.dataset && evt.currentTarget.dataset.url)
window.location = evt.currentTarget.dataset.url;
//Start the animation by setting the frameCount flag
//I suspect that frameCount being numerical is a holdover from a more elaborate version of this animation.
frameCount = 1;
//We get the size of the element, and create a transform that centers it on the mouse click at a very small size.
//Effectively, this makes it very small, so that when we remove the transform, it'll expand outward.
bound = evt.currentTarget.getBoundingClientRect();
x = Math.round(evt.clientX - bound.left);
y = Math.round(evt.clientY - bound.top);
transformString = 'translate(-50%, -50%) ' +
'translate(' + x + 'px, ' + y + 'px) ' +
'scale(0.0001, 0.0001)';
//Next we find the ripple, and set its transform property, thus positioning it for the start of the animation
ripple = evt.currentTarget.querySelector('.button__ripple');
ripple.style.webkitTransform = transformString;
ripple.style.transform = transformString;
ripple.style.opacity = '0.4';
//Remove animations to instantly move the ripple into its scaled position
ripple.classList.remove('button__ripple--animate');
//Now wait a frame, then call reset, which will actually play the animation
requestAnimFrame(reset);
}
function reset() {
//If the framecount is still high, wait before triggering animation (never happens)
if (frameCount-- > 0) {
requestAnimFrame(reset);
} else {
//create a new transform where the ripple is still centered, but at its normal size
transformString = 'translate(-50%, -50%) ' +
'translate(' + x + 'px, ' + y + 'px)' +
'scale(1, 1)';
//set that transform, and re-enable animations
ripple.style.webkitTransform = transformString;
ripple.style.transform = transformString;
ripple.style.opacity = '0';
ripple.classList.add('button__ripple--animate');
//At this point the button has a faint, fading "ripple" expand across its surface.
//Ironically, this is incredibly hard to notice, because the button is also contracting at the same time.
//If the styles of the .hidden class are disabled, you can see it actually run more effectively.
}
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment