Skip to content

Commit 72b8b15

Browse files
committed
finished pinning feature
1 parent ea63e9a commit 72b8b15

File tree

3 files changed

+104
-38
lines changed

3 files changed

+104
-38
lines changed

README.md

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,47 @@ Using the object syntax you can explicitly pass in a **from** value (optional),
4949

5050
### Available properties:
5151

52-
x
52+
#### pin
53+
**Type:** boolean or selector
5354

54-
y
55+
Make an elements' position fixed during the scene.
5556

56-
z
57+
#### x
58+
**Type:** number
5759

58-
scale
60+
translateX
5961

60-
rotate
62+
#### y
63+
**Type:** number
6164

62-
color
65+
translateY
6366

64-
backgroundColor
67+
#### z
68+
**Type:** number
69+
70+
translateZ
71+
72+
#### scale
73+
**Type:** number
74+
75+
#### rotate
76+
**Type:** number
77+
78+
Rotation in degrees.
79+
80+
#### color
81+
**Type:** string (e.g. "#ff0000")
82+
83+
Hex or rgb() color string.
84+
85+
#### backgroundColor
86+
**Type:** string (e.g. "#ff0000")
87+
88+
Hex or rgb() color string.
89+
90+
#### opacity
91+
**Type:** number (0 - 1)
6592

66-
opacity
6793

6894
### Options
6995

@@ -89,8 +115,8 @@ as well as (overridden) for each individually:
89115
**Type:** number or string (percentage or viewport units) or callback function
90116
**Default:** element top + height - start
91117

92-
Percentage is calculated against element dimensions rather than viewport: "50%" == 0.5 * $(el).width().
93-
For viewport relative values, use viewport units: vh or vw.
118+
Percentage is calculated against element dimensions rather than viewport: "50%" == 0.5 * $(el).outerWidth(true).
119+
For viewport relative values, use viewport units: vh or vw: "100vh" == $(window).height().
94120

95121
#### trigger
96122
**Type:** number or string (percentage)

jquery.data-parallax.js

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363
var options = $.extend(dataOptions[i] || {}, jsOptions[i] || {});
6464
typeof options.start == "undefined" || (options.start = convertToElement(options.start));
6565
typeof options.start != "undefined" || (options.start = this[0]);
66-
typeof options.trigger != "undefined" || (options.trigger = "100%");
6766
optionsArr.push(options);
6867
}
6968
return optionsArr;
@@ -181,25 +180,34 @@
181180
return $.extend({}, globalOptions, options);
182181
}
183182

184-
function getOffset(value, axis) {
185-
if (isElement(value)) {
186-
var offset = 0;
187-
do {
188-
offset += value[axis === 'x' ? 'offsetLeft' : 'offsetTop'];
189-
} while (value = value.offsetParent);
190-
return offset;
183+
function getOffset(elem) {
184+
var offsetLeft = elem.offsetLeft,
185+
offsetTop = elem.offsetTop,
186+
lastElem = elem;
187+
188+
while (elem = elem.offsetParent) {
189+
if (elem === document.body) { //from my observation, document.body always has scrollLeft/scrollTop == 0
190+
break;
191+
}
192+
offsetLeft += elem.offsetLeft;
193+
offsetTop += elem.offsetTop;
194+
lastElem = elem;
191195
}
192-
return value;
196+
if (lastElem && lastElem.style.position === 'fixed') { //slow - http://jsperf.com/offset-vs-getboundingclientrect/6
197+
offsetLeft += window.pageXOffset || document.documentElement.scrollLeft;
198+
offsetTop += window.pageYOffset || document.documentElement.scrollTop;
199+
}
200+
return {
201+
left: offsetLeft,
202+
top: offsetTop
203+
};
193204
}
194205

195-
function isElement(obj) {
196-
try {
197-
return obj instanceof HTMLElement;
198-
}
199-
catch(e) {
200-
return (typeof obj === "object") && (obj.nodeType === 1) &&
201-
(typeof obj.style === "object") && (typeof obj.ownerDocument ==="object");
206+
function convertToOffset(value, axis) {
207+
if (isElement(value)) {
208+
return getOffset(value)[axis === Scene.AXIS_X ? 'left' : 'top'];
202209
}
210+
return value;
203211
}
204212

205213
function convertToElement(value) {
@@ -233,6 +241,16 @@
233241
}
234242
return value;
235243
}
244+
245+
function isElement(obj) {
246+
try {
247+
return obj instanceof HTMLElement;
248+
}
249+
catch(e) {
250+
return (typeof obj === "object") && (obj.nodeType === 1) &&
251+
(typeof obj.style === "object") && (typeof obj.ownerDocument ==="object");
252+
}
253+
}
236254

237255
function interpolate(from, to, progress) {
238256
return (to - from) * progress + from;
@@ -243,8 +261,11 @@
243261
this.axis = options.axis;
244262
this.from = options.from;
245263
this.to = options.to;
246-
this.trigger = convertOption(options.trigger, options.axis === 'x' ? windowWidth : windowHeight);
264+
265+
typeof options.trigger != "undefined" || (options.trigger = "100%");
266+
this.trigger = convertOption(options.trigger, options.axis === Scene.AXIS_X ? windowWidth : windowHeight);
247267
this.start = convertToElement(options.start) || options.start;
268+
248269
if (typeof options.ease == "function") {
249270
this.ease = options.ease;
250271
}
@@ -254,7 +275,7 @@
254275
}
255276

256277
if (typeof options.duration != "undefined") {
257-
var maxDuration = options.axis === 'x' ? $el.outerWidth() : $el.outerHeight(),
278+
var maxDuration = options.axis === Scene.AXIS_X ? $el.outerWidth() : $el.outerHeight(),
258279
durationPx = convertOption(options.duration, maxDuration);
259280
this.duration = function() {
260281
return durationPx;
@@ -263,10 +284,12 @@
263284
else {
264285
var scene = this;
265286
this.duration = function() {
266-
return (getOffset(scene.$el[0], options.axis) + scene.$el.outerHeight()) - scene.startPx;
287+
return (convertToOffset(scene.$el[0], options.axis) + scene.$el.outerHeight()) - scene.startPx;
267288
};
268289
}
269290
}
291+
Scene.AXIS_X = 'x';
292+
Scene.AXIS_Y = 'y';
270293
Scene.STATE_BEFORE = 'before';
271294
Scene.STATE_DURING = 'during';
272295
Scene.STATE_AFTER = 'after';
@@ -280,11 +303,14 @@
280303
_needsUpdate: function() {
281304
return this.prevState === Scene.STATE_DURING ||
282305
this.state === Scene.STATE_DURING ||
283-
(typeof this.prevState === "undefined" &&
284-
(this.state === Scene.STATE_AFTER || typeof this.from != "undefined"));
306+
this.__needsInit();
307+
},
308+
__needsInit: function() {
309+
return typeof this.prevState === "undefined" &&
310+
(this.state === Scene.STATE_AFTER || typeof this.from != "undefined");
285311
},
286312
updateStart: function() {
287-
this.startPx = Math.max(getOffset(this.start, this.axis) - this.trigger, 0);
313+
this.startPx = Math.max(convertToOffset(this.start, this.axis) - this.trigger, 0);
288314
},
289315
updateDuration: function() {
290316
this.durationPx = this.duration.call(this);
@@ -368,11 +394,13 @@
368394

369395
function PinScene($el, options) {
370396
options.to = convertToElement(options.to) || $el[0];
397+
typeof options.trigger != "undefined" || (options.trigger = 0);
371398
Scene.call(this, $el, options);
372399
}
373400
PinScene.prototype = $.extend(Object.create(Scene.prototype), {
374401
_needsUpdate: function() {
375-
return this.prevState != this.state;
402+
return (typeof this.prevState != "undefined" || this.state == Scene.STATE_DURING) &&
403+
this.prevState != this.state;
376404
},
377405
_getOldValue: function(style) {
378406
var toStyle = getComputedStyle(this.to);
@@ -384,12 +412,10 @@
384412
},
385413
_getNewValue: function() {
386414
if (this.state == Scene.STATE_DURING) {
387-
var top = parseFloat(this.from.top),
388-
left = parseFloat(this.from.left);
389415
return {
390416
position: 'fixed',
391-
top: isNaN(top) ? 0 : top,
392-
left: isNaN(left) ? 0 : left
417+
top: this.from.pinTop + 'px',
418+
left: this.from.pinLeft + 'px'
393419
};
394420
}
395421
return this.from;
@@ -398,6 +424,20 @@
398424
this.to.style.position = newValue.position;
399425
this.to.style.top = newValue.top;
400426
this.to.style.left = newValue.left;
427+
},
428+
_setFrom: function(defaultValue) {
429+
if (typeof this.from === "undefined") {
430+
var offset = getOffset(this.to);
431+
if (this.axis === Scene.AXIS_X) {
432+
defaultValue.pinTop = offset.top;
433+
defaultValue.pinLeft = offset.left - this.startPx;
434+
}
435+
else {
436+
defaultValue.pinTop = offset.top - this.startPx;
437+
defaultValue.pinLeft = offset.left;
438+
}
439+
this.from = defaultValue;
440+
}
401441
}
402442
});
403443

0 commit comments

Comments
 (0)