Rewritten ripples effect, now is standalone.

The ripples effect provided by some jQuery spaghetti now is a standalone plain javascript script, there was some improvements with this new version, for example now multiple ripples are possible.
This commit is contained in:
Federico Zivolo 2014-08-25 13:00:49 +02:00
parent 1a4277d0cd
commit 1023f8f6f9
7 changed files with 166 additions and 116 deletions

View File

@ -70,12 +70,17 @@ Remember to use the proper HTML markup to get radio and checkboxes styled correc
# Plugins # Plugins
Material Design for Bootstrap comes with styling support for various Bootstrap plugins, at the moment only one plugin is supported but others will come: Material Design for Bootstrap comes with styling support for various external scripts, at the moment only two scripts are supported but others will come:
### SnackbarJS ### SnackbarJS
Create snackbars and toasts with [SnackbarJS plugin](https://github.com/FezVrasta/snackbarjs), the default toast style is the squared one (snackbar style), if you like to use the rounded style (toast style) please add the `toast` class to the `style` option of SnackbarJS. Create snackbars and toasts with [SnackbarJS plugin](https://github.com/FezVrasta/snackbarjs), the default toast style is the squared one (snackbar style), if you like to use the rounded style (toast style) please add the `toast` class to the `style` option of SnackbarJS.
### RipplesJS
This is part of Material Design for Bootstrap project and is a plain Javascript script which creates the ripple effect on click of the defined elements.
At the moment RipplesJS has not an own repository but probably in future it will have one.
# Compatibility # Compatibility

View File

@ -315,44 +315,6 @@ h6,
.btn-group-flat { .btn-group-flat {
box-shadow: none !important; box-shadow: none !important;
} }
.ripple-wrapper {
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 100%;
height: 100%;
overflow: hidden;
border-radius: 2px;
}
.ripple {
position: absolute;
width: 20px;
height: 20px;
margin-left: -10px;
margin-top: -10px;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.05);
-webkit-transform: scale(1);
-ms-transform: scale(1);
transform: scale(1);
-webkit-transform-origin: 50%;
-ms-transform-origin: 50%;
transform-origin: 50%;
opacity: 0;
}
.ripple.ripple-on {
-webkit-transition: opacity 0.15s ease-in 0s, -webkit-transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s;
-ms-transition: opacity 0.15s ease-in 0s, -ms-transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s;
-moz-transition: opacity 0.15s ease-in 0s, -moz-transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s;
transition: opacity 0.15s ease-in 0s, transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s;
opacity: 1;
}
.ripple.ripple-out {
-webkit-transition: opacity 1s linear 0s !important;
transition: opacity 0.8s linear 0s !important;
opacity: 0;
}
.form-horizontal .checkbox { .form-horizontal .checkbox {
padding-top: 15px; padding-top: 15px;
} }

42
css-compiled/ripples.css Normal file
View File

@ -0,0 +1,42 @@
/* Generated by less 1.7.0 */
.withripple {
position: relative;
}
.ripple-wrapper {
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 100%;
height: 100%;
overflow: hidden;
border-radius: 2px;
}
.ripple {
position: absolute;
width: 20px;
height: 20px;
margin-left: -10px;
margin-top: -10px;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.05);
-webkit-transform: scale(1);
-ms-transform: scale(1);
transform: scale(1);
-webkit-transform-origin: 50%;
-ms-transform-origin: 50%;
transform-origin: 50%;
opacity: 0;
}
.ripple.ripple-on {
-webkit-transition: opacity 0.15s ease-in 0s, -webkit-transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s;
-ms-transition: opacity 0.15s ease-in 0s, -ms-transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s;
-moz-transition: opacity 0.15s ease-in 0s, -moz-transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s;
transition: opacity 0.15s ease-in 0s, transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s;
opacity: 1;
}
.ripple.ripple-out {
-webkit-transition: opacity 1s linear 0s !important;
transition: opacity 0.8s linear 0s !important;
opacity: 0;
}

View File

@ -26,9 +26,6 @@ body, h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
// Buttons // Buttons
@import "buttons.less"; @import "buttons.less";
// Ripple effect
@import "ripple.less";
// Checkboxes // Checkboxes
@import "checkboxes.less"; @import "checkboxes.less";

View File

@ -1,5 +1,8 @@
// main: material.less // out: ../css-compiled/ripples.css
.withripple {
position: relative;
}
.ripple-wrapper { .ripple-wrapper {
position: absolute; position: absolute;
top: 0; top: 0;

View File

@ -1,9 +1,8 @@
$(function (){ /* globals ripples */
// with ripple elements
var withRipple = ".btn:not('.btn-link'), .navbar a, .nav-tabs a";
// Add ripple elements to material buttons $(function (){
$(withRipple).append("<div class=ripple-wrapper><div class=ripple></div></div>");
ripples.init(".btn:not(.btn-link), .navbar a, .nav-tabs a, .withripple");
// Add fake-checkbox to material checkboxes // Add fake-checkbox to material checkboxes
$(".checkbox label input").after("<span class=ripple></span><span class=check></span><span class=box></span>"); $(".checkbox label input").after("<span class=ripple></span><span class=check></span><span class=box></span>");
@ -25,74 +24,6 @@ $(function (){
} }
}); });
var mouseDown = false;
$(document).mousedown(function() {
mouseDown = true;
}).mouseup(function() {
mouseDown = false;
});
// Material buttons engine
$(document).on("mousedown", withRipple, function(e){
// Cache elements
var $self = $(this),
$rippleWrapper = $self.find(".ripple-wrapper"),
$ripple = $self.find(".ripple");
// Remove previous animation
$ripple.attr("class", "ripple");
$rippleWrapper.stop(true, true);
// Get mouse position
var parentOffset = $self.offset();
var relX = e.pageX - parentOffset.left;
var relY = e.pageY - parentOffset.top;
// Move ripple to the click position
$ripple.attr({"style": "top: " + relY + "px; left:" + relX + "px"});
// Start the animation
$rippleWrapper.attr("class", "ripple-wrapper").data("animating", true);
var scaleVal = "scale(" + Math.round($rippleWrapper.width() / 10) + ")";
$ripple.attr("class", "ripple ripple-on").css({
"-ms-transform": scaleVal,
"-moz-transform": scaleVal,
"-webkit-transform": scaleVal,
"transform": scaleVal
});
setTimeout(function() {
$rippleWrapper.attr("class", "ripple-wrapper").data("animating", false).trigger("rippleEnd");
}, 500);
})
.on("rippleEnd", withRipple, function() {
if (!mouseDown) {
var $self = $(this),
$rippleWrapper = $self.find(".ripple-wrapper"),
$ripple = $self.find(".ripple");
rippleOut($ripple, $rippleWrapper);
}
})
.on("mouseup mouseleave", withRipple, function() {
var $self = $(this),
$rippleWrapper = $self.find(".ripple-wrapper"),
$ripple = $self.find(".ripple");
if (!$rippleWrapper.data("animating")) {
rippleOut($ripple, $rippleWrapper);
}
});
var rippleOut = function($ripple, $rippleWrapper) {
$ripple.attr("class", "ripple ripple-on ripple-out");
$rippleWrapper.fadeOut(800, function() {
$rippleWrapper.attr("class", "ripple-wrapper").attr("style", "");
$ripple.attr("class", "ripple").attr("style", "");
});
};
// Material inputs engine (ripple effect) // Material inputs engine (ripple effect)
$(document).on("click", ".checkbox label, .radio label", function() { $(document).on("click", ".checkbox label, .radio label", function() {
var $ripple = $(this).find(".ripple"), var $ripple = $(this).find(".ripple"),

110
scripts/ripples.js Normal file
View File

@ -0,0 +1,110 @@
/* globals CustomEvent */
var ripples = {
init : function(withRipple) {
"use strict";
// Helper to bind events on dynamically created elements
var bind = function(event, selector, callback) {
document.addEventListener(event, function(e) {
var target = (typeof e.detail !== "number") ? e.detail : e.target;
if (target.matches(selector)) {
callback(e, target);
}
});
};
var rippleStart = function(e, target) {
// Init variables
var $rippleWrapper = (target.matches(".ripple-wrapper")) ? target : target.parentNode,
$el = $rippleWrapper.parentNode,
$ripple = document.createElement("div"),
elPos = $el.getBoundingClientRect(),
mousePos = {x: e.clientX - elPos.left, y: e.clientY - elPos.top},
scale = "transform:scale(" + Math.round($rippleWrapper.offsetWidth / 5) + ")",
rippleEnd = new CustomEvent("rippleEnd", {detail: $ripple}),
refreshElementStyle;
// Set ripple class
$ripple.className = "ripple";
// Move ripple to the mouse position
$ripple.setAttribute("style", "left:" + mousePos.x + "px; top:" + mousePos.y + "px;");
// Insert new ripple into ripple wrapper
$rippleWrapper.appendChild($ripple);
// Make sure the ripple has the class applied (ugly hack but it works)
refreshElementStyle = window.getComputedStyle($ripple).opacity;
// Let other funtions know that this element is animating
$ripple.dataset.animating = 1;
// Set scale value to ripple and animate it
$ripple.className = "ripple ripple-on";
$ripple.setAttribute("style", $ripple.getAttribute("style") + ["-ms-" + scale,"-moz-" + scale,"-webkit-" + scale,scale].join(";"));
// This function is called when the animation is finished
setTimeout(function() {
// Let know to other functions that this element has finished the animation
$ripple.dataset.animating = 0;
document.dispatchEvent(rippleEnd);
}, 500);
};
var rippleOut = function($ripple) {
// Clear previous animation
$ripple.className = "ripple ripple-on ripple-out";
// Let ripple fade out (with CSS)
setTimeout(function() {
$ripple.remove();
}, 1000);
};
// Helper, need to know if mouse is up or down
var mouseDown = false;
document.body.onmousedown = function() {
mouseDown = true;
};
document.body.onmouseup = function() {
mouseDown = false;
};
// Append ripple wrapper if not exists already
var rippleInit = function(e, target) {
if (target.getElementsByClassName("ripple-wrapper").length === 0) {
target.className += " withripple";
var $rippleWrapper = document.createElement("div");
$rippleWrapper.className = "ripple-wrapper";
target.appendChild($rippleWrapper);
rippleStart(e, $rippleWrapper);
}
};
// Events handler
// init RippleJS and start ripple effect on mousedown
bind("mousedown", withRipple, rippleInit);
// start ripple effect on mousedown
bind("mousedown", ".ripple-wrapper, .ripple", rippleStart);
// if animation ends and user is not holding mouse then destroy the ripple
bind("rippleEnd", ".ripple-wrapper, .ripple", function(e, $ripple) {
if (!mouseDown) {
rippleOut($ripple);
}
});
// Destroy ripple when mouse is not holded anymore if the ripple still exists
bind("mouseup", ".ripple-wrapper, .ripple", function(e, $ripple) {
if ($ripple.dataset.animating != 1) {
rippleOut($ripple);
}
});
}
};