mirror of
https://github.com/mdbootstrap/mdb-ui-kit.git
synced 2024-12-03 23:13:45 +03:00
333 lines
8.9 KiB
JavaScript
333 lines
8.9 KiB
JavaScript
import Util from "./util";
|
|
|
|
const Ripples = ($ => {
|
|
/**
|
|
* ------------------------------------------------------------------------
|
|
* Constants
|
|
* ------------------------------------------------------------------------
|
|
*/
|
|
const NAME = "ripples";
|
|
const DATA_KEY = `bmd.${NAME}`;
|
|
const JQUERY_NAME = `bmd${NAME.charAt(0).toUpperCase() + NAME.slice(1)}`;
|
|
const JQUERY_NO_CONFLICT = $.fn[JQUERY_NAME];
|
|
|
|
const ClassName = {
|
|
CONTAINER: "ripple-container",
|
|
DECORATOR: "ripple-decorator"
|
|
};
|
|
|
|
const Selector = {
|
|
CONTAINER: `.${ClassName.CONTAINER}`,
|
|
DECORATOR: `.${ClassName.DECORATOR}` //,
|
|
};
|
|
|
|
const Default = {
|
|
container: {
|
|
template: `<div class='${ClassName.CONTAINER}'></div>`
|
|
},
|
|
decorator: {
|
|
template: `<div class='${ClassName.DECORATOR}'></div>`
|
|
},
|
|
trigger: {
|
|
start: "mousedown touchstart",
|
|
end: "mouseup mouseleave touchend"
|
|
},
|
|
touchUserAgentRegex: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i,
|
|
duration: 500
|
|
};
|
|
|
|
/**
|
|
* ------------------------------------------------------------------------
|
|
* Class Definition
|
|
* ------------------------------------------------------------------------
|
|
*/
|
|
class Ripples {
|
|
constructor($element, config) {
|
|
this.$element = $element;
|
|
|
|
// console.log(`Adding ripples to ${Util.describe(this.$element)}`) // eslint-disable-line no-console
|
|
this.config = $.extend(true, {}, Default, config);
|
|
|
|
// attach initial listener
|
|
this.$element.on(this.config.trigger.start, event => {
|
|
this._onStartRipple(event);
|
|
});
|
|
}
|
|
|
|
dispose() {
|
|
this.$element.data(DATA_KEY, null);
|
|
this.$element = null;
|
|
this.$container = null;
|
|
this.$decorator = null;
|
|
this.config = null;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// private
|
|
|
|
_onStartRipple(event) {
|
|
// Verify if the user is just touching on a device and return if so
|
|
if (this._isTouch() && event.type === "mousedown") {
|
|
return;
|
|
}
|
|
|
|
// Find or create the ripple container element
|
|
this._findOrCreateContainer();
|
|
|
|
// Get relY and relX positions of the container element
|
|
let relY = this._getRelY(event);
|
|
let relX = this._getRelX(event);
|
|
|
|
// If relY and/or relX are false, return the event
|
|
if (!relY && !relX) {
|
|
return;
|
|
}
|
|
|
|
// set the location and color each time (even if element is cached)
|
|
this.$decorator.css({
|
|
left: relX,
|
|
top: relY,
|
|
"background-color": this._getRipplesColor()
|
|
});
|
|
|
|
// Make sure the ripple has the styles applied (ugly hack but it works)
|
|
this._forceStyleApplication();
|
|
|
|
// Turn on the ripple animation
|
|
this.rippleOn();
|
|
|
|
// Call the rippleEnd function when the transition 'on' ends
|
|
setTimeout(() => {
|
|
this.rippleEnd();
|
|
}, this.config.duration);
|
|
|
|
// Detect when the user leaves the element to cleanup if not already done?
|
|
this.$element.on(this.config.trigger.end, () => {
|
|
if (this.$decorator) {
|
|
// guard against race condition/mouse attack
|
|
this.$decorator.data("mousedown", "off");
|
|
|
|
if (this.$decorator.data("animating") === "off") {
|
|
this.rippleOut();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
_findOrCreateContainer() {
|
|
if (!this.$container || !this.$container.length > 0) {
|
|
this.$element.append(this.config.container.template);
|
|
this.$container = this.$element.find(Selector.CONTAINER);
|
|
}
|
|
|
|
// always add the rippleElement, it is always removed
|
|
this.$container.append(this.config.decorator.template);
|
|
this.$decorator = this.$container.find(Selector.DECORATOR);
|
|
}
|
|
|
|
// Make sure the ripple has the styles applied (ugly hack but it works)
|
|
_forceStyleApplication() {
|
|
return window.getComputedStyle(this.$decorator[0]).opacity;
|
|
}
|
|
|
|
/**
|
|
* Get the relX
|
|
*/
|
|
_getRelX(event) {
|
|
let wrapperOffset = this.$container.offset();
|
|
|
|
let result = null;
|
|
if (!this._isTouch()) {
|
|
// Get the mouse position relative to the ripple wrapper
|
|
result = event.pageX - wrapperOffset.left;
|
|
} else {
|
|
// Make sure the user is using only one finger and then get the touch
|
|
// position relative to the ripple wrapper
|
|
event = event.originalEvent;
|
|
|
|
if (event.touches.length === 1) {
|
|
result = event.touches[0].pageX - wrapperOffset.left;
|
|
} else {
|
|
result = false;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get the relY
|
|
*/
|
|
_getRelY(event) {
|
|
let containerOffset = this.$container.offset();
|
|
let result = null;
|
|
|
|
if (!this._isTouch()) {
|
|
/**
|
|
* Get the mouse position relative to the ripple wrapper
|
|
*/
|
|
result = event.pageY - containerOffset.top;
|
|
} else {
|
|
/**
|
|
* Make sure the user is using only one finger and then get the touch
|
|
* position relative to the ripple wrapper
|
|
*/
|
|
event = event.originalEvent;
|
|
|
|
if (event.touches.length === 1) {
|
|
result = event.touches[0].pageY - containerOffset.top;
|
|
} else {
|
|
result = false;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get the ripple color
|
|
*/
|
|
_getRipplesColor() {
|
|
let color = this.$element.data("ripple-color")
|
|
? this.$element.data("ripple-color")
|
|
: window.getComputedStyle(this.$element[0]).color;
|
|
return color;
|
|
}
|
|
|
|
/**
|
|
* Verify if the client is using a mobile device
|
|
*/
|
|
_isTouch() {
|
|
return this.config.touchUserAgentRegex.test(navigator.userAgent);
|
|
}
|
|
|
|
/**
|
|
* End the animation of the ripple
|
|
*/
|
|
rippleEnd() {
|
|
if (this.$decorator) {
|
|
// guard against race condition/mouse attack
|
|
this.$decorator.data("animating", "off");
|
|
|
|
if (this.$decorator.data("mousedown") === "off") {
|
|
this.rippleOut(this.$decorator);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Turn off the ripple effect
|
|
*/
|
|
rippleOut() {
|
|
this.$decorator.off();
|
|
|
|
if (Util.transitionEndSupported()) {
|
|
this.$decorator.addClass("ripple-out");
|
|
} else {
|
|
this.$decorator.animate({ opacity: 0 }, 100, () => {
|
|
this.$decorator.trigger("transitionend");
|
|
});
|
|
}
|
|
|
|
this.$decorator.on(Util.transitionEndSelector(), () => {
|
|
if (this.$decorator) {
|
|
this.$decorator.remove();
|
|
this.$decorator = null;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Turn on the ripple effect
|
|
*/
|
|
rippleOn() {
|
|
let size = this._getNewSize();
|
|
|
|
if (Util.transitionEndSupported()) {
|
|
this.$decorator
|
|
.css({
|
|
"-ms-transform": `scale(${size})`,
|
|
"-moz-transform": `scale(${size})`,
|
|
"-webkit-transform": `scale(${size})`,
|
|
transform: `scale(${size})`
|
|
})
|
|
.addClass("ripple-on")
|
|
.data("animating", "on")
|
|
.data("mousedown", "on");
|
|
} else {
|
|
this.$decorator.animate(
|
|
{
|
|
width:
|
|
Math.max(
|
|
this.$element.outerWidth(),
|
|
this.$element.outerHeight()
|
|
) * 2,
|
|
height:
|
|
Math.max(
|
|
this.$element.outerWidth(),
|
|
this.$element.outerHeight()
|
|
) * 2,
|
|
"margin-left":
|
|
Math.max(
|
|
this.$element.outerWidth(),
|
|
this.$element.outerHeight()
|
|
) * -1,
|
|
"margin-top":
|
|
Math.max(
|
|
this.$element.outerWidth(),
|
|
this.$element.outerHeight()
|
|
) * -1,
|
|
opacity: 0.2
|
|
},
|
|
this.config.duration,
|
|
() => {
|
|
this.$decorator.trigger("transitionend");
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the new size based on the element height/width and the ripple width
|
|
*/
|
|
_getNewSize() {
|
|
return (
|
|
Math.max(this.$element.outerWidth(), this.$element.outerHeight()) /
|
|
this.$decorator.outerWidth() *
|
|
2.5
|
|
);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// static
|
|
|
|
static _jQueryInterface(config) {
|
|
return this.each(function() {
|
|
let $element = $(this);
|
|
let data = $element.data(DATA_KEY);
|
|
|
|
if (!data) {
|
|
data = new Ripples($element, config);
|
|
$element.data(DATA_KEY, data);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ------------------------------------------------------------------------
|
|
* jQuery
|
|
* ------------------------------------------------------------------------
|
|
*/
|
|
$.fn[JQUERY_NAME] = Ripples._jQueryInterface;
|
|
$.fn[JQUERY_NAME].Constructor = Ripples;
|
|
$.fn[JQUERY_NAME].noConflict = () => {
|
|
$.fn[JQUERY_NAME] = JQUERY_NO_CONFLICT;
|
|
return Ripples._jQueryInterface;
|
|
};
|
|
|
|
return Ripples;
|
|
})(jQuery);
|
|
|
|
export default Ripples;
|