Skip to content

Commit 81f33cb

Browse files
committed
🇬🇧 Refactored motion calculations to use px and added interactive.html.
1 parent 05d85d4 commit 81f33cb

11 files changed

+332
-158
lines changed

README.md

+40-40
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
# Parallax.js
22

3-
Simple, lightweight **Parallax Engine** that reacts to the orientation of a
4-
smart device. Where no gyroscope or motion detection hardware is available, the
5-
position of the cursor is used instead.
3+
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.
64

75
Check out this **[demo][demo]** to see it in action!
86

97
## Setup
108

11-
Simply create a list of elements giving each item that you want to move within
12-
your parallax scene a class of `layer` and a `data-depth` attribute specifying
13-
its depth within the scene. A depth of **0** will cause the layer to remain
14-
stationary, and a depth of **1** will cause the layer to move by the total
15-
effect of the calculated motion. Values inbetween **0** and **1** will cause the
16-
layer to move by an amount relative to the supplied ratio.
9+
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.
1710

1811
```html
1912
<ul id="scene">
@@ -26,19 +19,38 @@ layer to move by an amount relative to the supplied ratio.
2619
</ul>
2720
```
2821

29-
To kickoff a **Parallax** scene, simply select your parent DOM Element and pass
30-
it to the **Parallax** constructor.
22+
To kickoff a **Parallax** scene, select your parent DOM Element and pass it to the **Parallax** constructor.
3123

3224
```javascript
3325
var scene = document.getElementById('scene');
3426
var parallax = new Parallax(scene);
3527
```
3628

29+
## Understanding Layer Motion Calculations
30+
31+
The amount of motion that each layer moves by depends on 3 contributing factors:
32+
33+
1. The `scalarX` and `scalarY` values (see [Behaviours](#behaviours) below for configuration)
34+
2. The dimensions of the parent DOM element
35+
3. The `depth` of a layer within a parallax scene (specified by it's `data-depth` attribute)
36+
37+
The calculation for this motion is as follows:
38+
39+
```coffeescript
40+
xMotion = parentElement.width / scalarX * layerDepth
41+
yMotion = parentElement.height / scalarY * layerDepth
42+
```
43+
44+
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:
45+
46+
```coffeescript
47+
xMotion = 1000 / 10 * 0.5 = 50 # 50px of positive and negative motion in x
48+
yMotion = 1000 / 10 * 0.5 = 50 # 50px of positive and negative motion in y
49+
```
50+
3751
## Behaviours
3852

39-
There are a number of behaviours that you can setup for any given **Parallax**
40-
instance. These behaviours can either be specified in the markup via data
41-
attributes or in JavaScript via the constructor and API.
53+
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.
4254

4355
| Behaviour | Values | Description |
4456
| ------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -55,8 +67,7 @@ attributes or in JavaScript via the constructor and API.
5567
| `friction-x` | `number` `0 - 1` | The amount of friction the layers experience. This essentially adds some easing to the layer motion. |
5668
| `friction-y` | `number` `0 - 1` | The amount of friction the layers experience. This essentially adds some easing to the layer motion. |
5769

58-
In addition to the behaviours described above, there are **two** methods `enable()`
59-
and `disable()` that *activate* and *deactivate* the **Parallax** instance respectively.
70+
In addition to the behaviours described above, there are **two** methods `enable()` and `disable()` that *activate* and *deactivate* the **Parallax** instance respectively.
6071

6172
### Behaviours: Data Attributes Example
6273

@@ -153,35 +164,24 @@ $scene.parallax('friction', 0.2, 0.8);
153164

154165
## iOS
155166

156-
If you are writing a **native iOS application** and would like to use **parallax.js**
157-
within a `UIWebView`, you will need to do a little bit of work to get it running.
167+
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.
158168

159-
`UIWebView` no longer automatically receives the `deviceorientation` event, so
160-
your native application must intercept the events from the gyroscope and reroute
161-
them to the `UIWebView`:
169+
`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`:
162170

163-
1. Include the **CoreMotion** framework `#import <CoreMotion/CoreMotion.h>`
164-
and create a reference to the **UIWebView** `@property(nonatomic, strong) IBOutlet UIWebView *parallaxWebView;`
165-
2. Add a property to the app delegate (or controller that will own the **UIWebView**)
166-
`@property(nonatomic, strong) CMMotionManager *motionManager;`
171+
1. Include the **CoreMotion** framework `#import <CoreMotion/CoreMotion.h>` and create a reference to the **UIWebView** `@property(nonatomic, strong) IBOutlet UIWebView *parallaxWebView;`
172+
2. Add a property to the app delegate (or controller that will own the **UIWebView**) `@property(nonatomic, strong) CMMotionManager *motionManager;`
167173
3. Finally, make the following calls:
168174

169175
```Objective-C
170-
self.motionManager = [[CMMotionManager alloc] init];
171-
172-
if (self.motionManager.isGyroAvailable && !self.motionManager.isGyroActive) {
173-
174-
[self.motionManager setGyroUpdateInterval:0.5f]; // Set the event update frequency (in seconds)
175-
176-
[self.motionManager startGyroUpdatesToQueue:NSOperationQueue.mainQueue
177-
withHandler:^(CMGyroData *gyroData, NSError *error) {
178-
179-
NSString *js = [NSString stringWithFormat:@"parallax.onDeviceOrientation({beta:%f, gamma:%f})", gyroData.rotationRate.x, gyroData.rotationRate.y];
180-
181-
[self.parallaxWebView stringByEvaluatingJavaScriptFromString:js];
182-
183-
}];
184-
}
176+
self.motionManager = [[CMMotionManager alloc] init];
177+
if (self.motionManager.isGyroAvailable && !self.motionManager.isGyroActive) {
178+
[self.motionManager setGyroUpdateInterval:0.5f]; // Set the event update frequency (in seconds)
179+
[self.motionManager startGyroUpdatesToQueue:NSOperationQueue.mainQueue
180+
withHandler:^(CMGyroData *gyroData, NSError *error) {
181+
NSString *js = [NSString stringWithFormat:@"parallax.onDeviceOrientation({beta:%f, gamma:%f})", gyroData.rotationRate.x, gyroData.rotationRate.y];
182+
[self.parallaxWebView stringByEvaluatingJavaScriptFromString:js];
183+
}];
184+
}
185185
```
186186

187187
## Build

bower.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "parallax",
33
"description": "Parallax Engine that reacts to the orientation of a smart device.",
4-
"version": "1.1.1",
4+
"version": "2.0.0",
55
"license": "MIT",
66
"homepage": "http://wagerfield.github.io/parallax/",
77
"authors": [

deploy/jquery.parallax.js

+50-30
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@
5959
scalarX: 10.0,
6060
scalarY: 10.0,
6161
frictionX: 0.1,
62-
frictionY: 0.1
62+
frictionY: 0.1,
63+
originX: 0.5,
64+
originY: 0.5
6365
};
6466

6567
function Plugin(element, options) {
@@ -163,11 +165,21 @@
163165
break;
164166
case '3D':
165167
if (propertySupport) {
166-
document.body.appendChild(element);
168+
var body = document.body || document.createElement('body');
169+
var documentElement = document.documentElement;
170+
var documentOverflow = documentElement.style.overflow;
171+
if (!document.body) {
172+
documentElement.style.overflow = 'hidden';
173+
documentElement.appendChild(body);
174+
body.style.overflow = 'hidden';
175+
body.style.background = '';
176+
}
177+
body.appendChild(element);
167178
element.style[jsProperty] = 'translate3d(1px,1px,1px)';
168179
propertyValue = window.getComputedStyle(element).getPropertyValue(cssProperty);
169180
featureSupport = propertyValue !== undefined && propertyValue.length > 0 && propertyValue !== "none";
170-
document.body.removeChild(element);
181+
documentElement.style.overflow = documentOverflow;
182+
body.removeChild(element);
171183
}
172184
break;
173185
}
@@ -186,6 +198,7 @@
186198
Plugin.prototype.orientationStatus = 0;
187199
Plugin.prototype.transform2DSupport = Plugin.prototype.transformSupport('2D');
188200
Plugin.prototype.transform3DSupport = Plugin.prototype.transformSupport('3D');
201+
Plugin.prototype.propertyCache = {};
189202

190203
Plugin.prototype.initialise = function() {
191204

@@ -198,8 +211,6 @@
198211
this.$layers.css({
199212
position:'absolute',
200213
display:'block',
201-
height:'100%',
202-
width:'100%',
203214
left: 0,
204215
top: 0
205216
});
@@ -225,8 +236,18 @@
225236
Plugin.prototype.updateDimensions = function() {
226237
this.ww = window.innerWidth;
227238
this.wh = window.innerHeight;
228-
this.wcx = this.ww / 2;
229-
this.wcy = this.wh / 2;
239+
this.wcx = this.ww * this.originX;
240+
this.wcy = this.wh * this.originY;
241+
};
242+
243+
Plugin.prototype.updateBounds = function() {
244+
this.bounds = this.element.getBoundingClientRect();
245+
this.ex = this.bounds.left;
246+
this.ey = this.bounds.top;
247+
this.ew = this.bounds.width;
248+
this.eh = this.bounds.height;
249+
this.ecx = this.ew * this.originX;
250+
this.ecy = this.eh * this.originY;
230251
};
231252

232253
Plugin.prototype.queueCalibration = function(delay) {
@@ -297,18 +318,21 @@
297318
};
298319

299320
Plugin.prototype.css = function(element, property, value) {
300-
var jsProperty = null;
301-
for (var i = 0, l = this.vendors.length; i < l; i++) {
302-
if (this.vendors[i] !== null) {
303-
jsProperty = $.camelCase(this.vendors[i][1] + '-' + property);
304-
} else {
305-
jsProperty = property;
306-
}
307-
if (element.style[jsProperty] !== undefined) {
308-
element.style[jsProperty] = value;
309-
break;
321+
var jsProperty = this.propertyCache[property];
322+
if (!jsProperty) {
323+
for (var i = 0, l = this.vendors.length; i < l; i++) {
324+
if (this.vendors[i] !== null) {
325+
jsProperty = $.camelCase(this.vendors[i][1] + '-' + property);
326+
} else {
327+
jsProperty = property;
328+
}
329+
if (element.style[jsProperty] !== undefined) {
330+
this.propertyCache[property] = jsProperty;
331+
break;
332+
}
310333
}
311334
}
335+
element.style[jsProperty] = value;
312336
};
313337

314338
Plugin.prototype.accelerate = function($element) {
@@ -321,8 +345,8 @@
321345
};
322346

323347
Plugin.prototype.setPosition = function(element, x, y) {
324-
x += '%';
325-
y += '%';
348+
x += 'px';
349+
y += 'px';
326350
if (this.transform3DSupport) {
327351
this.css(element, 'transform', 'translate3d('+x+','+y+',0)');
328352
} else if (this.transform2DSupport) {
@@ -350,18 +374,21 @@
350374
};
351375

352376
Plugin.prototype.onAnimationFrame = function() {
377+
this.updateBounds();
353378
var dx = this.ix - this.cx;
354379
var dy = this.iy - this.cy;
355380
if ((Math.abs(dx) > this.calibrationThreshold) || (Math.abs(dy) > this.calibrationThreshold)) {
356381
this.queueCalibration(0);
357382
}
358383
if (this.portrait) {
359-
this.mx = (this.calibrateX ? dy : this.iy) * this.scalarX;
360-
this.my = (this.calibrateY ? dx : this.ix) * this.scalarY;
384+
this.mx = this.calibrateX ? dy : this.iy;
385+
this.my = this.calibrateY ? dx : this.ix;
361386
} else {
362-
this.mx = (this.calibrateX ? dx : this.ix) * this.scalarX;
363-
this.my = (this.calibrateY ? dy : this.iy) * this.scalarY;
387+
this.mx = this.calibrateX ? dx : this.ix;
388+
this.my = this.calibrateY ? dy : this.iy;
364389
}
390+
this.mx *= this.ew / this.scalarX;
391+
this.my *= this.eh / this.scalarY;
365392
if (!isNaN(parseFloat(this.limitX))) {
366393
this.mx = this.clamp(this.mx, -this.limitX, this.limitX);
367394
}
@@ -418,13 +445,6 @@
418445
if (!this.orientationSupport && this.relativeInput) {
419446

420447
// Calculate input relative to the element.
421-
this.bounds = this.element.getBoundingClientRect();
422-
this.ex = this.bounds.left;
423-
this.ey = this.bounds.top;
424-
this.ew = this.bounds.width;
425-
this.eh = this.bounds.height;
426-
this.ecx = this.ew / 2;
427-
this.ecy = this.eh / 2;
428448
this.ix = (event.clientX - this.ex - this.ecx) / this.ecx;
429449
this.iy = (event.clientY - this.ey - this.ecy) / this.ecy;
430450

0 commit comments

Comments
 (0)