mdb-ui-kit/js/src/ripples.js

299 lines
8.1 KiB
JavaScript
Raw Normal View History

2015-12-01 20:22:13 +03:00
import Util from './util'
2015-11-30 23:57:40 +03:00
const Ripples = (($) => {
2015-11-21 02:50:50 +03:00
/**
2015-11-30 23:57:40 +03:00
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
2015-11-21 02:50:50 +03:00
*/
2015-11-30 23:57:40 +03:00
const NAME = 'ripples'
2015-12-01 20:58:10 +03:00
const DATA_KEY = `mdb.${NAME}`
2015-11-30 23:57:40 +03:00
const JQUERY_NO_CONFLICT = $.fn[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: `${ClassName.DECORATOR}'></div>`
},
trigger: {
start: 'mousedown touchstart',
end: 'mouseup mouseleave touchend'
},
touchUserAgentRegex: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i,
duration: 500
}
2015-11-30 23:57:40 +03:00
2015-11-21 02:50:50 +03:00
/**
2015-11-30 23:57:40 +03:00
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
2015-11-21 02:50:50 +03:00
*/
2015-11-30 23:57:40 +03:00
class Ripples {
2015-11-21 02:50:50 +03:00
constructor(element, config) {
this.element = $(element)
this.config = $.extend({}, Default, config)
// attach initial listener
this.element.on(this.config.triggerStart, this._onStartRipple)
2015-11-30 23:57:40 +03:00
}
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
dispose() {
$.removeData(this.element, DATA_KEY)
this.element = null
this.containerElement = null
this.decoratorElement = null
this.config = null
2015-11-30 23:57:40 +03:00
}
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
// ------------------------------------------------------------------------
// private
2015-11-21 02:50:50 +03:00
2015-12-01 00:47:47 +03:00
_onStartRipple(event) {
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
// Verify if the user is just touching on a device and return if so
if (this.isTouch() && event.type === 'mousedown') {
2015-11-30 23:57:40 +03:00
return
2015-11-21 02:50:50 +03:00
}
2015-12-01 00:47:47 +03:00
// Find or create the ripple container element
this._findOrCreateContainer()
2015-11-21 02:50:50 +03:00
2015-12-01 00:47:47 +03:00
// Get relY and relX positions of the container element
let relY = this._getRelY(event)
let relX = this._getRelX(event)
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
// If relY and/or relX are false, return the event
if (!relY && !relX) {
return
2015-11-21 02:50:50 +03:00
}
2015-12-01 00:47:47 +03:00
// set the location and color each time (even if element is cached)
this.decoratorElement.css({
'left': relX,
'top': relY,
'background-color': this._getRipplesColor()
2015-11-30 23:57:40 +03:00
})
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
// Make sure the ripple has the styles applied (ugly hack but it works)
2015-12-01 00:47:47 +03:00
this._forceStyleApplication()
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
// Turn on the ripple animation
2015-12-01 00:47:47 +03:00
this.rippleOn()
2015-11-21 02:50:50 +03:00
// Call the rippleEnd function when the transition 'on' ends
2015-11-30 23:57:40 +03:00
setTimeout(() => {
2015-12-01 00:47:47 +03:00
this.rippleEnd()
}, this.config.duration)
2015-11-21 02:50:50 +03:00
2015-12-01 00:47:47 +03:00
// Detect when the user leaves the element (attach only when necessary for performance)
this.element.on(this.config.triggerEnd, () => {
this.decoratorElement.data('mousedown', 'off')
2015-11-21 02:50:50 +03:00
if (this.decoratorElement.data('animating') === 'off') {
2015-12-01 00:47:47 +03:00
this.rippleOut()
2015-11-30 23:57:40 +03:00
}
})
}
2015-11-21 02:50:50 +03:00
2015-12-01 00:47:47 +03:00
_findOrCreateContainer() {
if (!this.containerElement || !this.containerElement.length > 0) {
this.element.append(this.config.container.template)
this.containerElement = this.element.find(Selector.CONTAINER)
2015-12-01 00:47:47 +03:00
}
// always add the rippleElement, it is always removed
this.containerElement.append(this.config.element.template)
this.decoratorElement = this.containerElement.find(Selector.DECORATOR)
2015-12-01 00:47:47 +03:00
}
// Make sure the ripple has the styles applied (ugly hack but it works)
_forceStyleApplication() {
return window.getComputedStyle(this.decoratorElement[0]).opacity
2015-12-01 00:47:47 +03:00
}
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
/**
* Get the relX
*/
2015-12-01 00:47:47 +03:00
_getRelX(event) {
let wrapperOffset = this.containerElement.offset()
2015-11-30 23:57:40 +03:00
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
}
}
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
return result
}
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
/**
* Get the relY
*/
2015-12-01 00:47:47 +03:00
_getRelY(event) {
let containerOffset = this.containerElement.offset()
2015-11-30 23:57:40 +03:00
let result = null
if (!this.isTouch()) {
/**
* Get the mouse position relative to the ripple wrapper
*/
2015-12-01 00:47:47 +03:00
result = event.pageY - containerOffset.top
2015-11-30 23:57:40 +03:00
} 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) {
2015-12-01 00:47:47 +03:00
result = event.touches[0].pageY - containerOffset.top
2015-11-30 23:57:40 +03:00
} else {
result = false
2015-11-21 02:50:50 +03:00
}
2015-11-30 23:57:40 +03:00
}
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
return result
}
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
/**
* Get the ripple color
*/
2015-12-01 00:47:47 +03:00
_getRipplesColor() {
let color = this.element.data('ripple-color') ? this.element.data('ripple-color') : window.getComputedStyle(this.element[0]).color
2015-11-30 23:57:40 +03:00
return color
}
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
/**
* Verify if the client is using a mobile device
*/
isTouch() {
return this.config.touchUserAgentRegex.test(navigator.userAgent)
2015-11-21 02:50:50 +03:00
}
2015-11-30 23:57:40 +03:00
/**
* End the animation of the ripple
*/
2015-12-01 00:47:47 +03:00
rippleEnd() {
this.decoratorElement.data('animating', 'off')
2015-11-21 02:50:50 +03:00
if (this.decoratorElement.data('mousedown') === 'off') {
this.rippleOut(this.decoratorElement)
2015-11-21 02:50:50 +03:00
}
}
2015-11-30 23:57:40 +03:00
/**
* Turn off the ripple effect
*/
2015-12-01 00:47:47 +03:00
rippleOut() {
this.decoratorElement.off()
2015-11-21 02:50:50 +03:00
2015-12-01 20:22:13 +03:00
if (Util.transitionEndSupported()) {
this.decoratorElement.addClass('ripple-out')
2015-11-30 23:57:40 +03:00
} else {
this.decoratorElement.animate({ 'opacity': 0 }, 100, () => {
this.decoratorElement.triggerStart('transitionend')
2015-11-30 23:57:40 +03:00
})
}
2015-11-21 02:50:50 +03:00
this.decoratorElement.on(Util.transitionEndSelector(), () => {
this.decoratorElement.remove()
this.decoratorElement = null
2015-11-30 23:57:40 +03:00
})
}
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
/**
* Turn on the ripple effect
*/
2015-12-01 00:47:47 +03:00
rippleOn() {
let size = this._getNewSize()
2015-11-30 23:57:40 +03:00
2015-12-01 20:22:13 +03:00
if (Util.transitionEndSupported()) {
this.decoratorElement
2015-11-30 23:57:40 +03:00
.css({
'-ms-transform': `scale(${size})`,
'-moz-transform': `scale(${size})`,
'-webkit-transform': `scale(${size})`,
'transform': `scale(${size})`
2015-11-30 23:57:40 +03:00
})
.addClass('ripple-on')
.data('animating', 'on')
.data('mousedown', 'on')
2015-11-30 23:57:40 +03:00
} else {
this.decoratorElement.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.decoratorElement.triggerStart('transitionend')
2015-11-30 23:57:40 +03:00
})
}
}
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
/**
* Get the new size based on the element height/width and the ripple width
*/
2015-12-01 00:47:47 +03:00
_getNewSize() {
return (Math.max(this.element.outerWidth(), this.element.outerHeight()) / this.decoratorElement.outerWidth()) * 2.5
2015-11-30 23:57:40 +03:00
}
2015-11-21 02:50:50 +03:00
2015-12-01 00:47:47 +03:00
// ------------------------------------------------------------------------
// static
2015-11-21 02:50:50 +03:00
2015-12-01 20:58:10 +03:00
static _jQueryInterface(config) {
2015-11-30 23:57:40 +03:00
return this.each(() => {
let element = $(this)
let data = element.data(DATA_KEY)
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
if (!data) {
2015-12-01 20:58:10 +03:00
data = new Ripples(this, config)
2015-11-30 23:57:40 +03:00
element.data(DATA_KEY, data)
}
})
2015-11-21 02:50:50 +03:00
}
2015-11-30 23:57:40 +03:00
}
2015-11-21 02:50:50 +03:00
/**
2015-11-30 23:57:40 +03:00
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
2015-11-21 02:50:50 +03:00
*/
2015-11-30 23:57:40 +03:00
$.fn[NAME] = Ripples._jQueryInterface
$.fn[NAME].Constructor = Ripples
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Ripples._jQueryInterface
}
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
return Ripples
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
})(jQuery)
2015-11-21 02:50:50 +03:00
2015-11-30 23:57:40 +03:00
export default Ripples