Lollipop click animation

Android 5.0 Lollipop brought us some fancy new design (material design). This trend is finding its way onto the web. So we, as webdevelopers, should find techniques to transpose these OS capabilities to the interwebz.

by BramVanroy

HTML

<div class="thumbnail"></div>
<div class="buttons">
    <button class="blue-light white">Click me</button>
    <button class="yellow">Me too!</button>
    <button class="pink">And me?</button>
    <button class="blue-dark white">Please?</button>
</div>
<div id="description">
     <h1>Lollipop click animations in CSS</h1>

    <p>Android 5.0 Lollipop brought us some fancy new design (<em>material</em> design). This trend is finding its way onto the web. So we, as webdevelopers, should find techniques to transpose these OS capabilities to the interwebz.</p>
    <p>I took the liberty to edit <a href="http://codepen.io/dwijitsolutions/pen/myrvJW" target="_blank">this pen</a> by dwijitsolutions. I used jQuery - 'cause <em>I'm lovin' it</em> - and added some improvements overall. Especially automatically determining the correct width for the bubble and its position, and fetching the correct timeOut before deleting the added elements are huge additions.</p>
    <p>You could also apply this technology on links (<code>&lt;a&gt;&lt;/a&gt;</code>), but that would be quite useless for off-page navigation. In other words, when you click a link, the browser immediately redirects you so you won't see the animation anyway. The animation is ideally used for buttons that do an on-page action, e.g. ajax calls and form submits. Note that <code>&lt;input type="button"&gt;</code> isn't supported yet. Use <code>&lt;button&gt;</code> instead.</p>
     <h2>To Do</h2>

    <ul>
        <li><s>Adding options in JS (e.g. text overlay?, background color?, speed?)</s>

        </li>
        <li><s>Adding animation speed through a variable</s>

        </li>
        <li><s>Multiple instances of plugin</s>

        </li>
        <li><s>Only output <em>one</em> stylesheet</s>

        </li>
        <li>Disable click after x clicks</li>
        <li>Support for inputs</li>
        <li>Testing browser compatibility</li>
        <li>Optimising/minifying</li>
    </ul>
</div>
<link...

SCSS

/* STYLE FOR THE ANIMATIONS
    These styles are required 
    for the lollipop click effect
*/

.bubble-wrap {
    overflow: hidden;
    position: relative;
    box-sizing: border-box;
    .bubble {
        position: absolute;
        z-index: 1;
        pointer-events: none;
        border-radius: 50%;
        opacity: 0;
        transform: scale(0);
    }
    &.clicked:after {opacity: 1;}
    &:after {
        box-sizing: border-box;
        opacity: 0;
        content: attr(data-text-overlay);
        width: 100%;
        height: 100%;
        position: absolute;
        z-index: 2;
        left: 0;
        top: 0;
        vertical-align: inherit;
        text-align: inherit;
        white-space: inherit;
        font: inherit;
        color: inherit;
        text-shadow: inherit;
        line-height: inherit;
        padding: inherit;
        text-rendering: optimizeLegibility;
    }
}
@keyframes bubble-effect-in {
    0% {
        opacity: 0;
        transform: scale(0);
    }
    100% {
        opacity: 1;
        transform: scale(1);
    }
}
@keyframes bubble-effect-out {
    from {opacity: 1;}
    to {opacity: 0;}
}

/* STYLE FOR THIS FIDDLE
    The styles below are merely for illustration, and
    not required for the animation
*/

*, *:before, *:after {
    box-sizing: border-box;
}

body {
    font: 300 16px/1.4 Roboto,"Roboto",Helvetica,Arial,sans-serif;
    max-width: 1280px;
    min-width: 480px;
    margin: 0 auto;
}
em {font-style: italic;}
bold {font-weight: 700;}
h1 {
    font: {
        size: 36px;
        weight: 100;
    };
    text-align: center;
    border-bottom: 1px solid;
}
p {margin: 1.2em 0;}
a {
    color: inherit;
    text-decoration: underline;
    
    &:hover {text-decoration: none;}
}
code {
    font: {
        family: Courier New, monospace, sans-serif;
        size: 13px;
    };
    background: #eee;
    white-space: pre-wrap;
    padding: 1px 5px;
}
.thumbnail {
    background: url('http://i.imgur.com/D8eok3f.jpg') no-repeat...

JavaScript

(function ($) {
    $.lollipopbutton = function (el, options) {
        var args = $.extend({
            textOverlay: true,
            bubbleColor: "rgb(255,255,255)",
            bubbleOpacity: 0.36,
            bubbleInSpeed: "1.6s",
            opacityOutSpeed: 800,
            textOverlaySpeed: 600,
            maxClicks: 3
        }, options),
            bubbleInSpeedMs = to_ms(args.bubbleInSpeed),
            opacityOutSpeedMs = to_ms(args.opacityOutSpeed),
            totalTimeout = bubbleInSpeedMs + opacityOutSpeedMs,
            textOverlaySpeedMs = to_ms(args.textOverlaySpeed),
            bubbleColorRgb;

        $(el).addClass("bubble-wrap").data({
            "counter": 0,
                "max-clicks": args.maxClicks,
                "timeout": totalTimeout,
                "text-overlay": args.textOverlay
        });

        var bubbleWrap = $(".bubble-wrap");

        bubbleWrap.each(function () {
            var $this = $(this);
            $this.data({
                "bubble-size": Math.sqrt(Math.pow($this.outerWidth(), 2) + Math.pow($this.outerHeight(), 2)) * 2
            });
        });

        // Check if rgbGenerator is used
        if ($.rgbGenerator) {
            bubbleColorRgb = $.rgbGenerator(args.bubbleColor);
        }
        // If not, only accept RGB values
        else {
            var rgb = args.bubbleColor.replace(/(^rgb?\(|\)$|\s)/g, "").split(",").map(Number);
            bubbleColorRgb = {
                r: rgb[0],
                g: rgb[1],
                b: rgb[2]
            };
        }

        if (args.textOverlay === true) {
            bubbleWrap.each(function () {
                var $this = $(this);
                $this.attr("data-text-overlay", $this.text());
            });
        }

        if (!$("#lollipopbutton-css").length) {
            $("<style>").attr("id", "lollipopbutton-css").appendTo("head");
        }

        var bubble_css = "".concat(
        el + " .bubble {",
           ...