toloframework
Version:
Javascript/HTML/CSS compiler for Firefox OS or nodewebkit apps using modules in the nodejs style.
155 lines (143 loc) • 4.53 kB
JavaScript
;
/**
* @module tfw.touchable
*
* @description
* Turn a DOM element into a touchable one with material design
* animation: a growing transparent disk.
*
* @example
* var Touchable = require('tfw.touchable');
* var div = document.querySelector('#button');
* var touchable = new Touchable( div, {
* opacity: .2,
* color: "white"
* });
* touchable.tap.add(function() { ... });
* touchable.press.add(function() { ... });
*/
module.exports = Touchable;
const $ = require("dom"),
Fx = require("dom.fx"),
Listeners = require("tfw.listeners");
/**
* @class Touchable
* @param {any} elem - DOMElement or string representing a DOM ID.
* @param {object} opts - Options.
* @param {boolean} opts.enabled - .
* @param {string} opts.color - CSS color.
* @returns {undefined}
*/
function Touchable(elem, opts = {}) {
const that = this,
target = {elem: null},
shadow = $.div("tfw-touchable-shadow"),
container = $.div("tfw-touchable-container", [shadow]);
if (typeof opts.enabled === "undefined") opts.enabled = true;
this.enabled = opts.enabled;
this.color = opts.color || "#333";
this.classToAdd = opts.classToAdd;
this.opacity = opts.opacity || 0.4;
this.element = $(elem);
this.tap = new Listeners();
this.press = new Listeners();
let lastX = 0,
lastY = 0;
$.addClass(elem, "tfw-touchable-elem");
const fxDown = Fx()
.css(shadow, {
transition: "none",
transform: "scale(0)"
})
.exec(function() {
const cls = that.classToAdd;
if (typeof cls === "string") {
$.addClass(elem, cls);
}
const rect = target.elem.getBoundingClientRect(),
w = Math.max(lastX, rect.width - lastX),
h = Math.max(lastY, rect.height - lastY),
radius = Math.ceil(Math.sqrt(w * w + h * h));
$.css(shadow, {
left: `${lastX}px`,
top: `${lastY}px`,
margin: `-${radius}px`,
width: `${2 * radius}px`,
height: `${2 * radius}px`,
opacity: 0,
background: that.color,
transform: "scale(0)",
transition: "all .15s ease",
"transition-timing-function": "cubic-bezier(0,1,0.780,1)",
"-moz-transition-timing-function": "cubic-bezier(0,1,0.780,1)",
"-webkit-transition-timing-function":
"cubic-bezier(0,1,0.780,1)"
});
$.clear(target.elem, container);
})
.wait(10)
.css(shadow, {
opacity: that.opacity,
transform: "scale(.25)"
})
.wait(150)
.css(shadow, {transform: "scale(.2)"})
.wait(150)
.css(shadow, {
transition: "all .6s ease",
transform: "scale(1)",
opacity: 0
})
.wait(600)
.exec(function() {
$.detach(target.elem);
const cls = that.classToAdd;
if (typeof cls === "string") {
$.removeClass(elem, cls);
}
});
$.on(elem, {
down(evt) {
if (!that.enabled) return;
evt.stopPropagation();
evt.preventDefault();
lastX = Math.floor(evt.x);
lastY = Math.floor(evt.y);
target.elem = makeFixedElementWithSameShape(elem);
$.add(document.body, target.elem);
fxDown.start();
},
tap(evt) {
if (!that.enabled) {
console.log("DISABLED!");
return;
}
that.tap.fire(evt);
},
doubletap(evt) {
if (!that.enabled) {
console.log("DISABLED!");
return;
}
that.press.fire(evt);
}
});
}
/**
* @param {DOMElement} elem - Element from which to make a new element with the same shape but fixed.
* @returns {undefined}
*/
function makeFixedElementWithSameShape(elem) {
const rect = $(elem).getBoundingClientRect(),
copy = $.div("tfw-touchable");
$.css(copy, {
left: `${rect.left}px`,
top: `${rect.top}px`,
width: `${rect.width}px`,
height: `${rect.height}px`,
borderRadius: window
.getComputedStyle($(elem))
.getPropertyValue("border-radius")
});
return copy;
}