From 81f33cbe5f2bd9064239ddbaa8fc0c9dff7c2243 Mon Sep 17 00:00:00 2001 From: Matthew Wagerfield Date: Fri, 25 Apr 2014 19:36:06 +0100 Subject: [PATCH] :gb: Refactored motion calculations to use px and added interactive.html. --- README.md | 80 +++++++++++++++++------------------ bower.json | 2 +- deploy/jquery.parallax.js | 80 ++++++++++++++++++++++------------- deploy/jquery.parallax.min.js | 2 +- deploy/parallax.js | 69 ++++++++++++++++++------------ deploy/parallax.min.js | 2 +- examples/interactive.html | 42 ++++++++++++++++++ examples/styles/styles.css | 62 +++++++++++++++++++++++++++ package.json | 2 +- source/jquery.parallax.js | 80 ++++++++++++++++++++++------------- source/parallax.js | 69 ++++++++++++++++++------------ 11 files changed, 332 insertions(+), 158 deletions(-) create mode 100644 examples/interactive.html diff --git a/README.md b/README.md index 69f32fb..bf20c44 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,12 @@ # Parallax.js -Simple, lightweight **Parallax Engine** that reacts to the orientation of a -smart device. Where no gyroscope or motion detection hardware is available, the -position of the cursor is used instead. +Parallax Engine that reacts to the orientation of a smart device. Where no gyroscope or motion detection hardware is available, the position of the cursor is used instead. Check out this **[demo][demo]** to see it in action! ## Setup -Simply create a list of elements giving each item that you want to move within -your parallax scene a class of `layer` and a `data-depth` attribute specifying -its depth within the scene. A depth of **0** will cause the layer to remain -stationary, and a depth of **1** will cause the layer to move by the total -effect of the calculated motion. Values inbetween **0** and **1** will cause the -layer to move by an amount relative to the supplied ratio. +Create a list of elements giving each item that you want to move within your parallax scene a class of `layer` and a `data-depth` attribute specifying its depth within the scene. A depth of **0** will cause the layer to remain stationary, and a depth of **1** will cause the layer to move by the total effect of the calculated motion. Values inbetween **0** and **1** will cause the layer to move by an amount relative to the supplied ratio. ```html ``` -To kickoff a **Parallax** scene, simply select your parent DOM Element and pass -it to the **Parallax** constructor. +To kickoff a **Parallax** scene, select your parent DOM Element and pass it to the **Parallax** constructor. ```javascript var scene = document.getElementById('scene'); var parallax = new Parallax(scene); ``` +## Understanding Layer Motion Calculations + +The amount of motion that each layer moves by depends on 3 contributing factors: + +1. The `scalarX` and `scalarY` values (see [Behaviours](#behaviours) below for configuration) +2. The dimensions of the parent DOM element +3. The `depth` of a layer within a parallax scene (specified by it's `data-depth` attribute) + +The calculation for this motion is as follows: + +```coffeescript +xMotion = parentElement.width / scalarX * layerDepth +yMotion = parentElement.height / scalarY * layerDepth +``` + +So for a layer with a `data-depth` value of `0.5` within a scene that has both the `scalarX` and `scalarY` values set to `10` ( *the default* ) where the containing scene element is `1000px x 1000px`, the total motion of the layer in both `x` and `y` would be: + +```coffeescript +xMotion = 1000 / 10 * 0.5 = 50 # 50px of positive and negative motion in x +yMotion = 1000 / 10 * 0.5 = 50 # 50px of positive and negative motion in y +``` + ## Behaviours -There are a number of behaviours that you can setup for any given **Parallax** -instance. These behaviours can either be specified in the markup via data -attributes or in JavaScript via the constructor and API. +There are a number of behaviours that you can setup for any given **Parallax** instance. These behaviours can either be specified in the markup via data attributes or in JavaScript via the constructor and API. | Behaviour | Values | Description | | ------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | @@ -55,8 +67,7 @@ attributes or in JavaScript via the constructor and API. | `friction-x` | `number` `0 - 1` | The amount of friction the layers experience. This essentially adds some easing to the layer motion. | | `friction-y` | `number` `0 - 1` | The amount of friction the layers experience. This essentially adds some easing to the layer motion. | -In addition to the behaviours described above, there are **two** methods `enable()` -and `disable()` that *activate* and *deactivate* the **Parallax** instance respectively. +In addition to the behaviours described above, there are **two** methods `enable()` and `disable()` that *activate* and *deactivate* the **Parallax** instance respectively. ### Behaviours: Data Attributes Example @@ -153,35 +164,24 @@ $scene.parallax('friction', 0.2, 0.8); ## iOS -If you are writing a **native iOS application** and would like to use **parallax.js** -within a `UIWebView`, you will need to do a little bit of work to get it running. +If you are writing a **native iOS application** and would like to use **parallax.js** within a `UIWebView`, you will need to do a little bit of work to get it running. -`UIWebView` no longer automatically receives the `deviceorientation` event, so -your native application must intercept the events from the gyroscope and reroute -them to the `UIWebView`: +`UIWebView` no longer automatically receives the `deviceorientation` event, so your native application must intercept the events from the gyroscope and reroute them to the `UIWebView`: -1. Include the **CoreMotion** framework `#import ` -and create a reference to the **UIWebView** `@property(nonatomic, strong) IBOutlet UIWebView *parallaxWebView;` -2. Add a property to the app delegate (or controller that will own the **UIWebView**) -`@property(nonatomic, strong) CMMotionManager *motionManager;` +1. Include the **CoreMotion** framework `#import ` and create a reference to the **UIWebView** `@property(nonatomic, strong) IBOutlet UIWebView *parallaxWebView;` +2. Add a property to the app delegate (or controller that will own the **UIWebView**) `@property(nonatomic, strong) CMMotionManager *motionManager;` 3. Finally, make the following calls: ```Objective-C - self.motionManager = [[CMMotionManager alloc] init]; - - if (self.motionManager.isGyroAvailable && !self.motionManager.isGyroActive) { - - [self.motionManager setGyroUpdateInterval:0.5f]; // Set the event update frequency (in seconds) - - [self.motionManager startGyroUpdatesToQueue:NSOperationQueue.mainQueue - withHandler:^(CMGyroData *gyroData, NSError *error) { - - NSString *js = [NSString stringWithFormat:@"parallax.onDeviceOrientation({beta:%f, gamma:%f})", gyroData.rotationRate.x, gyroData.rotationRate.y]; - - [self.parallaxWebView stringByEvaluatingJavaScriptFromString:js]; - - }]; - } +self.motionManager = [[CMMotionManager alloc] init]; +if (self.motionManager.isGyroAvailable && !self.motionManager.isGyroActive) { + [self.motionManager setGyroUpdateInterval:0.5f]; // Set the event update frequency (in seconds) + [self.motionManager startGyroUpdatesToQueue:NSOperationQueue.mainQueue + withHandler:^(CMGyroData *gyroData, NSError *error) { + NSString *js = [NSString stringWithFormat:@"parallax.onDeviceOrientation({beta:%f, gamma:%f})", gyroData.rotationRate.x, gyroData.rotationRate.y]; + [self.parallaxWebView stringByEvaluatingJavaScriptFromString:js]; + }]; +} ``` ## Build diff --git a/bower.json b/bower.json index 7bda3e0..91e835d 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "parallax", "description": "Parallax Engine that reacts to the orientation of a smart device.", - "version": "1.1.1", + "version": "2.0.0", "license": "MIT", "homepage": "http://wagerfield.github.io/parallax/", "authors": [ diff --git a/deploy/jquery.parallax.js b/deploy/jquery.parallax.js index 88ea4e4..a67f5b6 100644 --- a/deploy/jquery.parallax.js +++ b/deploy/jquery.parallax.js @@ -59,7 +59,9 @@ scalarX: 10.0, scalarY: 10.0, frictionX: 0.1, - frictionY: 0.1 + frictionY: 0.1, + originX: 0.5, + originY: 0.5 }; function Plugin(element, options) { @@ -163,11 +165,21 @@ break; case '3D': if (propertySupport) { - document.body.appendChild(element); + var body = document.body || document.createElement('body'); + var documentElement = document.documentElement; + var documentOverflow = documentElement.style.overflow; + if (!document.body) { + documentElement.style.overflow = 'hidden'; + documentElement.appendChild(body); + body.style.overflow = 'hidden'; + body.style.background = ''; + } + body.appendChild(element); element.style[jsProperty] = 'translate3d(1px,1px,1px)'; propertyValue = window.getComputedStyle(element).getPropertyValue(cssProperty); featureSupport = propertyValue !== undefined && propertyValue.length > 0 && propertyValue !== "none"; - document.body.removeChild(element); + documentElement.style.overflow = documentOverflow; + body.removeChild(element); } break; } @@ -186,6 +198,7 @@ Plugin.prototype.orientationStatus = 0; Plugin.prototype.transform2DSupport = Plugin.prototype.transformSupport('2D'); Plugin.prototype.transform3DSupport = Plugin.prototype.transformSupport('3D'); + Plugin.prototype.propertyCache = {}; Plugin.prototype.initialise = function() { @@ -198,8 +211,6 @@ this.$layers.css({ position:'absolute', display:'block', - height:'100%', - width:'100%', left: 0, top: 0 }); @@ -225,8 +236,18 @@ Plugin.prototype.updateDimensions = function() { this.ww = window.innerWidth; this.wh = window.innerHeight; - this.wcx = this.ww / 2; - this.wcy = this.wh / 2; + this.wcx = this.ww * this.originX; + this.wcy = this.wh * this.originY; + }; + + Plugin.prototype.updateBounds = function() { + this.bounds = this.element.getBoundingClientRect(); + this.ex = this.bounds.left; + this.ey = this.bounds.top; + this.ew = this.bounds.width; + this.eh = this.bounds.height; + this.ecx = this.ew * this.originX; + this.ecy = this.eh * this.originY; }; Plugin.prototype.queueCalibration = function(delay) { @@ -297,18 +318,21 @@ }; Plugin.prototype.css = function(element, property, value) { - var jsProperty = null; - for (var i = 0, l = this.vendors.length; i < l; i++) { - if (this.vendors[i] !== null) { - jsProperty = $.camelCase(this.vendors[i][1] + '-' + property); - } else { - jsProperty = property; - } - if (element.style[jsProperty] !== undefined) { - element.style[jsProperty] = value; - break; + var jsProperty = this.propertyCache[property]; + if (!jsProperty) { + for (var i = 0, l = this.vendors.length; i < l; i++) { + if (this.vendors[i] !== null) { + jsProperty = $.camelCase(this.vendors[i][1] + '-' + property); + } else { + jsProperty = property; + } + if (element.style[jsProperty] !== undefined) { + this.propertyCache[property] = jsProperty; + break; + } } } + element.style[jsProperty] = value; }; Plugin.prototype.accelerate = function($element) { @@ -321,8 +345,8 @@ }; Plugin.prototype.setPosition = function(element, x, y) { - x += '%'; - y += '%'; + x += 'px'; + y += 'px'; if (this.transform3DSupport) { this.css(element, 'transform', 'translate3d('+x+','+y+',0)'); } else if (this.transform2DSupport) { @@ -350,18 +374,21 @@ }; Plugin.prototype.onAnimationFrame = function() { + this.updateBounds(); var dx = this.ix - this.cx; var dy = this.iy - this.cy; if ((Math.abs(dx) > this.calibrationThreshold) || (Math.abs(dy) > this.calibrationThreshold)) { this.queueCalibration(0); } if (this.portrait) { - this.mx = (this.calibrateX ? dy : this.iy) * this.scalarX; - this.my = (this.calibrateY ? dx : this.ix) * this.scalarY; + this.mx = this.calibrateX ? dy : this.iy; + this.my = this.calibrateY ? dx : this.ix; } else { - this.mx = (this.calibrateX ? dx : this.ix) * this.scalarX; - this.my = (this.calibrateY ? dy : this.iy) * this.scalarY; + this.mx = this.calibrateX ? dx : this.ix; + this.my = this.calibrateY ? dy : this.iy; } + this.mx *= this.ew / this.scalarX; + this.my *= this.eh / this.scalarY; if (!isNaN(parseFloat(this.limitX))) { this.mx = this.clamp(this.mx, -this.limitX, this.limitX); } @@ -418,13 +445,6 @@ if (!this.orientationSupport && this.relativeInput) { // Calculate input relative to the element. - this.bounds = this.element.getBoundingClientRect(); - this.ex = this.bounds.left; - this.ey = this.bounds.top; - this.ew = this.bounds.width; - this.eh = this.bounds.height; - this.ecx = this.ew / 2; - this.ecy = this.eh / 2; this.ix = (event.clientX - this.ex - this.ecx) / this.ecx; this.iy = (event.clientY - this.ey - this.ecy) / this.ecy; diff --git a/deploy/jquery.parallax.min.js b/deploy/jquery.parallax.min.js index f5b75a7..bc66456 100644 --- a/deploy/jquery.parallax.min.js +++ b/deploy/jquery.parallax.min.js @@ -1 +1 @@ -!function(t,i,e,s){"use strict";function n(i,e){this.element=i,this.$context=t(i).data("api",this),this.$layers=this.$context.find(".layer");var s={calibrateX:this.$context.data("calibrate-x")||null,calibrateY:this.$context.data("calibrate-y")||null,invertX:this.$context.data("invert-x")||null,invertY:this.$context.data("invert-y")||null,limitX:parseFloat(this.$context.data("limit-x"))||null,limitY:parseFloat(this.$context.data("limit-y"))||null,scalarX:parseFloat(this.$context.data("scalar-x"))||null,scalarY:parseFloat(this.$context.data("scalar-y"))||null,frictionX:parseFloat(this.$context.data("friction-x"))||null,frictionY:parseFloat(this.$context.data("friction-y"))||null};for(var n in s)null===s[n]&&delete s[n];t.extend(this,r,e,s),this.calibrationTimer=null,this.calibrationFlag=!0,this.enabled=!1,this.depths=[],this.raf=null,this.bounds=null,this.ex=0,this.ey=0,this.ew=0,this.eh=0,this.ecx=0,this.ecy=0,this.cx=0,this.cy=0,this.ix=0,this.iy=0,this.mx=0,this.my=0,this.vx=0,this.vy=0,this.onMouseMove=this.onMouseMove.bind(this),this.onDeviceOrientation=this.onDeviceOrientation.bind(this),this.onOrientationTimer=this.onOrientationTimer.bind(this),this.onCalibrationTimer=this.onCalibrationTimer.bind(this),this.onAnimationFrame=this.onAnimationFrame.bind(this),this.onWindowResize=this.onWindowResize.bind(this),this.initialise()}var o="parallax",a=30,r={relativeInput:!1,clipRelativeInput:!1,calibrationThreshold:100,calibrationDelay:500,supportDelay:500,calibrateX:!1,calibrateY:!0,invertX:!0,invertY:!0,limitX:!1,limitY:!1,scalarX:10,scalarY:10,frictionX:.1,frictionY:.1};n.prototype.transformSupport=function(t){for(var n=e.createElement("div"),o=!1,a=null,r=!1,h=null,l=null,c=0,p=this.vendors.length;p>c;c++)if(null!==this.vendors[c]?(h=this.vendors[c][0]+"transform",l=this.vendors[c][1]+"Transform"):(h="transform",l="transform"),n.style[l]!==s){o=!0;break}switch(t){case"2D":r=o;break;case"3D":o&&(e.body.appendChild(n),n.style[l]="translate3d(1px,1px,1px)",a=i.getComputedStyle(n).getPropertyValue(h),r=a!==s&&a.length>0&&"none"!==a,e.body.removeChild(n))}return r},n.prototype.ww=null,n.prototype.wh=null,n.prototype.wcx=null,n.prototype.wcy=null,n.prototype.portrait=null,n.prototype.desktop=!navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|BB10|mobi|tablet|opera mini|nexus 7)/i),n.prototype.vendors=[null,["-webkit-","webkit"],["-moz-","Moz"],["-o-","O"],["-ms-","ms"]],n.prototype.motionSupport=!!i.DeviceMotionEvent,n.prototype.orientationSupport=!!i.DeviceOrientationEvent,n.prototype.orientationStatus=0,n.prototype.transform2DSupport=n.prototype.transformSupport("2D"),n.prototype.transform3DSupport=n.prototype.transformSupport("3D"),n.prototype.initialise=function(){"static"===this.$context.css("position")&&this.$context.css({position:"relative"}),this.$layers.css({position:"absolute",display:"block",height:"100%",width:"100%",left:0,top:0}),this.$layers.first().css({position:"relative"}),this.$layers.each(t.proxy(function(i,e){this.depths.push(t(e).data("depth")||0)},this)),this.accelerate(this.$context),this.accelerate(this.$layers),this.updateDimensions(),this.enable(),this.queueCalibration(this.calibrationDelay)},n.prototype.updateDimensions=function(){this.ww=i.innerWidth,this.wh=i.innerHeight,this.wcx=this.ww/2,this.wcy=this.wh/2},n.prototype.queueCalibration=function(t){clearTimeout(this.calibrationTimer),this.calibrationTimer=setTimeout(this.onCalibrationTimer,t)},n.prototype.enable=function(){this.enabled||(this.enabled=!0,this.orientationSupport?(this.portrait=null,i.addEventListener("deviceorientation",this.onDeviceOrientation),setTimeout(this.onOrientationTimer,this.supportDelay)):(this.cx=0,this.cy=0,this.portrait=!1,i.addEventListener("mousemove",this.onMouseMove)),i.addEventListener("resize",this.onWindowResize),this.raf=requestAnimationFrame(this.onAnimationFrame))},n.prototype.disable=function(){this.enabled&&(this.enabled=!1,this.orientationSupport?i.removeEventListener("deviceorientation",this.onDeviceOrientation):i.removeEventListener("mousemove",this.onMouseMove),i.removeEventListener("resize",this.onWindowResize),cancelAnimationFrame(this.raf))},n.prototype.calibrate=function(t,i){this.calibrateX=t===s?this.calibrateX:t,this.calibrateY=i===s?this.calibrateY:i},n.prototype.invert=function(t,i){this.invertX=t===s?this.invertX:t,this.invertY=i===s?this.invertY:i},n.prototype.friction=function(t,i){this.frictionX=t===s?this.frictionX:t,this.frictionY=i===s?this.frictionY:i},n.prototype.scalar=function(t,i){this.scalarX=t===s?this.scalarX:t,this.scalarY=i===s?this.scalarY:i},n.prototype.limit=function(t,i){this.limitX=t===s?this.limitX:t,this.limitY=i===s?this.limitY:i},n.prototype.clamp=function(t,i,e){return t=Math.max(t,i),t=Math.min(t,e)},n.prototype.css=function(i,e,n){for(var o=null,a=0,r=this.vendors.length;r>a;a++)if(o=null!==this.vendors[a]?t.camelCase(this.vendors[a][1]+"-"+e):e,i.style[o]!==s){i.style[o]=n;break}},n.prototype.accelerate=function(t){for(var i=0,e=t.length;e>i;i++){var s=t[i];this.css(s,"transform","translate3d(0,0,0)"),this.css(s,"transform-style","preserve-3d"),this.css(s,"backface-visibility","hidden")}},n.prototype.setPosition=function(t,i,e){i+="%",e+="%",this.transform3DSupport?this.css(t,"transform","translate3d("+i+","+e+",0)"):this.transform2DSupport?this.css(t,"transform","translate("+i+","+e+")"):(t.style.left=i,t.style.top=e)},n.prototype.onOrientationTimer=function(){this.orientationSupport&&0===this.orientationStatus&&(this.disable(),this.orientationSupport=!1,this.enable())},n.prototype.onCalibrationTimer=function(){this.calibrationFlag=!0},n.prototype.onWindowResize=function(){this.updateDimensions()},n.prototype.onAnimationFrame=function(){var t=this.ix-this.cx,i=this.iy-this.cy;(Math.abs(t)>this.calibrationThreshold||Math.abs(i)>this.calibrationThreshold)&&this.queueCalibration(0),this.portrait?(this.mx=(this.calibrateX?i:this.iy)*this.scalarX,this.my=(this.calibrateY?t:this.ix)*this.scalarY):(this.mx=(this.calibrateX?t:this.ix)*this.scalarX,this.my=(this.calibrateY?i:this.iy)*this.scalarY),isNaN(parseFloat(this.limitX))||(this.mx=this.clamp(this.mx,-this.limitX,this.limitX)),isNaN(parseFloat(this.limitY))||(this.my=this.clamp(this.my,-this.limitY,this.limitY)),this.vx+=(this.mx-this.vx)*this.frictionX,this.vy+=(this.my-this.vy)*this.frictionY;for(var e=0,s=this.$layers.length;s>e;e++){var n=this.depths[e],o=this.$layers[e],a=this.vx*n*(this.invertX?-1:1),r=this.vy*n*(this.invertY?-1:1);this.setPosition(o,a,r)}this.raf=requestAnimationFrame(this.onAnimationFrame)},n.prototype.onDeviceOrientation=function(t){if(!this.desktop&&null!==t.beta&&null!==t.gamma){this.orientationStatus=1;var e=(t.beta||0)/a,s=(t.gamma||0)/a,n=i.innerHeight>i.innerWidth;this.portrait!==n&&(this.portrait=n,this.calibrationFlag=!0),this.calibrationFlag&&(this.calibrationFlag=!1,this.cx=e,this.cy=s),this.ix=e,this.iy=s}},n.prototype.onMouseMove=function(t){!this.orientationSupport&&this.relativeInput?(this.bounds=this.element.getBoundingClientRect(),this.ex=this.bounds.left,this.ey=this.bounds.top,this.ew=this.bounds.width,this.eh=this.bounds.height,this.ecx=this.ew/2,this.ecy=this.eh/2,this.ix=(t.clientX-this.ex-this.ecx)/this.ecx,this.iy=(t.clientY-this.ey-this.ecy)/this.ecy,this.clipRelativeInput&&(this.ix=Math.max(this.ix,-1),this.ix=Math.min(this.ix,1),this.iy=Math.max(this.iy,-1),this.iy=Math.min(this.iy,1))):(this.ix=(t.clientX-this.wcx)/this.wcx,this.iy=(t.clientY-this.wcy)/this.wcy)};var h={enable:n.prototype.enable,disable:n.prototype.disable,calibrate:n.prototype.calibrate,friction:n.prototype.friction,invert:n.prototype.invert,scalar:n.prototype.scalar,limit:n.prototype.limit};t.fn[o]=function(i){var e=arguments;return this.each(function(){var s=t(this),a=s.data(o);a||(a=new n(this,i),s.data(o,a)),h[i]&&a[i].apply(a,Array.prototype.slice.call(e,1))})}}(window.jQuery||window.Zepto,window,document),function(){for(var t=0,i=["ms","moz","webkit","o"],e=0;ec;c++)if(null!==this.vendors[c]?(h=this.vendors[c][0]+"transform",l=this.vendors[c][1]+"Transform"):(h="transform",l="transform"),n.style[l]!==s){o=!0;break}switch(t){case"2D":r=o;break;case"3D":if(o){var m=e.body||e.createElement("body"),u=e.documentElement,y=u.style.overflow;e.body||(u.style.overflow="hidden",u.appendChild(m),m.style.overflow="hidden",m.style.background=""),m.appendChild(n),n.style[l]="translate3d(1px,1px,1px)",a=i.getComputedStyle(n).getPropertyValue(h),r=a!==s&&a.length>0&&"none"!==a,u.style.overflow=y,m.removeChild(n)}}return r},n.prototype.ww=null,n.prototype.wh=null,n.prototype.wcx=null,n.prototype.wcy=null,n.prototype.portrait=null,n.prototype.desktop=!navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|BB10|mobi|tablet|opera mini|nexus 7)/i),n.prototype.vendors=[null,["-webkit-","webkit"],["-moz-","Moz"],["-o-","O"],["-ms-","ms"]],n.prototype.motionSupport=!!i.DeviceMotionEvent,n.prototype.orientationSupport=!!i.DeviceOrientationEvent,n.prototype.orientationStatus=0,n.prototype.transform2DSupport=n.prototype.transformSupport("2D"),n.prototype.transform3DSupport=n.prototype.transformSupport("3D"),n.prototype.propertyCache={},n.prototype.initialise=function(){"static"===this.$context.css("position")&&this.$context.css({position:"relative"}),this.$layers.css({position:"absolute",display:"block",left:0,top:0}),this.$layers.first().css({position:"relative"}),this.$layers.each(t.proxy(function(i,e){this.depths.push(t(e).data("depth")||0)},this)),this.accelerate(this.$context),this.accelerate(this.$layers),this.updateDimensions(),this.enable(),this.queueCalibration(this.calibrationDelay)},n.prototype.updateDimensions=function(){this.ww=i.innerWidth,this.wh=i.innerHeight,this.wcx=this.ww*this.originX,this.wcy=this.wh*this.originY},n.prototype.updateBounds=function(){this.bounds=this.element.getBoundingClientRect(),this.ex=this.bounds.left,this.ey=this.bounds.top,this.ew=this.bounds.width,this.eh=this.bounds.height,this.ecx=this.ew*this.originX,this.ecy=this.eh*this.originY},n.prototype.queueCalibration=function(t){clearTimeout(this.calibrationTimer),this.calibrationTimer=setTimeout(this.onCalibrationTimer,t)},n.prototype.enable=function(){this.enabled||(this.enabled=!0,this.orientationSupport?(this.portrait=null,i.addEventListener("deviceorientation",this.onDeviceOrientation),setTimeout(this.onOrientationTimer,this.supportDelay)):(this.cx=0,this.cy=0,this.portrait=!1,i.addEventListener("mousemove",this.onMouseMove)),i.addEventListener("resize",this.onWindowResize),this.raf=requestAnimationFrame(this.onAnimationFrame))},n.prototype.disable=function(){this.enabled&&(this.enabled=!1,this.orientationSupport?i.removeEventListener("deviceorientation",this.onDeviceOrientation):i.removeEventListener("mousemove",this.onMouseMove),i.removeEventListener("resize",this.onWindowResize),cancelAnimationFrame(this.raf))},n.prototype.calibrate=function(t,i){this.calibrateX=t===s?this.calibrateX:t,this.calibrateY=i===s?this.calibrateY:i},n.prototype.invert=function(t,i){this.invertX=t===s?this.invertX:t,this.invertY=i===s?this.invertY:i},n.prototype.friction=function(t,i){this.frictionX=t===s?this.frictionX:t,this.frictionY=i===s?this.frictionY:i},n.prototype.scalar=function(t,i){this.scalarX=t===s?this.scalarX:t,this.scalarY=i===s?this.scalarY:i},n.prototype.limit=function(t,i){this.limitX=t===s?this.limitX:t,this.limitY=i===s?this.limitY:i},n.prototype.clamp=function(t,i,e){return t=Math.max(t,i),t=Math.min(t,e)},n.prototype.css=function(i,e,n){var o=this.propertyCache[e];if(!o)for(var a=0,r=this.vendors.length;r>a;a++)if(o=null!==this.vendors[a]?t.camelCase(this.vendors[a][1]+"-"+e):e,i.style[o]!==s){this.propertyCache[e]=o;break}i.style[o]=n},n.prototype.accelerate=function(t){for(var i=0,e=t.length;e>i;i++){var s=t[i];this.css(s,"transform","translate3d(0,0,0)"),this.css(s,"transform-style","preserve-3d"),this.css(s,"backface-visibility","hidden")}},n.prototype.setPosition=function(t,i,e){i+="px",e+="px",this.transform3DSupport?this.css(t,"transform","translate3d("+i+","+e+",0)"):this.transform2DSupport?this.css(t,"transform","translate("+i+","+e+")"):(t.style.left=i,t.style.top=e)},n.prototype.onOrientationTimer=function(){this.orientationSupport&&0===this.orientationStatus&&(this.disable(),this.orientationSupport=!1,this.enable())},n.prototype.onCalibrationTimer=function(){this.calibrationFlag=!0},n.prototype.onWindowResize=function(){this.updateDimensions()},n.prototype.onAnimationFrame=function(){this.updateBounds();var t=this.ix-this.cx,i=this.iy-this.cy;(Math.abs(t)>this.calibrationThreshold||Math.abs(i)>this.calibrationThreshold)&&this.queueCalibration(0),this.portrait?(this.mx=this.calibrateX?i:this.iy,this.my=this.calibrateY?t:this.ix):(this.mx=this.calibrateX?t:this.ix,this.my=this.calibrateY?i:this.iy),this.mx*=this.ew/this.scalarX,this.my*=this.eh/this.scalarY,isNaN(parseFloat(this.limitX))||(this.mx=this.clamp(this.mx,-this.limitX,this.limitX)),isNaN(parseFloat(this.limitY))||(this.my=this.clamp(this.my,-this.limitY,this.limitY)),this.vx+=(this.mx-this.vx)*this.frictionX,this.vy+=(this.my-this.vy)*this.frictionY;for(var e=0,s=this.$layers.length;s>e;e++){var n=this.depths[e],o=this.$layers[e],a=this.vx*n*(this.invertX?-1:1),r=this.vy*n*(this.invertY?-1:1);this.setPosition(o,a,r)}this.raf=requestAnimationFrame(this.onAnimationFrame)},n.prototype.onDeviceOrientation=function(t){if(!this.desktop&&null!==t.beta&&null!==t.gamma){this.orientationStatus=1;var e=(t.beta||0)/a,s=(t.gamma||0)/a,n=i.innerHeight>i.innerWidth;this.portrait!==n&&(this.portrait=n,this.calibrationFlag=!0),this.calibrationFlag&&(this.calibrationFlag=!1,this.cx=e,this.cy=s),this.ix=e,this.iy=s}},n.prototype.onMouseMove=function(t){!this.orientationSupport&&this.relativeInput?(this.ix=(t.clientX-this.ex-this.ecx)/this.ecx,this.iy=(t.clientY-this.ey-this.ecy)/this.ecy,this.clipRelativeInput&&(this.ix=Math.max(this.ix,-1),this.ix=Math.min(this.ix,1),this.iy=Math.max(this.iy,-1),this.iy=Math.min(this.iy,1))):(this.ix=(t.clientX-this.wcx)/this.wcx,this.iy=(t.clientY-this.wcy)/this.wcy)};var h={enable:n.prototype.enable,disable:n.prototype.disable,calibrate:n.prototype.calibrate,friction:n.prototype.friction,invert:n.prototype.invert,scalar:n.prototype.scalar,limit:n.prototype.limit};t.fn[o]=function(i){var e=arguments;return this.each(function(){var s=t(this),a=s.data(o);a||(a=new n(this,i),s.data(o,a)),h[i]&&a[i].apply(a,Array.prototype.slice.call(e,1))})}}(window.jQuery||window.Zepto,window,document),function(){for(var t=0,i=["ms","moz","webkit","o"],e=0;e 0 && propertyValue !== "none"; + documentElement.style.overflow = documentOverflow; body.removeChild(element); } break; @@ -222,6 +231,7 @@ Parallax.prototype.orientationStatus = 0; Parallax.prototype.transform2DSupport = Parallax.prototype.transformSupport('2D'); Parallax.prototype.transform3DSupport = Parallax.prototype.transformSupport('3D'); + Parallax.prototype.propertyCache = {}; Parallax.prototype.initialise = function() { @@ -238,8 +248,6 @@ if (this.transform3DSupport) this.accelerate(layer); layer.style.position = i ? 'absolute' : 'relative'; layer.style.display = 'block'; - layer.style.height = '100%'; - layer.style.width = '100%'; layer.style.left = 0; layer.style.top = 0; @@ -260,6 +268,16 @@ this.wcy = this.wh * this.originY; }; + Parallax.prototype.updateBounds = function() { + this.bounds = this.element.getBoundingClientRect(); + this.ex = this.bounds.left; + this.ey = this.bounds.top; + this.ew = this.bounds.width; + this.eh = this.bounds.height; + this.ecx = this.ew * this.originX; + this.ecy = this.eh * this.originY; + }; + Parallax.prototype.queueCalibration = function(delay) { clearTimeout(this.calibrationTimer); this.calibrationTimer = setTimeout(this.onCalibrationTimer, delay); @@ -328,18 +346,21 @@ }; Parallax.prototype.css = function(element, property, value) { - var jsProperty = null; - for (var i = 0, l = this.vendors.length; i < l; i++) { - if (this.vendors[i] !== null) { - jsProperty = this.camelCase(this.vendors[i][1] + '-' + property); - } else { - jsProperty = property; - } - if (element.style[jsProperty] !== undefined) { - element.style[jsProperty] = value; - break; + var jsProperty = this.propertyCache[property]; + if (!jsProperty) { + for (var i = 0, l = this.vendors.length; i < l; i++) { + if (this.vendors[i] !== null) { + jsProperty = this.camelCase(this.vendors[i][1] + '-' + property); + } else { + jsProperty = property; + } + if (element.style[jsProperty] !== undefined) { + this.propertyCache[property] = jsProperty; + break; + } } } + element.style[jsProperty] = value; }; Parallax.prototype.accelerate = function(element) { @@ -349,8 +370,8 @@ }; Parallax.prototype.setPosition = function(element, x, y) { - x += '%'; - y += '%'; + x += 'px'; + y += 'px'; if (this.transform3DSupport) { this.css(element, 'transform', 'translate3d('+x+','+y+',0)'); } else if (this.transform2DSupport) { @@ -378,18 +399,21 @@ }; Parallax.prototype.onAnimationFrame = function() { + this.updateBounds(); var dx = this.ix - this.cx; var dy = this.iy - this.cy; if ((Math.abs(dx) > this.calibrationThreshold) || (Math.abs(dy) > this.calibrationThreshold)) { this.queueCalibration(0); } if (this.portrait) { - this.mx = (this.calibrateX ? dy : this.iy) * this.scalarX; - this.my = (this.calibrateY ? dx : this.ix) * this.scalarY; + this.mx = this.calibrateX ? dy : this.iy; + this.my = this.calibrateY ? dx : this.ix; } else { - this.mx = (this.calibrateX ? dx : this.ix) * this.scalarX; - this.my = (this.calibrateY ? dy : this.iy) * this.scalarY; + this.mx = this.calibrateX ? dx : this.ix; + this.my = this.calibrateY ? dy : this.iy; } + this.mx *= this.ew / this.scalarX; + this.my *= this.eh / this.scalarY; if (!isNaN(parseFloat(this.limitX))) { this.mx = this.clamp(this.mx, -this.limitX, this.limitX); } @@ -445,15 +469,6 @@ // Calculate Mouse Input if (!this.orientationSupport && this.relativeInput) { - // Extract element bounds. - this.bounds = this.element.getBoundingClientRect(); - this.ex = this.bounds.left; - this.ey = this.bounds.top; - this.ew = this.bounds.width; - this.eh = this.bounds.height; - this.ecx = this.ew * this.originX; - this.ecy = this.eh * this.originY; - // Calculate input relative to the element. this.ix = (event.clientX - this.ex - this.ecx) / this.ecx; this.iy = (event.clientY - this.ey - this.ecy) / this.ecy; diff --git a/deploy/parallax.min.js b/deploy/parallax.min.js index 7a07f59..e53a292 100644 --- a/deploy/parallax.min.js +++ b/deploy/parallax.min.js @@ -1 +1 @@ -!function(t,i,e){"use strict";function s(t,i){this.element=t,this.layers=t.getElementsByClassName("layer");var e={calibrateX:this.data(this.element,"calibrate-x"),calibrateY:this.data(this.element,"calibrate-y"),invertX:this.data(this.element,"invert-x"),invertY:this.data(this.element,"invert-y"),limitX:this.data(this.element,"limit-x"),limitY:this.data(this.element,"limit-y"),scalarX:this.data(this.element,"scalar-x"),scalarY:this.data(this.element,"scalar-y"),frictionX:this.data(this.element,"friction-x"),frictionY:this.data(this.element,"friction-y")};for(var s in e)null===e[s]&&delete e[s];this.extend(this,a,i,e),this.calibrationTimer=null,this.calibrationFlag=!0,this.enabled=!1,this.depths=[],this.raf=null,this.bounds=null,this.ex=0,this.ey=0,this.ew=0,this.eh=0,this.ecx=0,this.ecy=0,this.cx=0,this.cy=0,this.ix=0,this.iy=0,this.mx=0,this.my=0,this.vx=0,this.vy=0,this.onMouseMove=this.onMouseMove.bind(this),this.onDeviceOrientation=this.onDeviceOrientation.bind(this),this.onOrientationTimer=this.onOrientationTimer.bind(this),this.onCalibrationTimer=this.onCalibrationTimer.bind(this),this.onAnimationFrame=this.onAnimationFrame.bind(this),this.onWindowResize=this.onWindowResize.bind(this),this.initialise()}var n="Parallax",o=30,a={relativeInput:!1,clipRelativeInput:!1,calibrationThreshold:100,calibrationDelay:500,supportDelay:500,calibrateX:!1,calibrateY:!0,invertX:!0,invertY:!0,limitX:!1,limitY:!1,scalarX:10,scalarY:10,frictionX:.1,frictionY:.1,originX:.5,originY:.5};s.prototype.extend=function(){if(arguments.length>1)for(var t=arguments[0],i=1,e=arguments.length;e>i;i++){var s=arguments[i];for(var n in s)t[n]=s[n]}},s.prototype.data=function(t,i){return this.deserialize(t.getAttribute("data-"+i))},s.prototype.deserialize=function(t){return"true"===t?!0:"false"===t?!1:"null"===t?null:!isNaN(parseFloat(t))&&isFinite(t)?parseFloat(t):t},s.prototype.camelCase=function(t){return t.replace(/-+(.)?/g,function(t,i){return i?i.toUpperCase():""})},s.prototype.transformSupport=function(s){for(var n=i.createElement("div"),o=!1,a=null,r=!1,h=null,l=null,c=0,p=this.vendors.length;p>c;c++)if(null!==this.vendors[c]?(h=this.vendors[c][0]+"transform",l=this.vendors[c][1]+"Transform"):(h="transform",l="transform"),n.style[l]!==e){o=!0;break}switch(s){case"2D":r=o;break;case"3D":if(o){var m=i.body||i.createElement("body");m.appendChild(n),n.style[l]="translate3d(1px,1px,1px)",a=t.getComputedStyle(n).getPropertyValue(h),r=a!==e&&a.length>0&&"none"!==a,m.removeChild(n)}}return r},s.prototype.ww=null,s.prototype.wh=null,s.prototype.wcx=null,s.prototype.wcy=null,s.prototype.portrait=null,s.prototype.desktop=!navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|BB10|mobi|tablet|opera mini|nexus 7)/i),s.prototype.vendors=[null,["-webkit-","webkit"],["-moz-","Moz"],["-o-","O"],["-ms-","ms"]],s.prototype.motionSupport=!!t.DeviceMotionEvent,s.prototype.orientationSupport=!!t.DeviceOrientationEvent,s.prototype.orientationStatus=0,s.prototype.transform2DSupport=s.prototype.transformSupport("2D"),s.prototype.transform3DSupport=s.prototype.transformSupport("3D"),s.prototype.initialise=function(){this.transform3DSupport&&this.accelerate(this.element);var i=t.getComputedStyle(this.element);"static"===i.getPropertyValue("position")&&(this.element.style.position="relative");for(var e=0,s=this.layers.length;s>e;e++){var n=this.layers[e];this.transform3DSupport&&this.accelerate(n),n.style.position=e?"absolute":"relative",n.style.display="block",n.style.height="100%",n.style.width="100%",n.style.left=0,n.style.top=0,this.depths.push(this.data(n,"depth")||0)}this.updateDimensions(),this.enable(),this.queueCalibration(this.calibrationDelay)},s.prototype.updateDimensions=function(){this.ww=t.innerWidth,this.wh=t.innerHeight,this.wcx=this.ww*this.originX,this.wcy=this.wh*this.originY},s.prototype.queueCalibration=function(t){clearTimeout(this.calibrationTimer),this.calibrationTimer=setTimeout(this.onCalibrationTimer,t)},s.prototype.enable=function(){this.enabled||(this.enabled=!0,this.orientationSupport?(this.portrait=null,t.addEventListener("deviceorientation",this.onDeviceOrientation),setTimeout(this.onOrientationTimer,this.supportDelay)):(this.cx=0,this.cy=0,this.portrait=!1,t.addEventListener("mousemove",this.onMouseMove)),t.addEventListener("resize",this.onWindowResize),this.raf=requestAnimationFrame(this.onAnimationFrame))},s.prototype.disable=function(){this.enabled&&(this.enabled=!1,this.orientationSupport?t.removeEventListener("deviceorientation",this.onDeviceOrientation):t.removeEventListener("mousemove",this.onMouseMove),t.removeEventListener("resize",this.onWindowResize),cancelAnimationFrame(this.raf))},s.prototype.calibrate=function(t,i){this.calibrateX=t===e?this.calibrateX:t,this.calibrateY=i===e?this.calibrateY:i},s.prototype.invert=function(t,i){this.invertX=t===e?this.invertX:t,this.invertY=i===e?this.invertY:i},s.prototype.friction=function(t,i){this.frictionX=t===e?this.frictionX:t,this.frictionY=i===e?this.frictionY:i},s.prototype.scalar=function(t,i){this.scalarX=t===e?this.scalarX:t,this.scalarY=i===e?this.scalarY:i},s.prototype.limit=function(t,i){this.limitX=t===e?this.limitX:t,this.limitY=i===e?this.limitY:i},s.prototype.clamp=function(t,i,e){return t=Math.max(t,i),t=Math.min(t,e)},s.prototype.css=function(t,i,s){for(var n=null,o=0,a=this.vendors.length;a>o;o++)if(n=null!==this.vendors[o]?this.camelCase(this.vendors[o][1]+"-"+i):i,t.style[n]!==e){t.style[n]=s;break}},s.prototype.accelerate=function(t){this.css(t,"transform","translate3d(0,0,0)"),this.css(t,"transform-style","preserve-3d"),this.css(t,"backface-visibility","hidden")},s.prototype.setPosition=function(t,i,e){i+="%",e+="%",this.transform3DSupport?this.css(t,"transform","translate3d("+i+","+e+",0)"):this.transform2DSupport?this.css(t,"transform","translate("+i+","+e+")"):(t.style.left=i,t.style.top=e)},s.prototype.onOrientationTimer=function(){this.orientationSupport&&0===this.orientationStatus&&(this.disable(),this.orientationSupport=!1,this.enable())},s.prototype.onCalibrationTimer=function(){this.calibrationFlag=!0},s.prototype.onWindowResize=function(){this.updateDimensions()},s.prototype.onAnimationFrame=function(){var t=this.ix-this.cx,i=this.iy-this.cy;(Math.abs(t)>this.calibrationThreshold||Math.abs(i)>this.calibrationThreshold)&&this.queueCalibration(0),this.portrait?(this.mx=(this.calibrateX?i:this.iy)*this.scalarX,this.my=(this.calibrateY?t:this.ix)*this.scalarY):(this.mx=(this.calibrateX?t:this.ix)*this.scalarX,this.my=(this.calibrateY?i:this.iy)*this.scalarY),isNaN(parseFloat(this.limitX))||(this.mx=this.clamp(this.mx,-this.limitX,this.limitX)),isNaN(parseFloat(this.limitY))||(this.my=this.clamp(this.my,-this.limitY,this.limitY)),this.vx+=(this.mx-this.vx)*this.frictionX,this.vy+=(this.my-this.vy)*this.frictionY;for(var e=0,s=this.layers.length;s>e;e++){var n=this.layers[e],o=this.depths[e],a=this.vx*o*(this.invertX?-1:1),r=this.vy*o*(this.invertY?-1:1);this.setPosition(n,a,r)}this.raf=requestAnimationFrame(this.onAnimationFrame)},s.prototype.onDeviceOrientation=function(t){if(!this.desktop&&null!==t.beta&&null!==t.gamma){this.orientationStatus=1;var i=(t.beta||0)/o,e=(t.gamma||0)/o,s=this.wh>this.ww;this.portrait!==s&&(this.portrait=s,this.calibrationFlag=!0),this.calibrationFlag&&(this.calibrationFlag=!1,this.cx=i,this.cy=e),this.ix=i,this.iy=e}},s.prototype.onMouseMove=function(t){!this.orientationSupport&&this.relativeInput?(this.bounds=this.element.getBoundingClientRect(),this.ex=this.bounds.left,this.ey=this.bounds.top,this.ew=this.bounds.width,this.eh=this.bounds.height,this.ecx=this.ew*this.originX,this.ecy=this.eh*this.originY,this.ix=(t.clientX-this.ex-this.ecx)/this.ecx,this.iy=(t.clientY-this.ey-this.ecy)/this.ecy,this.clipRelativeInput&&(this.ix=Math.max(this.ix,-1),this.ix=Math.min(this.ix,1),this.iy=Math.max(this.iy,-1),this.iy=Math.min(this.iy,1))):(this.ix=(t.clientX-this.wcx)/this.wcx,this.iy=(t.clientY-this.wcy)/this.wcy)},t[n]=s}(window,document),function(){for(var t=0,i=["ms","moz","webkit","o"],e=0;e1)for(var t=arguments[0],i=1,e=arguments.length;e>i;i++){var s=arguments[i];for(var n in s)t[n]=s[n]}},s.prototype.data=function(t,i){return this.deserialize(t.getAttribute("data-"+i))},s.prototype.deserialize=function(t){return"true"===t?!0:"false"===t?!1:"null"===t?null:!isNaN(parseFloat(t))&&isFinite(t)?parseFloat(t):t},s.prototype.camelCase=function(t){return t.replace(/-+(.)?/g,function(t,i){return i?i.toUpperCase():""})},s.prototype.transformSupport=function(s){for(var n=i.createElement("div"),o=!1,r=null,a=!1,h=null,l=null,p=0,c=this.vendors.length;c>p;p++)if(null!==this.vendors[p]?(h=this.vendors[p][0]+"transform",l=this.vendors[p][1]+"Transform"):(h="transform",l="transform"),n.style[l]!==e){o=!0;break}switch(s){case"2D":a=o;break;case"3D":if(o){var m=i.body||i.createElement("body"),u=i.documentElement,y=u.style.overflow;i.body||(u.style.overflow="hidden",u.appendChild(m),m.style.overflow="hidden",m.style.background=""),m.appendChild(n),n.style[l]="translate3d(1px,1px,1px)",r=t.getComputedStyle(n).getPropertyValue(h),a=r!==e&&r.length>0&&"none"!==r,u.style.overflow=y,m.removeChild(n)}}return a},s.prototype.ww=null,s.prototype.wh=null,s.prototype.wcx=null,s.prototype.wcy=null,s.prototype.portrait=null,s.prototype.desktop=!navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|BB10|mobi|tablet|opera mini|nexus 7)/i),s.prototype.vendors=[null,["-webkit-","webkit"],["-moz-","Moz"],["-o-","O"],["-ms-","ms"]],s.prototype.motionSupport=!!t.DeviceMotionEvent,s.prototype.orientationSupport=!!t.DeviceOrientationEvent,s.prototype.orientationStatus=0,s.prototype.transform2DSupport=s.prototype.transformSupport("2D"),s.prototype.transform3DSupport=s.prototype.transformSupport("3D"),s.prototype.propertyCache={},s.prototype.initialise=function(){this.transform3DSupport&&this.accelerate(this.element);var i=t.getComputedStyle(this.element);"static"===i.getPropertyValue("position")&&(this.element.style.position="relative");for(var e=0,s=this.layers.length;s>e;e++){var n=this.layers[e];this.transform3DSupport&&this.accelerate(n),n.style.position=e?"absolute":"relative",n.style.display="block",n.style.left=0,n.style.top=0,this.depths.push(this.data(n,"depth")||0)}this.updateDimensions(),this.enable(),this.queueCalibration(this.calibrationDelay)},s.prototype.updateDimensions=function(){this.ww=t.innerWidth,this.wh=t.innerHeight,this.wcx=this.ww*this.originX,this.wcy=this.wh*this.originY},s.prototype.updateBounds=function(){this.bounds=this.element.getBoundingClientRect(),this.ex=this.bounds.left,this.ey=this.bounds.top,this.ew=this.bounds.width,this.eh=this.bounds.height,this.ecx=this.ew*this.originX,this.ecy=this.eh*this.originY},s.prototype.queueCalibration=function(t){clearTimeout(this.calibrationTimer),this.calibrationTimer=setTimeout(this.onCalibrationTimer,t)},s.prototype.enable=function(){this.enabled||(this.enabled=!0,this.orientationSupport?(this.portrait=null,t.addEventListener("deviceorientation",this.onDeviceOrientation),setTimeout(this.onOrientationTimer,this.supportDelay)):(this.cx=0,this.cy=0,this.portrait=!1,t.addEventListener("mousemove",this.onMouseMove)),t.addEventListener("resize",this.onWindowResize),this.raf=requestAnimationFrame(this.onAnimationFrame))},s.prototype.disable=function(){this.enabled&&(this.enabled=!1,this.orientationSupport?t.removeEventListener("deviceorientation",this.onDeviceOrientation):t.removeEventListener("mousemove",this.onMouseMove),t.removeEventListener("resize",this.onWindowResize),cancelAnimationFrame(this.raf))},s.prototype.calibrate=function(t,i){this.calibrateX=t===e?this.calibrateX:t,this.calibrateY=i===e?this.calibrateY:i},s.prototype.invert=function(t,i){this.invertX=t===e?this.invertX:t,this.invertY=i===e?this.invertY:i},s.prototype.friction=function(t,i){this.frictionX=t===e?this.frictionX:t,this.frictionY=i===e?this.frictionY:i},s.prototype.scalar=function(t,i){this.scalarX=t===e?this.scalarX:t,this.scalarY=i===e?this.scalarY:i},s.prototype.limit=function(t,i){this.limitX=t===e?this.limitX:t,this.limitY=i===e?this.limitY:i},s.prototype.clamp=function(t,i,e){return t=Math.max(t,i),t=Math.min(t,e)},s.prototype.css=function(t,i,s){var n=this.propertyCache[i];if(!n)for(var o=0,r=this.vendors.length;r>o;o++)if(n=null!==this.vendors[o]?this.camelCase(this.vendors[o][1]+"-"+i):i,t.style[n]!==e){this.propertyCache[i]=n;break}t.style[n]=s},s.prototype.accelerate=function(t){this.css(t,"transform","translate3d(0,0,0)"),this.css(t,"transform-style","preserve-3d"),this.css(t,"backface-visibility","hidden")},s.prototype.setPosition=function(t,i,e){i+="px",e+="px",this.transform3DSupport?this.css(t,"transform","translate3d("+i+","+e+",0)"):this.transform2DSupport?this.css(t,"transform","translate("+i+","+e+")"):(t.style.left=i,t.style.top=e)},s.prototype.onOrientationTimer=function(){this.orientationSupport&&0===this.orientationStatus&&(this.disable(),this.orientationSupport=!1,this.enable())},s.prototype.onCalibrationTimer=function(){this.calibrationFlag=!0},s.prototype.onWindowResize=function(){this.updateDimensions()},s.prototype.onAnimationFrame=function(){this.updateBounds();var t=this.ix-this.cx,i=this.iy-this.cy;(Math.abs(t)>this.calibrationThreshold||Math.abs(i)>this.calibrationThreshold)&&this.queueCalibration(0),this.portrait?(this.mx=this.calibrateX?i:this.iy,this.my=this.calibrateY?t:this.ix):(this.mx=this.calibrateX?t:this.ix,this.my=this.calibrateY?i:this.iy),this.mx*=this.ew/this.scalarX,this.my*=this.eh/this.scalarY,isNaN(parseFloat(this.limitX))||(this.mx=this.clamp(this.mx,-this.limitX,this.limitX)),isNaN(parseFloat(this.limitY))||(this.my=this.clamp(this.my,-this.limitY,this.limitY)),this.vx+=(this.mx-this.vx)*this.frictionX,this.vy+=(this.my-this.vy)*this.frictionY;for(var e=0,s=this.layers.length;s>e;e++){var n=this.layers[e],o=this.depths[e],r=this.vx*o*(this.invertX?-1:1),a=this.vy*o*(this.invertY?-1:1);this.setPosition(n,r,a)}this.raf=requestAnimationFrame(this.onAnimationFrame)},s.prototype.onDeviceOrientation=function(t){if(!this.desktop&&null!==t.beta&&null!==t.gamma){this.orientationStatus=1;var i=(t.beta||0)/o,e=(t.gamma||0)/o,s=this.wh>this.ww;this.portrait!==s&&(this.portrait=s,this.calibrationFlag=!0),this.calibrationFlag&&(this.calibrationFlag=!1,this.cx=i,this.cy=e),this.ix=i,this.iy=e}},s.prototype.onMouseMove=function(t){!this.orientationSupport&&this.relativeInput?(this.ix=(t.clientX-this.ex-this.ecx)/this.ecx,this.iy=(t.clientY-this.ey-this.ecy)/this.ecy,this.clipRelativeInput&&(this.ix=Math.max(this.ix,-1),this.ix=Math.min(this.ix,1),this.iy=Math.max(this.iy,-1),this.iy=Math.min(this.iy,1))):(this.ix=(t.clientX-this.wcx)/this.wcx,this.iy=(t.clientY-this.wcy)/this.wcy)},t[n]=s}(window,document),function(){for(var t=0,i=["ms","moz","webkit","o"],e=0;e + + + + + + Parallax.js | Interactive Example + + + + + + + + + + + +
+ +
    +
  • +
  • +
  • +
  • +
  • +
  • +
+
+ + + + + + + diff --git a/examples/styles/styles.css b/examples/styles/styles.css index 1ca6b1a..9b0702e 100644 --- a/examples/styles/styles.css +++ b/examples/styles/styles.css @@ -22,6 +22,29 @@ label { cursor: pointer; } +button { + border: 10px solid #00FFAA; + font-family: monospace; + position: absolute; + line-height: 60px; + text-align: right; + padding: 0 1.2em; + background: #444; + font-size: 24px; + cursor: pointer; + display: block; + outline: none; + height: 80px; + width: 80%; + top: 260px; + margin: 0; + left: 10%; +} + +button:hover { + background: #00FFAA; +} + input[type=checkbox] + label:before { display: inline-block; position: relative; @@ -38,6 +61,7 @@ input[type=checkbox]:checked + label:before { } .container { + position: relative; max-width: 600px; margin: 0 auto; padding: 5%; @@ -48,25 +72,63 @@ input[type=checkbox]:checked + label:before { margin: 0; } +.fill { + position: absolute; + bottom: 5%; + right: 5%; + left: 5%; + top: 5%; +} + +.expand-width { + width: 100%; +} + .border { border: 2px dashed #00FFAA; } +.aspect { + opacity: 0.2; +} + .layer:nth-child(1) { opacity: 0.15; } +.layer:nth-child(1) button { + -webkit-transform: rotate(150deg); +} + .layer:nth-child(2) { opacity: 0.30; } +.layer:nth-child(2) button { + -webkit-transform: rotate(120deg); +} + .layer:nth-child(3) { opacity: 0.45; } +.layer:nth-child(3) button { + -webkit-transform: rotate(90deg); +} + .layer:nth-child(4) { opacity: 0.60; } +.layer:nth-child(4) button { + -webkit-transform: rotate(60deg); +} + .layer:nth-child(5) { opacity: 0.75; } +.layer:nth-child(5) button { + -webkit-transform: rotate(30deg); +} + .layer:nth-child(6) { opacity: 0.90; } +.layer:nth-child(6) button { +} diff --git a/package.json b/package.json index 4e46e3b..c1da070 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "parallax", "description": "Parallax Engine that reacts to the orientation of a smart device.", - "version": "1.1.1", + "version": "2.0.0", "license": "MIT", "homepage": "http://wagerfield.github.io/parallax/", "author": "Matthew Wagerfield ", diff --git a/source/jquery.parallax.js b/source/jquery.parallax.js index 8855b01..c80d22d 100644 --- a/source/jquery.parallax.js +++ b/source/jquery.parallax.js @@ -28,7 +28,9 @@ scalarX: 10.0, scalarY: 10.0, frictionX: 0.1, - frictionY: 0.1 + frictionY: 0.1, + originX: 0.5, + originY: 0.5 }; function Plugin(element, options) { @@ -132,11 +134,21 @@ break; case '3D': if (propertySupport) { - document.body.appendChild(element); + var body = document.body || document.createElement('body'); + var documentElement = document.documentElement; + var documentOverflow = documentElement.style.overflow; + if (!document.body) { + documentElement.style.overflow = 'hidden'; + documentElement.appendChild(body); + body.style.overflow = 'hidden'; + body.style.background = ''; + } + body.appendChild(element); element.style[jsProperty] = 'translate3d(1px,1px,1px)'; propertyValue = window.getComputedStyle(element).getPropertyValue(cssProperty); featureSupport = propertyValue !== undefined && propertyValue.length > 0 && propertyValue !== "none"; - document.body.removeChild(element); + documentElement.style.overflow = documentOverflow; + body.removeChild(element); } break; } @@ -155,6 +167,7 @@ Plugin.prototype.orientationStatus = 0; Plugin.prototype.transform2DSupport = Plugin.prototype.transformSupport('2D'); Plugin.prototype.transform3DSupport = Plugin.prototype.transformSupport('3D'); + Plugin.prototype.propertyCache = {}; Plugin.prototype.initialise = function() { @@ -167,8 +180,6 @@ this.$layers.css({ position:'absolute', display:'block', - height:'100%', - width:'100%', left: 0, top: 0 }); @@ -194,8 +205,18 @@ Plugin.prototype.updateDimensions = function() { this.ww = window.innerWidth; this.wh = window.innerHeight; - this.wcx = this.ww / 2; - this.wcy = this.wh / 2; + this.wcx = this.ww * this.originX; + this.wcy = this.wh * this.originY; + }; + + Plugin.prototype.updateBounds = function() { + this.bounds = this.element.getBoundingClientRect(); + this.ex = this.bounds.left; + this.ey = this.bounds.top; + this.ew = this.bounds.width; + this.eh = this.bounds.height; + this.ecx = this.ew * this.originX; + this.ecy = this.eh * this.originY; }; Plugin.prototype.queueCalibration = function(delay) { @@ -266,18 +287,21 @@ }; Plugin.prototype.css = function(element, property, value) { - var jsProperty = null; - for (var i = 0, l = this.vendors.length; i < l; i++) { - if (this.vendors[i] !== null) { - jsProperty = $.camelCase(this.vendors[i][1] + '-' + property); - } else { - jsProperty = property; - } - if (element.style[jsProperty] !== undefined) { - element.style[jsProperty] = value; - break; + var jsProperty = this.propertyCache[property]; + if (!jsProperty) { + for (var i = 0, l = this.vendors.length; i < l; i++) { + if (this.vendors[i] !== null) { + jsProperty = $.camelCase(this.vendors[i][1] + '-' + property); + } else { + jsProperty = property; + } + if (element.style[jsProperty] !== undefined) { + this.propertyCache[property] = jsProperty; + break; + } } } + element.style[jsProperty] = value; }; Plugin.prototype.accelerate = function($element) { @@ -290,8 +314,8 @@ }; Plugin.prototype.setPosition = function(element, x, y) { - x += '%'; - y += '%'; + x += 'px'; + y += 'px'; if (this.transform3DSupport) { this.css(element, 'transform', 'translate3d('+x+','+y+',0)'); } else if (this.transform2DSupport) { @@ -319,18 +343,21 @@ }; Plugin.prototype.onAnimationFrame = function() { + this.updateBounds(); var dx = this.ix - this.cx; var dy = this.iy - this.cy; if ((Math.abs(dx) > this.calibrationThreshold) || (Math.abs(dy) > this.calibrationThreshold)) { this.queueCalibration(0); } if (this.portrait) { - this.mx = (this.calibrateX ? dy : this.iy) * this.scalarX; - this.my = (this.calibrateY ? dx : this.ix) * this.scalarY; + this.mx = this.calibrateX ? dy : this.iy; + this.my = this.calibrateY ? dx : this.ix; } else { - this.mx = (this.calibrateX ? dx : this.ix) * this.scalarX; - this.my = (this.calibrateY ? dy : this.iy) * this.scalarY; + this.mx = this.calibrateX ? dx : this.ix; + this.my = this.calibrateY ? dy : this.iy; } + this.mx *= this.ew / this.scalarX; + this.my *= this.eh / this.scalarY; if (!isNaN(parseFloat(this.limitX))) { this.mx = this.clamp(this.mx, -this.limitX, this.limitX); } @@ -387,13 +414,6 @@ if (!this.orientationSupport && this.relativeInput) { // Calculate input relative to the element. - this.bounds = this.element.getBoundingClientRect(); - this.ex = this.bounds.left; - this.ey = this.bounds.top; - this.ew = this.bounds.width; - this.eh = this.bounds.height; - this.ecx = this.ew / 2; - this.ecy = this.eh / 2; this.ix = (event.clientX - this.ex - this.ecx) / this.ecx; this.iy = (event.clientY - this.ey - this.ecy) / this.ecy; diff --git a/source/parallax.js b/source/parallax.js index b8d02b1..98f69ce 100644 --- a/source/parallax.js +++ b/source/parallax.js @@ -168,10 +168,19 @@ case '3D': if (propertySupport) { var body = document.body || document.createElement('body'); + var documentElement = document.documentElement; + var documentOverflow = documentElement.style.overflow; + if (!document.body) { + documentElement.style.overflow = 'hidden'; + documentElement.appendChild(body); + body.style.overflow = 'hidden'; + body.style.background = ''; + } body.appendChild(element); element.style[jsProperty] = 'translate3d(1px,1px,1px)'; propertyValue = window.getComputedStyle(element).getPropertyValue(cssProperty); featureSupport = propertyValue !== undefined && propertyValue.length > 0 && propertyValue !== "none"; + documentElement.style.overflow = documentOverflow; body.removeChild(element); } break; @@ -191,6 +200,7 @@ Parallax.prototype.orientationStatus = 0; Parallax.prototype.transform2DSupport = Parallax.prototype.transformSupport('2D'); Parallax.prototype.transform3DSupport = Parallax.prototype.transformSupport('3D'); + Parallax.prototype.propertyCache = {}; Parallax.prototype.initialise = function() { @@ -207,8 +217,6 @@ if (this.transform3DSupport) this.accelerate(layer); layer.style.position = i ? 'absolute' : 'relative'; layer.style.display = 'block'; - layer.style.height = '100%'; - layer.style.width = '100%'; layer.style.left = 0; layer.style.top = 0; @@ -229,6 +237,16 @@ this.wcy = this.wh * this.originY; }; + Parallax.prototype.updateBounds = function() { + this.bounds = this.element.getBoundingClientRect(); + this.ex = this.bounds.left; + this.ey = this.bounds.top; + this.ew = this.bounds.width; + this.eh = this.bounds.height; + this.ecx = this.ew * this.originX; + this.ecy = this.eh * this.originY; + }; + Parallax.prototype.queueCalibration = function(delay) { clearTimeout(this.calibrationTimer); this.calibrationTimer = setTimeout(this.onCalibrationTimer, delay); @@ -297,18 +315,21 @@ }; Parallax.prototype.css = function(element, property, value) { - var jsProperty = null; - for (var i = 0, l = this.vendors.length; i < l; i++) { - if (this.vendors[i] !== null) { - jsProperty = this.camelCase(this.vendors[i][1] + '-' + property); - } else { - jsProperty = property; - } - if (element.style[jsProperty] !== undefined) { - element.style[jsProperty] = value; - break; + var jsProperty = this.propertyCache[property]; + if (!jsProperty) { + for (var i = 0, l = this.vendors.length; i < l; i++) { + if (this.vendors[i] !== null) { + jsProperty = this.camelCase(this.vendors[i][1] + '-' + property); + } else { + jsProperty = property; + } + if (element.style[jsProperty] !== undefined) { + this.propertyCache[property] = jsProperty; + break; + } } } + element.style[jsProperty] = value; }; Parallax.prototype.accelerate = function(element) { @@ -318,8 +339,8 @@ }; Parallax.prototype.setPosition = function(element, x, y) { - x += '%'; - y += '%'; + x += 'px'; + y += 'px'; if (this.transform3DSupport) { this.css(element, 'transform', 'translate3d('+x+','+y+',0)'); } else if (this.transform2DSupport) { @@ -347,18 +368,21 @@ }; Parallax.prototype.onAnimationFrame = function() { + this.updateBounds(); var dx = this.ix - this.cx; var dy = this.iy - this.cy; if ((Math.abs(dx) > this.calibrationThreshold) || (Math.abs(dy) > this.calibrationThreshold)) { this.queueCalibration(0); } if (this.portrait) { - this.mx = (this.calibrateX ? dy : this.iy) * this.scalarX; - this.my = (this.calibrateY ? dx : this.ix) * this.scalarY; + this.mx = this.calibrateX ? dy : this.iy; + this.my = this.calibrateY ? dx : this.ix; } else { - this.mx = (this.calibrateX ? dx : this.ix) * this.scalarX; - this.my = (this.calibrateY ? dy : this.iy) * this.scalarY; + this.mx = this.calibrateX ? dx : this.ix; + this.my = this.calibrateY ? dy : this.iy; } + this.mx *= this.ew / this.scalarX; + this.my *= this.eh / this.scalarY; if (!isNaN(parseFloat(this.limitX))) { this.mx = this.clamp(this.mx, -this.limitX, this.limitX); } @@ -414,15 +438,6 @@ // Calculate Mouse Input if (!this.orientationSupport && this.relativeInput) { - // Extract element bounds. - this.bounds = this.element.getBoundingClientRect(); - this.ex = this.bounds.left; - this.ey = this.bounds.top; - this.ew = this.bounds.width; - this.eh = this.bounds.height; - this.ecx = this.ew * this.originX; - this.ecy = this.eh * this.originY; - // Calculate input relative to the element. this.ix = (event.clientX - this.ex - this.ecx) / this.ecx; this.iy = (event.clientY - this.ey - this.ecy) / this.ecy;