2017-01-29 21:03:03 +01:00
|
|
|
var VanillaTilt = (function () {
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var classCallCheck = function (instance, Constructor) {
|
|
|
|
if (!(instance instanceof Constructor)) {
|
|
|
|
throw new TypeError("Cannot call a class as a function");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2017-01-29 21:19:23 +01:00
|
|
|
* Created by Șandor Sergiu (micku7zu) on 1/27/2017.
|
|
|
|
* Original idea: https://github.com/gijsroge/tilt.js
|
2017-01-29 21:03:03 +01:00
|
|
|
* MIT License.
|
2017-07-10 09:11:23 +02:00
|
|
|
* Version 1.4.1
|
2017-01-29 21:03:03 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
var VanillaTilt = function () {
|
2017-01-30 19:45:12 +01:00
|
|
|
function VanillaTilt(element) {
|
|
|
|
var settings = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
|
|
classCallCheck(this, VanillaTilt);
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
if (!(element instanceof Node)) {
|
|
|
|
throw "Can't initialize VanillaTilt because " + element + " is not a Node.";
|
2017-01-29 21:03:03 +01:00
|
|
|
}
|
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
this.width = null;
|
|
|
|
this.height = null;
|
|
|
|
this.left = null;
|
|
|
|
this.top = null;
|
|
|
|
this.transitionTimeout = null;
|
|
|
|
this.updateCall = null;
|
2017-01-29 21:19:23 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
this.updateBind = this.update.bind(this);
|
2017-07-10 09:11:23 +02:00
|
|
|
this.resetBind = this.reset.bind(this);
|
2017-01-29 21:19:23 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
this.element = element;
|
|
|
|
this.settings = this.extendSettings(settings);
|
2017-01-29 21:19:23 +01:00
|
|
|
|
2017-01-30 20:33:56 +01:00
|
|
|
this.reverse = this.settings.reverse ? -1 : 1;
|
2017-06-13 14:51:38 +02:00
|
|
|
|
2017-06-13 10:39:14 +02:00
|
|
|
this.glare = this.isSettingTrue(this.settings.glare);
|
|
|
|
this.glarePrerender = this.isSettingTrue(this.settings["glare-prerender"]);
|
2017-01-30 20:33:56 +01:00
|
|
|
|
2017-06-13 14:51:38 +02:00
|
|
|
if (this.glare) {
|
|
|
|
this.prepareGlare();
|
|
|
|
}
|
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
this.addEventListeners();
|
|
|
|
}
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-06-13 10:39:14 +02:00
|
|
|
VanillaTilt.prototype.isSettingTrue = function isSettingTrue(setting) {
|
|
|
|
return setting === "" || setting === true || setting === 1;
|
|
|
|
};
|
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
VanillaTilt.prototype.addEventListeners = function addEventListeners() {
|
|
|
|
this.onMouseEnterBind = this.onMouseEnter.bind(this);
|
|
|
|
this.onMouseMoveBind = this.onMouseMove.bind(this);
|
|
|
|
this.onMouseLeaveBind = this.onMouseLeave.bind(this);
|
2017-06-13 10:39:14 +02:00
|
|
|
this.onWindowResizeBind = this.onWindowResizeBind.bind(this);
|
2017-01-30 19:45:12 +01:00
|
|
|
|
|
|
|
this.element.addEventListener("mouseenter", this.onMouseEnterBind);
|
|
|
|
this.element.addEventListener("mousemove", this.onMouseMoveBind);
|
|
|
|
this.element.addEventListener("mouseleave", this.onMouseLeaveBind);
|
2017-06-13 14:51:38 +02:00
|
|
|
if (this.glare) {
|
|
|
|
window.addEventListener("resize", this.onWindowResizeBind);
|
|
|
|
}
|
2017-01-30 19:45:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
VanillaTilt.prototype.removeEventListeners = function removeEventListeners() {
|
|
|
|
this.element.removeEventListener("mouseenter", this.onMouseEnterBind);
|
|
|
|
this.element.removeEventListener("mousemove", this.onMouseMoveBind);
|
|
|
|
this.element.removeEventListener("mouseleave", this.onMouseLeaveBind);
|
2017-06-13 14:51:38 +02:00
|
|
|
if (this.glare) {
|
|
|
|
window.removeEventListener("resize", this.onWindowResizeBind);
|
|
|
|
}
|
2017-01-30 19:45:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
VanillaTilt.prototype.destroy = function destroy() {
|
2017-07-10 09:11:23 +02:00
|
|
|
clearTimeout(this.transitionTimeout);
|
|
|
|
if (this.updateCall !== null) {
|
|
|
|
cancelAnimationFrame(this.updateCall);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.reset();
|
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
this.removeEventListeners();
|
|
|
|
this.element.vanillaTilt = null;
|
|
|
|
delete this.element.vanillaTilt;
|
|
|
|
|
|
|
|
this.element = null;
|
|
|
|
};
|
|
|
|
|
|
|
|
VanillaTilt.prototype.onMouseEnter = function onMouseEnter(event) {
|
|
|
|
this.updateElementPosition();
|
|
|
|
this.element.style.willChange = "transform";
|
|
|
|
this.setTransition();
|
|
|
|
};
|
|
|
|
|
|
|
|
VanillaTilt.prototype.onMouseMove = function onMouseMove(event) {
|
|
|
|
if (this.updateCall !== null) {
|
|
|
|
cancelAnimationFrame(this.updateCall);
|
|
|
|
}
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
this.event = event;
|
|
|
|
this.updateCall = requestAnimationFrame(this.updateBind);
|
|
|
|
};
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
VanillaTilt.prototype.onMouseLeave = function onMouseLeave(event) {
|
|
|
|
this.setTransition();
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
if (this.settings.reset) {
|
2017-07-10 09:11:23 +02:00
|
|
|
requestAnimationFrame(this.resetBind);
|
2017-01-30 19:45:12 +01:00
|
|
|
}
|
|
|
|
};
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
VanillaTilt.prototype.reset = function reset() {
|
2017-07-10 09:11:23 +02:00
|
|
|
this.event = {
|
|
|
|
pageX: this.left + this.width / 2,
|
|
|
|
pageY: this.top + this.height / 2
|
|
|
|
};
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-07-10 09:11:23 +02:00
|
|
|
this.element.style.transform = "perspective(" + this.settings.perspective + "px) " + "rotateX(0deg) " + "rotateY(0deg) " + "scale3d(1, 1, 1)";
|
2017-06-13 14:51:38 +02:00
|
|
|
|
2017-06-13 10:39:14 +02:00
|
|
|
if (this.glare) {
|
2017-06-13 14:51:38 +02:00
|
|
|
this.glareElement.style.transform = 'rotate(180deg) translate(-50%, -50%)';
|
|
|
|
this.glareElement.style.opacity = '0';
|
2017-06-13 10:39:14 +02:00
|
|
|
}
|
2017-01-30 19:45:12 +01:00
|
|
|
};
|
2017-01-29 21:19:23 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
VanillaTilt.prototype.getValues = function getValues() {
|
|
|
|
var x = (this.event.clientX - this.left) / this.width;
|
|
|
|
var y = (this.event.clientY - this.top) / this.height;
|
2017-01-29 21:19:23 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
x = Math.min(Math.max(x, 0), 1);
|
|
|
|
y = Math.min(Math.max(y, 0), 1);
|
2017-01-29 21:19:23 +01:00
|
|
|
|
2017-01-30 20:33:56 +01:00
|
|
|
var tiltX = (this.reverse * (this.settings.max / 2 - x * this.settings.max)).toFixed(2);
|
|
|
|
var tiltY = (this.reverse * (y * this.settings.max - this.settings.max / 2)).toFixed(2);
|
2017-06-13 14:51:38 +02:00
|
|
|
var angle = Math.atan2(this.event.clientX - (this.left + this.width / 2), -(this.event.clientY - (this.top + this.height / 2))) * (180 / Math.PI);
|
2017-01-29 21:19:23 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
return {
|
|
|
|
tiltX: tiltX,
|
|
|
|
tiltY: tiltY,
|
|
|
|
percentageX: x * 100,
|
2017-06-13 14:51:38 +02:00
|
|
|
percentageY: y * 100,
|
|
|
|
angle: angle
|
2017-01-29 21:03:03 +01:00
|
|
|
};
|
2017-01-30 19:45:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
VanillaTilt.prototype.updateElementPosition = function updateElementPosition() {
|
|
|
|
var rect = this.element.getBoundingClientRect();
|
|
|
|
|
|
|
|
this.width = this.element.offsetWidth;
|
|
|
|
this.height = this.element.offsetHeight;
|
|
|
|
this.left = rect.left;
|
|
|
|
this.top = rect.top;
|
|
|
|
};
|
|
|
|
|
|
|
|
VanillaTilt.prototype.update = function update() {
|
|
|
|
var values = this.getValues();
|
|
|
|
|
2017-06-23 23:05:26 +02:00
|
|
|
this.element.style.transform = "perspective(" + this.settings.perspective + "px) " + "rotateX(" + (this.settings.axis === "x" ? 0 : values.tiltY) + "deg) " + "rotateY(" + (this.settings.axis === "y" ? 0 : values.tiltX) + "deg) " + "scale3d(" + this.settings.scale + ", " + this.settings.scale + ", " + this.settings.scale + ")";
|
2017-01-30 19:45:12 +01:00
|
|
|
|
2017-06-13 10:39:14 +02:00
|
|
|
if (this.glare) {
|
|
|
|
this.glareElement.style.transform = "rotate(" + values.angle + "deg) translate(-50%, -50%)";
|
|
|
|
this.glareElement.style.opacity = "" + values.percentageY * this.settings["max-glare"] / 100;
|
|
|
|
}
|
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
this.element.dispatchEvent(new CustomEvent("tiltChange", {
|
|
|
|
"detail": values
|
|
|
|
}));
|
|
|
|
|
|
|
|
this.updateCall = null;
|
|
|
|
};
|
|
|
|
|
2017-06-13 10:39:14 +02:00
|
|
|
/**
|
|
|
|
* Appends the glare element (if glarePrerender equals false)
|
|
|
|
* and sets the default style
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
VanillaTilt.prototype.prepareGlare = function prepareGlare() {
|
|
|
|
// If option pre-render is enabled we assume all html/css is present for an optimal glare effect.
|
|
|
|
if (!this.glarePrerender) {
|
|
|
|
// Create glare element
|
|
|
|
var jsTiltGlare = document.createElement("div");
|
|
|
|
jsTiltGlare.classList.add("js-tilt-glare");
|
|
|
|
|
|
|
|
var jsTiltGlareInner = document.createElement("div");
|
|
|
|
jsTiltGlareInner.classList.add("js-tilt-glare-inner");
|
|
|
|
|
|
|
|
jsTiltGlare.appendChild(jsTiltGlareInner);
|
|
|
|
this.element.appendChild(jsTiltGlare);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.glareElementWrapper = this.element.querySelector(".js-tilt-glare");
|
|
|
|
this.glareElement = this.element.querySelector(".js-tilt-glare-inner");
|
|
|
|
|
2017-06-13 14:51:38 +02:00
|
|
|
if (this.glarePrerender) {
|
|
|
|
return;
|
|
|
|
}
|
2017-06-13 10:39:14 +02:00
|
|
|
|
2017-06-13 14:51:38 +02:00
|
|
|
Object.assign(this.glareElementWrapper.style, {
|
2017-06-13 10:39:14 +02:00
|
|
|
"position": "absolute",
|
|
|
|
"top": "0",
|
|
|
|
"left": "0",
|
|
|
|
"width": "100%",
|
|
|
|
"height": "100%",
|
|
|
|
"overflow": "hidden"
|
2017-06-13 14:51:38 +02:00
|
|
|
});
|
2017-06-13 10:39:14 +02:00
|
|
|
|
2017-06-13 14:51:38 +02:00
|
|
|
Object.assign(this.glareElement.style, {
|
2017-06-13 10:39:14 +02:00
|
|
|
'position': 'absolute',
|
|
|
|
'top': '50%',
|
|
|
|
'left': '50%',
|
|
|
|
'pointer-events': 'none',
|
|
|
|
'background-image': "linear-gradient(0deg, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%)",
|
|
|
|
'width': this.element.offsetWidth * 2 + "px",
|
|
|
|
'height': this.element.offsetWidth * 2 + "px",
|
|
|
|
'transform': 'rotate(180deg) translate(-50%, -50%)',
|
|
|
|
'transform-origin': '0% 0%',
|
|
|
|
'opacity': '0'
|
2017-06-13 14:51:38 +02:00
|
|
|
});
|
2017-06-13 10:39:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
VanillaTilt.prototype.updateGlareSize = function updateGlareSize() {
|
|
|
|
Object.assign(this.glareElement.style, {
|
|
|
|
'width': "" + this.element.offsetWidth * 2,
|
|
|
|
'height': "" + this.element.offsetWidth * 2
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
VanillaTilt.prototype.onWindowResizeBind = function onWindowResizeBind() {
|
|
|
|
this.updateGlareSize();
|
|
|
|
};
|
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
VanillaTilt.prototype.setTransition = function setTransition() {
|
2017-07-10 09:11:23 +02:00
|
|
|
var _this = this;
|
2017-01-30 19:45:12 +01:00
|
|
|
|
|
|
|
clearTimeout(this.transitionTimeout);
|
|
|
|
this.element.style.transition = this.settings.speed + "ms " + this.settings.easing;
|
2017-06-13 10:39:14 +02:00
|
|
|
if (this.glare) this.glareElement.style.transition = "opacity " + this.settings.speed + "ms " + this.settings.easing;
|
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
this.transitionTimeout = setTimeout(function () {
|
2017-07-10 09:11:23 +02:00
|
|
|
_this.element.style.transition = "";
|
|
|
|
if (_this.glare) {
|
|
|
|
_this.glareElement.style.transition = "";
|
2017-06-13 14:51:38 +02:00
|
|
|
}
|
2017-01-30 19:45:12 +01:00
|
|
|
}, this.settings.speed);
|
|
|
|
};
|
|
|
|
|
|
|
|
VanillaTilt.prototype.extendSettings = function extendSettings(settings) {
|
|
|
|
var defaultSettings = {
|
2017-01-30 20:33:56 +01:00
|
|
|
reverse: false,
|
2017-01-30 19:45:12 +01:00
|
|
|
max: 35,
|
|
|
|
perspective: 1000,
|
|
|
|
easing: "cubic-bezier(.03,.98,.52,.99)",
|
|
|
|
scale: "1",
|
|
|
|
speed: "300",
|
|
|
|
transition: true,
|
|
|
|
axis: null,
|
2017-06-13 10:39:14 +02:00
|
|
|
glare: false,
|
|
|
|
"max-glare": 1,
|
|
|
|
"glare-prerender": false,
|
2017-01-30 19:45:12 +01:00
|
|
|
reset: true
|
2017-01-29 21:19:23 +01:00
|
|
|
};
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
var newSettings = {};
|
|
|
|
for (var property in defaultSettings) {
|
|
|
|
if (property in settings) {
|
|
|
|
newSettings[property] = settings[property];
|
|
|
|
} else if (this.element.hasAttribute("data-tilt-" + property)) {
|
|
|
|
var attribute = this.element.getAttribute("data-tilt-" + property);
|
|
|
|
try {
|
|
|
|
newSettings[property] = JSON.parse(attribute);
|
|
|
|
} catch (e) {
|
|
|
|
newSettings[property] = attribute;
|
2017-01-29 21:19:23 +01:00
|
|
|
}
|
2017-01-30 19:45:12 +01:00
|
|
|
} else {
|
|
|
|
newSettings[property] = defaultSettings[property];
|
|
|
|
}
|
|
|
|
}
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
return newSettings;
|
|
|
|
};
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
VanillaTilt.init = function init(elements, settings) {
|
|
|
|
if (elements instanceof Node) {
|
|
|
|
elements = [elements];
|
|
|
|
}
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
if (elements instanceof NodeList) {
|
|
|
|
elements = [].slice.call(elements);
|
|
|
|
}
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
if (!(elements instanceof Array)) {
|
|
|
|
return;
|
|
|
|
}
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
elements.forEach(function (element) {
|
|
|
|
if (!("vanillaTilt" in element)) {
|
|
|
|
element.vanillaTilt = new VanillaTilt(element, settings);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
2017-01-29 21:03:03 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
return VanillaTilt;
|
2017-01-29 21:03:03 +01:00
|
|
|
}();
|
|
|
|
|
2017-01-29 21:19:23 +01:00
|
|
|
if (typeof document !== "undefined") {
|
2017-01-30 19:45:12 +01:00
|
|
|
/* expose the class to window */
|
|
|
|
window.VanillaTilt = VanillaTilt;
|
2017-01-29 21:19:23 +01:00
|
|
|
|
2017-01-30 19:45:12 +01:00
|
|
|
/**
|
|
|
|
* Auto load
|
|
|
|
*/
|
|
|
|
VanillaTilt.init(document.querySelectorAll("[data-tilt]"));
|
2017-01-29 21:03:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return VanillaTilt;
|
|
|
|
|
|
|
|
}());
|