2014-10-01 11:06:25 +04:00
|
|
|
/* Copyright 2014+, Federico Zivolo, LICENSE at https://github.com/FezVrasta/bootstrap-material-design/blob/master/LICENSE.md */
|
|
|
|
/* globals CustomEvent */
|
2014-10-04 00:13:48 +04:00
|
|
|
window.ripples = {
|
2014-10-01 11:06:25 +04:00
|
|
|
init : function(withRipple) {
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
// Cross browser matches function
|
2014-10-04 00:46:56 +04:00
|
|
|
function matchesSelector(domElement, selector) {
|
2014-10-13 11:43:23 +04:00
|
|
|
var matches = domElement.matches ||
|
|
|
|
domElement.matchesSelector ||
|
|
|
|
domElement.webkitMatchesSelector ||
|
2014-10-04 00:46:56 +04:00
|
|
|
domElement.mozMatchesSelector ||
|
2014-10-13 11:43:23 +04:00
|
|
|
domElement.msMatchesSelector ||
|
|
|
|
domElement.oMatchesSelector;
|
2014-10-04 00:46:56 +04:00
|
|
|
return matches.call(domElement, selector);
|
2014-10-01 11:06:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// animations time
|
|
|
|
var rippleOutTime = 100,
|
|
|
|
rippleStartTime = 500;
|
|
|
|
|
|
|
|
// Helper to bind events on dynamically created elements
|
2014-10-13 12:50:34 +04:00
|
|
|
var bind = function(events, selector, callback) {
|
|
|
|
if (typeof events === "string") {
|
|
|
|
events = [events];
|
|
|
|
}
|
|
|
|
events.forEach(function(event) {
|
|
|
|
document.addEventListener(event, function(e) {
|
|
|
|
var target = (typeof e.detail !== "number") ? e.detail : e.target;
|
|
|
|
|
|
|
|
if (matchesSelector(target, selector)) {
|
|
|
|
callback(e, target);
|
|
|
|
}
|
|
|
|
});
|
2014-10-01 11:06:25 +04:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-10-13 12:50:34 +04:00
|
|
|
var rippleStart = function(e, target, callback) {
|
2014-10-01 11:06:25 +04:00
|
|
|
|
|
|
|
// Init variables
|
2014-10-13 11:43:23 +04:00
|
|
|
var $rippleWrapper = target,
|
|
|
|
$el = $rippleWrapper.parentNode,
|
|
|
|
$ripple = document.createElement("div"),
|
|
|
|
elPos = $el.getBoundingClientRect(),
|
2014-10-13 12:50:34 +04:00
|
|
|
// Mouse pos in most cases
|
|
|
|
mousePos = {x: e.clientX - elPos.left, y: ((window.ontouchstart) ? e.clientY - window.scrollY: e.clientY) - elPos.top},
|
2014-10-13 11:43:23 +04:00
|
|
|
scale = "scale(" + Math.round($rippleWrapper.offsetWidth / 5) + ")",
|
|
|
|
rippleEnd = new CustomEvent("rippleEnd", {detail: $ripple}),
|
|
|
|
_rippleOpacity = 0.1,
|
2014-10-01 11:06:25 +04:00
|
|
|
refreshElementStyle;
|
|
|
|
|
2014-10-13 12:50:34 +04:00
|
|
|
|
|
|
|
// If multitouch is detected or some other black magic suff is happening...
|
|
|
|
if (e.touches) {
|
|
|
|
mousePos = {x: e.touches[0].clientX - elPos.left, y: e.touches[0].clientY - elPos.top};
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log(mousePos);
|
|
|
|
|
2014-10-01 11:06:25 +04:00
|
|
|
$ripplecache = $ripple;
|
|
|
|
|
|
|
|
// Set ripple class
|
2014-10-13 11:43:23 +04:00
|
|
|
$ripple.className = "ripple";
|
|
|
|
|
2014-10-10 02:48:13 +04:00
|
|
|
// Move ripple to the mouse position
|
2014-10-01 11:06:25 +04:00
|
|
|
$ripple.setAttribute("style", "left:" + mousePos.x + "px; top:" + mousePos.y + "px;");
|
2014-10-13 11:43:23 +04:00
|
|
|
|
|
|
|
// Get the clicked target's text color, this will be applied to the ripple as background-color.
|
|
|
|
var targetColor = window.getComputedStyle($el).color;
|
|
|
|
|
|
|
|
// Convert the rgb color to an rgba color with opacity set to __rippleOpacity__
|
|
|
|
targetColor = targetColor.replace("rgb", "rgba").replace(")", ", " + _rippleOpacity + ")");
|
2014-10-01 11:06:25 +04:00
|
|
|
|
|
|
|
// Insert new ripple into ripple wrapper
|
2014-10-10 02:48:13 +04:00
|
|
|
$rippleWrapper.appendChild($ripple);
|
2014-10-01 11:06:25 +04:00
|
|
|
|
|
|
|
// 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;
|
2014-10-13 11:43:23 +04:00
|
|
|
|
2014-10-10 02:48:13 +04:00
|
|
|
// Set scale value, background-color and opacity to ripple and animate it
|
2014-10-01 11:06:25 +04:00
|
|
|
$ripple.className = "ripple ripple-on";
|
2014-10-10 02:48:13 +04:00
|
|
|
|
2014-10-13 11:43:23 +04:00
|
|
|
// Prepare the style of the ripple
|
|
|
|
var rippleStyle = [
|
|
|
|
$ripple.getAttribute("style"),
|
|
|
|
"background-color: " + targetColor,
|
|
|
|
"-ms-transform: " + scale,
|
|
|
|
"-moz-transform" + scale,
|
|
|
|
"-webkit-transform" + scale,
|
|
|
|
"transform: " + scale
|
|
|
|
];
|
|
|
|
|
|
|
|
// Apply the style
|
|
|
|
$ripple.setAttribute("style", rippleStyle.join(";"));
|
2014-10-01 11:06:25 +04:00
|
|
|
|
|
|
|
// 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);
|
2014-10-13 12:50:34 +04:00
|
|
|
if (callback) {
|
|
|
|
callback();
|
|
|
|
}
|
2014-10-01 11:06:25 +04:00
|
|
|
|
|
|
|
}, rippleStartTime);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
var rippleOut = function($ripple) {
|
|
|
|
// Clear previous animation
|
|
|
|
$ripple.className = "ripple ripple-on ripple-out";
|
|
|
|
|
|
|
|
// Let ripple fade out (with CSS)
|
|
|
|
setTimeout(function() {
|
|
|
|
$ripple.remove();
|
|
|
|
}, rippleOutTime);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Helper, need to know if mouse is up or down
|
|
|
|
var mouseDown = false;
|
2014-10-13 12:50:34 +04:00
|
|
|
bind(["mousedown", "touchstart"], "*", function() {
|
2014-10-01 11:06:25 +04:00
|
|
|
mouseDown = true;
|
2014-10-13 12:50:34 +04:00
|
|
|
});
|
|
|
|
bind(["mouseup", "touchend"], "*", function() {
|
2014-10-01 11:06:25 +04:00
|
|
|
mouseDown = false;
|
2014-10-13 12:50:34 +04:00
|
|
|
});
|
2014-10-01 11:06:25 +04:00
|
|
|
|
|
|
|
// 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);
|
2014-10-13 12:50:34 +04:00
|
|
|
if (window.ontouchstart === null) {
|
|
|
|
rippleStart(e, $rippleWrapper, function() {
|
|
|
|
// FIXME: ugly fix for first touchstart event on mobile devices...
|
|
|
|
$rippleWrapper.getElementsByClassName("ripple")[0].remove();
|
|
|
|
});
|
|
|
|
}
|
2014-10-01 11:06:25 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var $ripplecache;
|
|
|
|
|
|
|
|
// Events handler
|
|
|
|
// init RippleJS and start ripple effect on mousedown
|
2014-10-13 12:50:34 +04:00
|
|
|
bind(["mouseover", "touchstart"], withRipple, rippleInit);
|
2014-10-01 11:06:25 +04:00
|
|
|
|
|
|
|
// start ripple effect on mousedown
|
2014-10-13 12:50:34 +04:00
|
|
|
bind(["mousedown", "touchstart"], ".ripple-wrapper", function(e, $ripple) {
|
|
|
|
// Start ripple only on left or middle mouse click and touch click
|
|
|
|
if (e.which === 0 || e.which === 1 || e.which === 2) {
|
2014-10-04 10:43:25 +04:00
|
|
|
rippleStart(e, $ripple);
|
|
|
|
}
|
|
|
|
});
|
2014-10-13 11:43:23 +04:00
|
|
|
|
2014-10-01 11:06:25 +04:00
|
|
|
// if animation ends and user is not holding mouse then destroy the ripple
|
|
|
|
bind("rippleEnd", ".ripple-wrapper .ripple", function(e, $ripple) {
|
2014-10-02 13:36:05 +04:00
|
|
|
|
|
|
|
var $ripples = $ripple.parentNode.getElementsByClassName("ripple");
|
|
|
|
|
|
|
|
if (!mouseDown || ( $ripples[0] == $ripple && $ripples.length > 1)) {
|
2014-10-01 11:06:25 +04:00
|
|
|
rippleOut($ripple);
|
|
|
|
}
|
|
|
|
});
|
2014-10-13 11:43:23 +04:00
|
|
|
|
2014-10-01 11:06:25 +04:00
|
|
|
// Destroy ripple when mouse is not holded anymore if the ripple still exists
|
2014-10-13 12:50:34 +04:00
|
|
|
bind(["mouseup", "touchend"], ".ripple-wrapper", function() {
|
2014-10-01 11:06:25 +04:00
|
|
|
var $ripple = $ripplecache;
|
2014-10-12 12:40:19 +04:00
|
|
|
if ($ripple && $ripple.dataset.animating != 1) {
|
2014-10-01 11:06:25 +04:00
|
|
|
rippleOut($ripple);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
2014-10-02 13:36:05 +04:00
|
|
|
};
|