diff --git a/README.md b/README.md index 519d122..69f32fb 100644 --- a/README.md +++ b/README.md @@ -40,18 +40,20 @@ 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 | -| ------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------ | -| `calibrate-x` | `true` or `false` | Specifies whether or not to cache & calculate the motion relative to the initial `x` axis value on initialisation. | -| `calibrate-y` | `true` or `false` | Specifies whether or not to cache & calculate the motion relative to the initial `y` axis value on initialisation. | -| `invert-x` | `true` or `false` | `true` moves layers in opposition to the device motion, `false` slides them away. | -| `invert-y` | `true` or `false` | `true` moves layers in opposition to the device motion, `false` slides them away. | -| `limit-x` | `number` or `false` | A numeric value limits the total range of motion in `x`, `false` allows layers to move with complete freedom. | -| `limit-y` | `number` or `false` | A numeric value limits the total range of motion in `y`, `false` allows layers to move with complete freedom. | -| `scalar-x` | `number` | Multiplies the input motion by this value, increasing or decreasing the sensitivity of the layer motion. | -| `scalar-y` | `number` | Multiplies the input motion by this value, increasing or decreasing the sensitivity of the layer motion. | -| `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. | +| Behaviour | Values | Description | +| ------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| `relativeInput` | `true` or `false` | Specifies whether or not to use the coordinate system of the `element` passed to the parallax `constructor`. **Mouse input only.** | +| `clipRelativeInput` | `true` or `false` | Specifies whether or not to clip the mouse input to the bounds of the `element` passed to the parallax `constructor`. **Mouse input only.** | +| `calibrate-x` | `true` or `false` | Specifies whether or not to cache & calculate the motion relative to the initial `x` axis value on initialisation. | +| `calibrate-y` | `true` or `false` | Specifies whether or not to cache & calculate the motion relative to the initial `y` axis value on initialisation. | +| `invert-x` | `true` or `false` | `true` moves layers in opposition to the device motion, `false` slides them away. | +| `invert-y` | `true` or `false` | `true` moves layers in opposition to the device motion, `false` slides them away. | +| `limit-x` | `number` or `false` | A numeric value limits the total range of motion in `x`, `false` allows layers to move with complete freedom. | +| `limit-y` | `number` or `false` | A numeric value limits the total range of motion in `y`, `false` allows layers to move with complete freedom. | +| `scalar-x` | `number` | Multiplies the input motion by this value, increasing or decreasing the sensitivity of the layer motion. | +| `scalar-y` | `number` | Multiplies the input motion by this value, increasing or decreasing the sensitivity of the layer motion. | +| `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. diff --git a/bower.json b/bower.json index a36d417..2024268 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.0.0", + "version": "1.1.0", "license": "MIT", "homepage": "http://wagerfield.github.io/parallax/", "authors": [ diff --git a/deploy/jquery.parallax.js b/deploy/jquery.parallax.js index ff80886..1139e60 100644 --- a/deploy/jquery.parallax.js +++ b/deploy/jquery.parallax.js @@ -45,6 +45,8 @@ var NAME = 'parallax'; var MAGIC_NUMBER = 30; var DEFAULTS = { + relativeInput: false, + clipRelativeInput: false, calibrationThreshold: 100, calibrationDelay: 500, supportDelay: 500, @@ -98,11 +100,14 @@ this.depths = []; this.raf = null; - // Offset - this.ox = 0; - this.oy = 0; - this.ow = 0; - this.oh = 0; + // Element + this.bounds = null; + this.ex = 0; + this.ey = 0; + this.ew = 0; + this.eh = 0; + this.ecx = 0; + this.ecy = 0; // Calibration this.cx = 0; @@ -171,8 +176,8 @@ Plugin.prototype.ww = null; Plugin.prototype.wh = null; - Plugin.prototype.hw = null; - Plugin.prototype.hh = null; + Plugin.prototype.wcx = null; + Plugin.prototype.wcy = null; Plugin.prototype.portrait = null; Plugin.prototype.desktop = !navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|BB10|mobi|tablet|opera mini|nexus 7)/i); Plugin.prototype.vendors = [null,['-webkit-','webkit'],['-moz-','Moz'],['-o-','O'],['-ms-','ms']]; @@ -218,18 +223,10 @@ }; Plugin.prototype.updateDimensions = function() { - - // Cache Context Dimensions - this.ox = this.$context.offset().left; - this.oy = this.$context.offset().top; - this.ow = this.$context.width(); - this.oh = this.$context.height(); - - // Cache Window Dimensions this.ww = window.innerWidth; this.wh = window.innerHeight; - this.hw = this.ww / 2; - this.hh = this.wh / 2; + this.wcw = this.ww / 2; + this.wcy = this.wh / 2; }; Plugin.prototype.queueCalibration = function(delay) { @@ -417,9 +414,34 @@ Plugin.prototype.onMouseMove = function(event) { - // Calculate Input - this.ix = (event.pageX - this.hw) / this.hw; - this.iy = (event.pageY - this.hh) / this.hh; + // Calculate Mouse Input + 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; + + // Clip input to the element bounds. + if (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); + } + + } else { + + // Calculate input relative to the window. + this.ix = (event.clientX - this.wcx) / this.wcx; + this.iy = (event.clientY - this.wcy) / this.wcy; + } }; var API = { diff --git a/deploy/jquery.parallax.min.js b/deploy/jquery.parallax.min.js index e10202a..832811c 100644 --- a/deploy/jquery.parallax.min.js +++ b/deploy/jquery.parallax.min.js @@ -1 +1 @@ -!function(t,i,e,o){"use strict";function n(i,e){this.element=i,this.$context=t(i).data("api",this),this.$layers=this.$context.find(".layer");var o={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 o)null===o[n]&&delete o[n];t.extend(this,r,e,o),this.calibrationTimer=null,this.calibrationFlag=!0,this.enabled=!1,this.depths=[],this.raf=null,this.ox=0,this.oy=0,this.ow=0,this.oh=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 s="parallax",a=30,r={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"),s=!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]!==o){s=!0;break}switch(t){case"2D":r=s;break;case"3D":s&&(e.body.appendChild(n),n.style[l]="translate3d(1px,1px,1px)",a=i.getComputedStyle(n).getPropertyValue(h),r=a!==o&&a.length>0&&"none"!==a,e.body.removeChild(n))}return r},n.prototype.ww=null,n.prototype.wh=null,n.prototype.hw=null,n.prototype.hh=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.ox=this.$context.offset().left,this.oy=this.$context.offset().top,this.ow=this.$context.width(),this.oh=this.$context.height(),this.ww=i.innerWidth,this.wh=i.innerHeight,this.hw=this.ww/2,this.hh=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===o?this.calibrateX:t,this.calibrateY=i===o?this.calibrateY:i},n.prototype.invert=function(t,i){this.invertX=t===o?this.invertX:t,this.invertY=i===o?this.invertY:i},n.prototype.friction=function(t,i){this.frictionX=t===o?this.frictionX:t,this.frictionY=i===o?this.frictionY:i},n.prototype.scalar=function(t,i){this.scalarX=t===o?this.scalarX:t,this.scalarY=i===o?this.scalarY:i},n.prototype.limit=function(t,i){this.limitX=t===o?this.limitX:t,this.limitY=i===o?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 s=null,a=0,r=this.vendors.length;r>a;a++)if(s=null!==this.vendors[a]?t.camelCase(this.vendors[a][1]+"-"+e):e,i.style[s]!==o){i.style[s]=n;break}},n.prototype.accelerate=function(t){for(var i=0,e=t.length;e>i;i++){var o=t[i];this.css(o,"transform","translate3d(0,0,0)"),this.css(o,"transform-style","preserve-3d"),this.css(o,"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,o=this.$layers.length;o>e;e++){var n=this.depths[e],s=this.$layers[e],a=this.vx*n*(this.invertX?-1:1),r=this.vy*n*(this.invertY?-1:1);this.setPosition(s,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,o=(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=o),this.ix=e,this.iy=o}},n.prototype.onMouseMove=function(t){this.ix=(t.pageX-this.hw)/this.hw,this.iy=(t.pageY-this.hh)/this.hh};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[s]=function(i){var e=arguments;return this.each(function(){var o=t(this),a=o.data(s);a||(a=new n(this,i),o.data(s,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":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.wcw=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;e1)for(var t=arguments[0],i=1,e=arguments.length;e>i;i++){var s=arguments[i];for(var o in s)t[o]=s[o]}},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.offset=function(t){for(var e,s,o=0,n=0;t&&!isNaN(t.offsetLeft)&&!isNaN(t.offsetTop);)t===i.body?(e=i.documentElement.scrollLeft,s=i.documentElement.scrollTop):(e=t.scrollLeft,s=t.scrollTop),o+=t.offsetLeft-e,n+=t.offsetTop-s,t=t.offsetParent;return{top:n,left:o}},s.prototype.camelCase=function(t){return t.replace(/-+(.)?/g,function(t,i){return i?i.toUpperCase():""})},s.prototype.transformSupport=function(s){for(var o=i.createElement("div"),n=!1,a=null,r=!1,h=null,l=null,p=0,m=this.vendors.length;m>p;p++)if(null!==this.vendors[p]?(h=this.vendors[p][0]+"transform",l=this.vendors[p][1]+"Transform"):(h="transform",l="transform"),o.style[l]!==e){n=!0;break}switch(s){case"2D":r=n;break;case"3D":n&&(i.body.appendChild(o),o.style[l]="translate3d(1px,1px,1px)",a=t.getComputedStyle(o).getPropertyValue(h),r=a!==e&&a.length>0&&"none"!==a,i.body.removeChild(o))}return r},s.prototype.ww=null,s.prototype.wh=null,s.prototype.hw=null,s.prototype.hh=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 o=this.layers[e];this.transform3DSupport&&this.accelerate(o),o.style.position=e?"absolute":"relative",o.style.display="block",o.style.height="100%",o.style.width="100%",o.style.left=0,o.style.top=0,this.depths.push(this.data(o,"depth")||0)}this.updateDimensions(),this.enable(),this.queueCalibration(this.calibrationDelay)},s.prototype.updateDimensions=function(){this.ox=this.offset(this.element).left,this.oy=this.offset(this.element).top,this.ow=this.element.offsetWidth,this.oh=this.element.offsetHeight,this.ww=t.innerWidth,this.wh=t.innerHeight,this.hw=this.ww/2,this.hh=this.wh/2},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 o=null,n=0,a=this.vendors.length;a>n;n++)if(o=null!==this.vendors[n]?this.camelCase(this.vendors[n][1]+"-"+i):i,t.style[o]!==e){t.style[o]=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 o=this.layers[e],n=this.depths[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)},s.prototype.onDeviceOrientation=function(t){if(!this.desktop&&null!==t.beta&&null!==t.gamma){this.orientationStatus=1;var i=(t.beta||0)/n,e=(t.gamma||0)/n,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.ix=(t.pageX-this.hw)/this.hw,this.iy=(t.pageY-this.hh)/this.hh},t[o]=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.offset=function(t){for(var e,s,n=0,o=0;t&&!isNaN(t.offsetLeft)&&!isNaN(t.offsetTop);)t===i.body?(e=i.documentElement.scrollLeft,s=i.documentElement.scrollTop):(e=t.scrollLeft,s=t.scrollTop),n+=t.offsetLeft-e,o+=t.offsetTop-s,t=t.offsetParent;return{top:o,left:n}},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":o&&(i.body.appendChild(n),n.style[l]="translate3d(1px,1px,1px)",a=t.getComputedStyle(n).getPropertyValue(h),r=a!==e&&a.length>0&&"none"!==a,i.body.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/2,this.wcy=this.wh/2},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/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)},t[n]=s}(window,document),function(){for(var t=0,i=["ms","moz","webkit","o"],e=0;e + + + + + + Parallax.js | Relative Example + + + + + + + + + + + +
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
+
+ + + + +
+ + + + + + + diff --git a/examples/styles/styles.css b/examples/styles/styles.css index 1eec51b..1ca6b1a 100644 --- a/examples/styles/styles.css +++ b/examples/styles/styles.css @@ -1,5 +1,8 @@ body { + font-family: monospace; + font-size: 18px; background: #111; + color: #666; margin: 0; } @@ -8,10 +11,45 @@ img { width: 100%; } -.scene { - max-width: 500px; +input[type=checkbox] { + display: none; +} + +label { + display: inline-block; + margin-right: 1em; + padding: 0.4em 0; + cursor: pointer; +} + +input[type=checkbox] + label:before { + display: inline-block; + position: relative; + background: #444; + margin-right: 8px; + content: ""; + height: 16px; + width: 16px; + top: 1px; +} + +input[type=checkbox]:checked + label:before { + background: #00FFAA; +} + +.container { + max-width: 600px; margin: 0 auto; + padding: 5%; +} + +.scene { padding: 0; + margin: 0; +} + +.border { + border: 2px dashed #00FFAA; } .layer:nth-child(1) { diff --git a/package.json b/package.json index 1a362f3..16e8f47 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.0.0", + "version": "1.1.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 cd3fa6f..e286db6 100644 --- a/source/jquery.parallax.js +++ b/source/jquery.parallax.js @@ -14,6 +14,8 @@ var NAME = 'parallax'; var MAGIC_NUMBER = 30; var DEFAULTS = { + relativeInput: false, + clipRelativeInput: false, calibrationThreshold: 100, calibrationDelay: 500, supportDelay: 500, @@ -67,11 +69,14 @@ this.depths = []; this.raf = null; - // Offset - this.ox = 0; - this.oy = 0; - this.ow = 0; - this.oh = 0; + // Element + this.bounds = null; + this.ex = 0; + this.ey = 0; + this.ew = 0; + this.eh = 0; + this.ecx = 0; + this.ecy = 0; // Calibration this.cx = 0; @@ -140,8 +145,8 @@ Plugin.prototype.ww = null; Plugin.prototype.wh = null; - Plugin.prototype.hw = null; - Plugin.prototype.hh = null; + Plugin.prototype.wcx = null; + Plugin.prototype.wcy = null; Plugin.prototype.portrait = null; Plugin.prototype.desktop = !navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|BB10|mobi|tablet|opera mini|nexus 7)/i); Plugin.prototype.vendors = [null,['-webkit-','webkit'],['-moz-','Moz'],['-o-','O'],['-ms-','ms']]; @@ -187,18 +192,10 @@ }; Plugin.prototype.updateDimensions = function() { - - // Cache Context Dimensions - this.ox = this.$context.offset().left; - this.oy = this.$context.offset().top; - this.ow = this.$context.width(); - this.oh = this.$context.height(); - - // Cache Window Dimensions this.ww = window.innerWidth; this.wh = window.innerHeight; - this.hw = this.ww / 2; - this.hh = this.wh / 2; + this.wcw = this.ww / 2; + this.wcy = this.wh / 2; }; Plugin.prototype.queueCalibration = function(delay) { @@ -386,9 +383,34 @@ Plugin.prototype.onMouseMove = function(event) { - // Calculate Input - this.ix = (event.pageX - this.hw) / this.hw; - this.iy = (event.pageY - this.hh) / this.hh; + // Calculate Mouse Input + 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; + + // Clip input to the element bounds. + if (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); + } + + } else { + + // Calculate input relative to the window. + this.ix = (event.clientX - this.wcx) / this.wcx; + this.iy = (event.clientY - this.wcy) / this.wcy; + } }; var API = { diff --git a/source/parallax.js b/source/parallax.js index 4427fd5..dbb6cbb 100644 --- a/source/parallax.js +++ b/source/parallax.js @@ -14,6 +14,8 @@ var NAME = 'Parallax'; var MAGIC_NUMBER = 30; var DEFAULTS = { + relativeInput: false, + clipRelativeInput: false, calibrationThreshold: 100, calibrationDelay: 500, supportDelay: 500, @@ -64,11 +66,14 @@ this.depths = []; this.raf = null; - // Offset - this.ox = 0; - this.oy = 0; - this.ow = 0; - this.oh = 0; + // Element + this.bounds = null; + this.ex = 0; + this.ey = 0; + this.ew = 0; + this.eh = 0; + this.ecx = 0; + this.ecy = 0; // Calibration this.cx = 0; @@ -190,8 +195,8 @@ Parallax.prototype.ww = null; Parallax.prototype.wh = null; - Parallax.prototype.hw = null; - Parallax.prototype.hh = null; + Parallax.prototype.wcx = null; + Parallax.prototype.wcy = null; Parallax.prototype.portrait = null; Parallax.prototype.desktop = !navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|BB10|mobi|tablet|opera mini|nexus 7)/i); Parallax.prototype.vendors = [null,['-webkit-','webkit'],['-moz-','Moz'],['-o-','O'],['-ms-','ms']]; @@ -232,18 +237,10 @@ }; Parallax.prototype.updateDimensions = function() { - - // Cache Context Dimensions - this.ox = this.offset(this.element).left; - this.oy = this.offset(this.element).top; - this.ow = this.element.offsetWidth; - this.oh = this.element.offsetHeight; - - // Cache Window Dimensions this.ww = window.innerWidth; this.wh = window.innerHeight; - this.hw = this.ww / 2; - this.hh = this.wh / 2; + this.wcx = this.ww / 2; + this.wcy = this.wh / 2; }; Parallax.prototype.queueCalibration = function(delay) { @@ -428,9 +425,34 @@ Parallax.prototype.onMouseMove = function(event) { - // Calculate Input - this.ix = (event.pageX - this.hw) / this.hw; - this.iy = (event.pageY - this.hh) / this.hh; + // Calculate Mouse Input + 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; + + // Clip input to the element bounds. + if (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); + } + + } else { + + // Calculate input relative to the window. + this.ix = (event.clientX - this.wcx) / this.wcx; + this.iy = (event.clientY - this.wcy) / this.wcy; + } }; // Expose Parallax