parallax-rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZTdhNTlmMmY5NDgwMzRmZGVlZmU1YTk1NTIyMzhjNGQ3YjgzZDc1Mg==
5
+ data.tar.gz: !binary |-
6
+ OWFlNTE3MDNmMGRlNWQ1ODUyZDkwMDVlOTZhNzhhOGUwMWI0MTY0OQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MTFiZDFiMDhjMWIwZTc4ODJiY2VmNWU4ZDg0MDNhNGM2NWVmZWQwNWZkYjQy
10
+ OGNhNzVmODg2OTNkM2VkZjE4MmI3MGM0NTZiNzVjY2NlOWZmMGE4MjFkM2Iz
11
+ ZmNjODM1ZjI0ZWVlOWE5ZGQzMDk4MTA0YmQ5NjI4OGNlMTZjM2Y=
12
+ data.tar.gz: !binary |-
13
+ ZjYxYWVlNTlmYjdiNmM1MjMxMGZiN2ExZTcwZjlmODhhNzdiZGMxZjdjZjk0
14
+ ODBhZmRiZjExODljNGMwNDA4MjFlYTI1ODgzNTQwZTQ1MDZmYjFkZWYxYWM5
15
+ MmJlOGIzMTZlZDEwNTM4ZjZjZDMyNmRjNmYxNmQ3N2JhM2Q2MGU=
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in parallax-rails.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 producao02
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ # parallax-rails
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,498 @@
1
+ /**
2
+ * jQuery || Zepto Parallax Plugin
3
+ * @author Matthew Wagerfield - @wagerfield
4
+ * @description Creates a parallax effect between an array of layers,
5
+ * driving the motion from the gyroscope output of a smartdevice.
6
+ * If no gyroscope is available, the cursor position is used.
7
+ */
8
+ ;(function($, window, document, undefined) {
9
+
10
+ // Strict Mode
11
+ 'use strict';
12
+
13
+ // Constants
14
+ var NAME = 'parallax';
15
+ var MAGIC_NUMBER = 30;
16
+ var DEFAULTS = {
17
+ relativeInput: false,
18
+ clipRelativeInput: false,
19
+ calibrationThreshold: 100,
20
+ calibrationDelay: 500,
21
+ supportDelay: 500,
22
+ calibrateX: false,
23
+ calibrateY: true,
24
+ invertX: true,
25
+ invertY: true,
26
+ limitX: false,
27
+ limitY: false,
28
+ scalarX: 10.0,
29
+ scalarY: 10.0,
30
+ frictionX: 0.1,
31
+ frictionY: 0.1,
32
+ originX: 0.5,
33
+ originY: 0.5
34
+ };
35
+
36
+ function Plugin(element, options) {
37
+
38
+ // DOM Context
39
+ this.element = element;
40
+
41
+ // Selections
42
+ this.$context = $(element).data('api', this);
43
+ this.$layers = this.$context.find('.layer');
44
+
45
+ // Data Extraction
46
+ var data = {
47
+ calibrateX: this.$context.data('calibrate-x') || null,
48
+ calibrateY: this.$context.data('calibrate-y') || null,
49
+ invertX: this.$context.data('invert-x') || null,
50
+ invertY: this.$context.data('invert-y') || null,
51
+ limitX: parseFloat(this.$context.data('limit-x')) || null,
52
+ limitY: parseFloat(this.$context.data('limit-y')) || null,
53
+ scalarX: parseFloat(this.$context.data('scalar-x')) || null,
54
+ scalarY: parseFloat(this.$context.data('scalar-y')) || null,
55
+ frictionX: parseFloat(this.$context.data('friction-x')) || null,
56
+ frictionY: parseFloat(this.$context.data('friction-y')) || null,
57
+ originX: parseFloat(this.$context.data('origin-x')) || null,
58
+ originY: parseFloat(this.$context.data('origin-y')) || null
59
+ };
60
+
61
+ // Delete Null Data Values
62
+ for (var key in data) {
63
+ if (data[key] === null) delete data[key];
64
+ }
65
+
66
+ // Compose Settings Object
67
+ $.extend(this, DEFAULTS, options, data);
68
+
69
+ // States
70
+ this.calibrationTimer = null;
71
+ this.calibrationFlag = true;
72
+ this.enabled = false;
73
+ this.depths = [];
74
+ this.raf = null;
75
+
76
+ // Element Bounds
77
+ this.bounds = null;
78
+ this.ex = 0;
79
+ this.ey = 0;
80
+ this.ew = 0;
81
+ this.eh = 0;
82
+
83
+ // Element Center
84
+ this.ecx = 0;
85
+ this.ecy = 0;
86
+
87
+ // Element Range
88
+ this.erx = 0;
89
+ this.ery = 0;
90
+
91
+ // Calibration
92
+ this.cx = 0;
93
+ this.cy = 0;
94
+
95
+ // Input
96
+ this.ix = 0;
97
+ this.iy = 0;
98
+
99
+ // Motion
100
+ this.mx = 0;
101
+ this.my = 0;
102
+
103
+ // Velocity
104
+ this.vx = 0;
105
+ this.vy = 0;
106
+
107
+ // Callbacks
108
+ this.onMouseMove = this.onMouseMove.bind(this);
109
+ this.onDeviceOrientation = this.onDeviceOrientation.bind(this);
110
+ this.onOrientationTimer = this.onOrientationTimer.bind(this);
111
+ this.onCalibrationTimer = this.onCalibrationTimer.bind(this);
112
+ this.onAnimationFrame = this.onAnimationFrame.bind(this);
113
+ this.onWindowResize = this.onWindowResize.bind(this);
114
+
115
+ // Initialise
116
+ this.initialise();
117
+ }
118
+
119
+ Plugin.prototype.transformSupport = function(value) {
120
+ var element = document.createElement('div');
121
+ var propertySupport = false;
122
+ var propertyValue = null;
123
+ var featureSupport = false;
124
+ var cssProperty = null;
125
+ var jsProperty = null;
126
+ for (var i = 0, l = this.vendors.length; i < l; i++) {
127
+ if (this.vendors[i] !== null) {
128
+ cssProperty = this.vendors[i][0] + 'transform';
129
+ jsProperty = this.vendors[i][1] + 'Transform';
130
+ } else {
131
+ cssProperty = 'transform';
132
+ jsProperty = 'transform';
133
+ }
134
+ if (element.style[jsProperty] !== undefined) {
135
+ propertySupport = true;
136
+ break;
137
+ }
138
+ }
139
+ switch(value) {
140
+ case '2D':
141
+ featureSupport = propertySupport;
142
+ break;
143
+ case '3D':
144
+ if (propertySupport) {
145
+ var body = document.body || document.createElement('body');
146
+ var documentElement = document.documentElement;
147
+ var documentOverflow = documentElement.style.overflow;
148
+ if (!document.body) {
149
+ documentElement.style.overflow = 'hidden';
150
+ documentElement.appendChild(body);
151
+ body.style.overflow = 'hidden';
152
+ body.style.background = '';
153
+ }
154
+ body.appendChild(element);
155
+ element.style[jsProperty] = 'translate3d(1px,1px,1px)';
156
+ propertyValue = window.getComputedStyle(element).getPropertyValue(cssProperty);
157
+ featureSupport = propertyValue !== undefined && propertyValue.length > 0 && propertyValue !== "none";
158
+ documentElement.style.overflow = documentOverflow;
159
+ body.removeChild(element);
160
+ }
161
+ break;
162
+ }
163
+ return featureSupport;
164
+ };
165
+
166
+ Plugin.prototype.ww = null;
167
+ Plugin.prototype.wh = null;
168
+ Plugin.prototype.wcx = null;
169
+ Plugin.prototype.wcy = null;
170
+ Plugin.prototype.wrx = null;
171
+ Plugin.prototype.wry = null;
172
+ Plugin.prototype.portrait = null;
173
+ Plugin.prototype.desktop = !navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|BB10|mobi|tablet|opera mini|nexus 7)/i);
174
+ Plugin.prototype.vendors = [null,['-webkit-','webkit'],['-moz-','Moz'],['-o-','O'],['-ms-','ms']];
175
+ Plugin.prototype.motionSupport = !!window.DeviceMotionEvent;
176
+ Plugin.prototype.orientationSupport = !!window.DeviceOrientationEvent;
177
+ Plugin.prototype.orientationStatus = 0;
178
+ Plugin.prototype.transform2DSupport = Plugin.prototype.transformSupport('2D');
179
+ Plugin.prototype.transform3DSupport = Plugin.prototype.transformSupport('3D');
180
+ Plugin.prototype.propertyCache = {};
181
+
182
+ Plugin.prototype.initialise = function() {
183
+
184
+ // Configure Styles
185
+ if (this.$context.css('position') === 'static') {
186
+ this.$context.css({
187
+ position:'relative'
188
+ });
189
+ }
190
+
191
+ // Hardware Accelerate Context
192
+ this.accelerate(this.$context);
193
+
194
+ // Setup
195
+ this.updateLayers();
196
+ this.updateDimensions();
197
+ this.enable();
198
+ this.queueCalibration(this.calibrationDelay);
199
+ };
200
+
201
+ Plugin.prototype.updateLayers = function() {
202
+
203
+ // Cache Layer Elements
204
+ this.$layers = this.$context.find('.layer');
205
+ this.depths = [];
206
+
207
+ // Configure Layer Styles
208
+ this.$layers.css({
209
+ position:'absolute',
210
+ display:'block',
211
+ left: 0,
212
+ top: 0
213
+ });
214
+ this.$layers.first().css({
215
+ position:'relative'
216
+ });
217
+
218
+ // Hardware Accelerate Layers
219
+ this.accelerate(this.$layers);
220
+
221
+ // Cache Depths
222
+ this.$layers.each($.proxy(function(index, element) {
223
+ this.depths.push($(element).data('depth') || 0);
224
+ }, this));
225
+ };
226
+
227
+ Plugin.prototype.updateDimensions = function() {
228
+ this.ww = window.innerWidth;
229
+ this.wh = window.innerHeight;
230
+ this.wcx = this.ww * this.originX;
231
+ this.wcy = this.wh * this.originY;
232
+ this.wrx = Math.max(this.wcx, this.ww - this.wcx);
233
+ this.wry = Math.max(this.wcy, this.wh - this.wcy);
234
+ };
235
+
236
+ Plugin.prototype.updateBounds = function() {
237
+ this.bounds = this.element.getBoundingClientRect();
238
+ this.ex = this.bounds.left;
239
+ this.ey = this.bounds.top;
240
+ this.ew = this.bounds.width;
241
+ this.eh = this.bounds.height;
242
+ this.ecx = this.ew * this.originX;
243
+ this.ecy = this.eh * this.originY;
244
+ this.erx = Math.max(this.ecx, this.ew - this.ecx);
245
+ this.ery = Math.max(this.ecy, this.eh - this.ecy);
246
+ };
247
+
248
+ Plugin.prototype.queueCalibration = function(delay) {
249
+ clearTimeout(this.calibrationTimer);
250
+ this.calibrationTimer = setTimeout(this.onCalibrationTimer, delay);
251
+ };
252
+
253
+ Plugin.prototype.enable = function() {
254
+ if (!this.enabled) {
255
+ this.enabled = true;
256
+ if (this.orientationSupport) {
257
+ this.portrait = null;
258
+ window.addEventListener('deviceorientation', this.onDeviceOrientation);
259
+ setTimeout(this.onOrientationTimer, this.supportDelay);
260
+ } else {
261
+ this.cx = 0;
262
+ this.cy = 0;
263
+ this.portrait = false;
264
+ window.addEventListener('mousemove', this.onMouseMove);
265
+ }
266
+ window.addEventListener('resize', this.onWindowResize);
267
+ this.raf = requestAnimationFrame(this.onAnimationFrame);
268
+ }
269
+ };
270
+
271
+ Plugin.prototype.disable = function() {
272
+ if (this.enabled) {
273
+ this.enabled = false;
274
+ if (this.orientationSupport) {
275
+ window.removeEventListener('deviceorientation', this.onDeviceOrientation);
276
+ } else {
277
+ window.removeEventListener('mousemove', this.onMouseMove);
278
+ }
279
+ window.removeEventListener('resize', this.onWindowResize);
280
+ cancelAnimationFrame(this.raf);
281
+ }
282
+ };
283
+
284
+ Plugin.prototype.calibrate = function(x, y) {
285
+ this.calibrateX = x === undefined ? this.calibrateX : x;
286
+ this.calibrateY = y === undefined ? this.calibrateY : y;
287
+ };
288
+
289
+ Plugin.prototype.invert = function(x, y) {
290
+ this.invertX = x === undefined ? this.invertX : x;
291
+ this.invertY = y === undefined ? this.invertY : y;
292
+ };
293
+
294
+ Plugin.prototype.friction = function(x, y) {
295
+ this.frictionX = x === undefined ? this.frictionX : x;
296
+ this.frictionY = y === undefined ? this.frictionY : y;
297
+ };
298
+
299
+ Plugin.prototype.scalar = function(x, y) {
300
+ this.scalarX = x === undefined ? this.scalarX : x;
301
+ this.scalarY = y === undefined ? this.scalarY : y;
302
+ };
303
+
304
+ Plugin.prototype.limit = function(x, y) {
305
+ this.limitX = x === undefined ? this.limitX : x;
306
+ this.limitY = y === undefined ? this.limitY : y;
307
+ };
308
+
309
+ Plugin.prototype.origin = function(x, y) {
310
+ this.originX = x === undefined ? this.originX : x;
311
+ this.originY = y === undefined ? this.originY : y;
312
+ };
313
+
314
+ Plugin.prototype.clamp = function(value, min, max) {
315
+ value = Math.max(value, min);
316
+ value = Math.min(value, max);
317
+ return value;
318
+ };
319
+
320
+ Plugin.prototype.css = function(element, property, value) {
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
+ }
333
+ }
334
+ }
335
+ element.style[jsProperty] = value;
336
+ };
337
+
338
+ Plugin.prototype.accelerate = function($element) {
339
+ for (var i = 0, l = $element.length; i < l; i++) {
340
+ var element = $element[i];
341
+ this.css(element, 'transform', 'translate3d(0,0,0)');
342
+ this.css(element, 'transform-style', 'preserve-3d');
343
+ this.css(element, 'backface-visibility', 'hidden');
344
+ }
345
+ };
346
+
347
+ Plugin.prototype.setPosition = function(element, x, y) {
348
+ x += 'px';
349
+ y += 'px';
350
+ if (this.transform3DSupport) {
351
+ this.css(element, 'transform', 'translate3d('+x+','+y+',0)');
352
+ } else if (this.transform2DSupport) {
353
+ this.css(element, 'transform', 'translate('+x+','+y+')');
354
+ } else {
355
+ element.style.left = x;
356
+ element.style.top = y;
357
+ }
358
+ };
359
+
360
+ Plugin.prototype.onOrientationTimer = function(event) {
361
+ if (this.orientationSupport && this.orientationStatus === 0) {
362
+ this.disable();
363
+ this.orientationSupport = false;
364
+ this.enable();
365
+ }
366
+ };
367
+
368
+ Plugin.prototype.onCalibrationTimer = function(event) {
369
+ this.calibrationFlag = true;
370
+ };
371
+
372
+ Plugin.prototype.onWindowResize = function(event) {
373
+ this.updateDimensions();
374
+ };
375
+
376
+ Plugin.prototype.onAnimationFrame = function() {
377
+ this.updateBounds();
378
+ var dx = this.ix - this.cx;
379
+ var dy = this.iy - this.cy;
380
+ if ((Math.abs(dx) > this.calibrationThreshold) || (Math.abs(dy) > this.calibrationThreshold)) {
381
+ this.queueCalibration(0);
382
+ }
383
+ if (this.portrait) {
384
+ this.mx = this.calibrateX ? dy : this.iy;
385
+ this.my = this.calibrateY ? dx : this.ix;
386
+ } else {
387
+ this.mx = this.calibrateX ? dx : this.ix;
388
+ this.my = this.calibrateY ? dy : this.iy;
389
+ }
390
+ this.mx *= this.ew * (this.scalarX / 100);
391
+ this.my *= this.eh * (this.scalarY / 100);
392
+ if (!isNaN(parseFloat(this.limitX))) {
393
+ this.mx = this.clamp(this.mx, -this.limitX, this.limitX);
394
+ }
395
+ if (!isNaN(parseFloat(this.limitY))) {
396
+ this.my = this.clamp(this.my, -this.limitY, this.limitY);
397
+ }
398
+ this.vx += (this.mx - this.vx) * this.frictionX;
399
+ this.vy += (this.my - this.vy) * this.frictionY;
400
+ for (var i = 0, l = this.$layers.length; i < l; i++) {
401
+ var depth = this.depths[i];
402
+ var layer = this.$layers[i];
403
+ var xOffset = this.vx * depth * (this.invertX ? -1 : 1);
404
+ var yOffset = this.vy * depth * (this.invertY ? -1 : 1);
405
+ this.setPosition(layer, xOffset, yOffset);
406
+ }
407
+ this.raf = requestAnimationFrame(this.onAnimationFrame);
408
+ };
409
+
410
+ Plugin.prototype.onDeviceOrientation = function(event) {
411
+
412
+ // Validate environment and event properties.
413
+ if (!this.desktop && event.beta !== null && event.gamma !== null) {
414
+
415
+ // Set orientation status.
416
+ this.orientationStatus = 1;
417
+
418
+ // Extract Rotation
419
+ var x = (event.beta || 0) / MAGIC_NUMBER; // -90 :: 90
420
+ var y = (event.gamma || 0) / MAGIC_NUMBER; // -180 :: 180
421
+
422
+ // Detect Orientation Change
423
+ var portrait = window.innerHeight > window.innerWidth;
424
+ if (this.portrait !== portrait) {
425
+ this.portrait = portrait;
426
+ this.calibrationFlag = true;
427
+ }
428
+
429
+ // Set Calibration
430
+ if (this.calibrationFlag) {
431
+ this.calibrationFlag = false;
432
+ this.cx = x;
433
+ this.cy = y;
434
+ }
435
+
436
+ // Set Input
437
+ this.ix = x;
438
+ this.iy = y;
439
+ }
440
+ };
441
+
442
+ Plugin.prototype.onMouseMove = function(event) {
443
+
444
+ // Cache mouse coordinates.
445
+ var clientX = event.clientX;
446
+ var clientY = event.clientY;
447
+
448
+ // Calculate Mouse Input
449
+ if (!this.orientationSupport && this.relativeInput) {
450
+
451
+ // Clip mouse coordinates inside element bounds.
452
+ if (this.clipRelativeInput) {
453
+ clientX = Math.max(clientX, this.ex);
454
+ clientX = Math.min(clientX, this.ex + this.ew);
455
+ clientY = Math.max(clientY, this.ey);
456
+ clientY = Math.min(clientY, this.ey + this.eh);
457
+ }
458
+
459
+ // Calculate input relative to the element.
460
+ this.ix = (clientX - this.ex - this.ecx) / this.erx;
461
+ this.iy = (clientY - this.ey - this.ecy) / this.ery;
462
+
463
+ } else {
464
+
465
+ // Calculate input relative to the window.
466
+ this.ix = (clientX - this.wcx) / this.wrx;
467
+ this.iy = (clientY - this.wcy) / this.wry;
468
+ }
469
+ };
470
+
471
+ var API = {
472
+ enable: Plugin.prototype.enable,
473
+ disable: Plugin.prototype.disable,
474
+ updateLayers: Plugin.prototype.updateLayers,
475
+ calibrate: Plugin.prototype.calibrate,
476
+ friction: Plugin.prototype.friction,
477
+ invert: Plugin.prototype.invert,
478
+ scalar: Plugin.prototype.scalar,
479
+ limit: Plugin.prototype.limit,
480
+ origin: Plugin.prototype.origin
481
+ };
482
+
483
+ $.fn[NAME] = function (value) {
484
+ var args = arguments;
485
+ return this.each(function () {
486
+ var $this = $(this);
487
+ var plugin = $this.data(NAME);
488
+ if (!plugin) {
489
+ plugin = new Plugin(this, value);
490
+ $this.data(NAME, plugin);
491
+ }
492
+ if (API[value]) {
493
+ plugin[value].apply(plugin, Array.prototype.slice.call(args, 1));
494
+ }
495
+ });
496
+ };
497
+
498
+ })(window.jQuery || window.Zepto, window, document);