Created
August 21, 2019 18:52
-
-
Save uxder/5db18b0ee020920d133b6c73d8f7762f to your computer and use it in GitHub Desktop.
Mouse parallaxer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { MouseTracker } from './mousetracker'; | |
/** | |
* A class that creates a parallax effect based on the mouse position. | |
* @hidden | |
*/ | |
export class Parallaxer { | |
private animate_: boolean; | |
private mousePosition_: any; | |
private rotationSensitivity: any; | |
private translateSensitivity: any; | |
private transformValues: any; | |
private pivotElement_: HTMLElement | undefined; | |
private rootElement_: HTMLElement; | |
private mouseTracker_: MouseTracker; | |
/** | |
* @param {HTMLElement} rootElement The root element to apply parallax effects to. | |
* @param {Object} rotationSentivity x,y object with rotation sensitivity. | |
* @param {Object} translateSentitivy x,y object with translate senstiivity. | |
* @param {HTMLElement} pivotElement Optional element to make the pixot center. | |
* @constructor | |
* TODO(uxder) Clean this up. | |
*/ | |
constructor(rootElement: HTMLElement, rotationSensitivity: any, | |
translateSensitity: any, pivotElement?: HTMLElement) { | |
/** | |
* Flag to allow animation. | |
* @type {boolean} | |
*/ | |
this.animate_ = false; | |
/** | |
* The current mouse position data acquired from the mouse tracker. | |
* @type {Object} | |
*/ | |
this.mousePosition_ = null; | |
/** | |
* The rotation sensitivity of the parallax effect. | |
*/ | |
this.rotationSensitivity = rotationSensitivity || { | |
x: 0.2, | |
y: 0.2 | |
}; | |
/** | |
* The translate sensitivity of the parallax effect. | |
*/ | |
this.translateSensitivity = translateSensitity || { | |
x: 1, | |
y: 1 | |
}; | |
/** | |
* The current transform values. | |
*/ | |
this.transformValues = { | |
xDeg: 0, | |
yDeg: 0, | |
zDeg: 0, | |
xTrans: 0, | |
yTrans: 0 | |
}; | |
/** | |
* The pivot element if defined. | |
* @type {Element} | |
*/ | |
this.pivotElement_ = pivotElement; | |
/** | |
* The root element to manipulate. | |
* @type {Element} | |
*/ | |
this.rootElement_ = rootElement; | |
this.mouseTracker_ = new MouseTracker( | |
this.pivotElement_ || document.body, | |
(mousePosition: any) => { | |
this.mousePosition_ = mousePosition; | |
}, | |
false | |
); | |
} | |
/** | |
* Runs the animation. | |
*/ | |
run() { | |
this.animate_ = true; | |
this.rafLoop_(); | |
} | |
/** | |
* Stops the animation. | |
*/ | |
stop() { | |
this.animate_ = false; | |
} | |
/** | |
* Internal animation cycle. | |
*/ | |
rafLoop_() { | |
if (!this.animate_) { | |
return; | |
} | |
window.requestAnimationFrame(() => { | |
this.rafLoop_(); | |
}); | |
this.render_(); | |
} | |
/** | |
* Internal render cycle. | |
*/ | |
render_() { | |
if (!this.mousePosition_) { | |
return; | |
} | |
const xDegree = (this.mousePosition_.percentageX) * this.rotationSensitivity.x; | |
const yDegree = (this.mousePosition_.percentageY) * this.rotationSensitivity.y; | |
const xTrans = -(this.mousePosition_.percentageX) * this.translateSensitivity.x; | |
const yTrans = -(this.mousePosition_.percentageY) * this.translateSensitivity.y; | |
// TODO(uxder): Add complex easing. | |
this.transformValues.xDeg += (xDegree - this.transformValues.xDeg) * 0.03; | |
this.transformValues.yDeg += (yDegree - this.transformValues.yDeg) * 0.03; | |
this.transformValues.xTrans += (xTrans - this.transformValues.xTrans) * 0.03; | |
this.transformValues.yTrans += (yTrans - this.transformValues.yTrans) * 0.03; | |
const transformString = | |
'rotateX(' + this.transformValues.yDeg + 'deg) rotateY(' + | |
-this.transformValues.xDeg + 'deg) rotateZ(0deg)' + | |
' translate(' + -this.transformValues.xTrans + 'px, ' + | |
this.transformValues.yTrans + 'px)'; | |
// console.log(transformString, this.mousePosition_); | |
this.rootElement_.style.perspectiveOrigin = '50% 50%'; | |
this.rootElement_.style.transform = transformString; | |
this.rootElement_.style.transformOrigin = '50% 50%'; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare function require(x: string): any; | |
import {math} from '../math'; | |
import {detector} from '../detector'; | |
/** | |
* Class that helps with mouse tracking. | |
*/ | |
export class MouseTracker { | |
/** | |
* The root element to calculate the center position of the effect. | |
*/ | |
private rootElement_:Element; | |
/** | |
* The basic dimensions of the root element. | |
* @type {Object} | |
*/ | |
private dimensions_:any; | |
/** | |
* The callback for when there is mouse movement. | |
*/ | |
private moveCallBack_:Function; | |
/** | |
* The current mouse position data. | |
*/ | |
private mousePosition_:Object; | |
/** | |
* @constructor | |
*/ | |
constructor(rootElement:Element, moveCallBack:Function, disableMobile:boolean) { | |
this.rootElement_ = rootElement; | |
this.dimensions_ = null; | |
this.moveCallBack_ = moveCallBack; | |
this.mousePosition_ = {}; | |
this.calculateRootElementDimensions_(); | |
window.addEventListener('resize', () => { | |
this.calculateRootElementDimensions_(); | |
}); | |
if(!disableMobile && detector.allowDeviceOrientation()) { | |
window.addEventListener('deviceorientation', (e) => { | |
this.onDeviceOrientation_(e); | |
}); | |
} | |
document.body.addEventListener('mousemove', (e) => { | |
this.onMouseMove_(e); | |
}, false); | |
} | |
/** | |
* Calculates the base dimensions of the rootElement. | |
*/ | |
calculateRootElementDimensions_() { | |
let docRect = document.body.getBoundingClientRect(); | |
let rect = this.rootElement_.getBoundingClientRect(); | |
// Calculate the center point. | |
let xCenter = rect.left + rect.width / 2; | |
let yCenter = rect.top + rect.height / 2; | |
this.dimensions_ = { | |
width: rect.width, | |
height: rect.height, | |
halfWidth: rect.width / 2, | |
halfHeight: rect.height / 2, | |
top: rect.top, | |
left: rect.left, | |
xCenter: xCenter, | |
yCenter: yCenter, | |
docWidth: docRect.width, | |
docHeight: docRect.height | |
} | |
} | |
/** | |
* Handles the mouseRootElement move. | |
* @type {MouseEvent} | |
*/ | |
onMouseMove_(e:any) { | |
let x = e.pageX || e.clientX; | |
let y = e.pageY || e.clientY; | |
this.mousePosition_ = { | |
x: x, | |
y: y, | |
deltaX: x - this.dimensions_.xCenter, | |
deltaY: y - this.dimensions_.yCenter, | |
percentageX: (x - this.dimensions_.xCenter) / | |
(this.dimensions_.docWidth) * 100, | |
percentageY: (y - this.dimensions_.yCenter) / | |
(this.dimensions_.docHeight) * 100, | |
} | |
this.moveCallBack_(this.mousePosition_); | |
} | |
/** | |
* Handles the device orientation. | |
* @type {MouseEvent} | |
*/ | |
onDeviceOrientation_(event:any) { | |
let x = math.limitRange(-50, 50, event.gamma); | |
let y = math.limitRange(-50, 50, event.beta); | |
this.mousePosition_ = { | |
x: 0, | |
y: 0, | |
deltaX: 0, | |
deltaY: 0, | |
percentageX: x, | |
percentageY: y | |
} | |
this.moveCallBack_(this.mousePosition_); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment