Skip to content

Commit

Permalink
🇬🇧 Refactored motion calculations to use px and added interactive.html.
Browse files Browse the repository at this point in the history
  • Loading branch information
wagerfield committed Apr 25, 2014
1 parent 05d85d4 commit 81f33cb
Show file tree
Hide file tree
Showing 11 changed files with 332 additions and 158 deletions.
80 changes: 40 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
<ul id="scene">
Expand All @@ -26,19 +19,38 @@ layer to move by an amount relative to the supplied ratio.
</ul>
```

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 |
| ------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
Expand All @@ -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

Expand Down Expand Up @@ -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 <CoreMotion/CoreMotion.h>`
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 <CoreMotion/CoreMotion.h>` 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
Expand Down
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -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": [
Expand Down
80 changes: 50 additions & 30 deletions deploy/jquery.parallax.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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() {

Expand All @@ -198,8 +211,6 @@
this.$layers.css({
position:'absolute',
display:'block',
height:'100%',
width:'100%',
left: 0,
top: 0
});
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;

Expand Down
Loading

0 comments on commit 81f33cb

Please sign in to comment.