|
4 | 4 | var $elements = null,
|
5 | 5 | elementsArr,
|
6 | 6 | animationsArr,
|
7 |
| - scrollTop, |
| 7 | + scroll, |
8 | 8 | windowHeight, windowWidth,
|
9 | 9 | documentWidth, documentHeight,
|
10 | 10 | scrollTicking = false,
|
|
30 | 30 | if (!isTouchDevice) {
|
31 | 31 | this.data("parallax-js", method);
|
32 | 32 | var firstCall = ($elements === null);
|
33 |
| - animationsArr = []; |
34 | 33 | if (firstCall) {
|
35 | 34 | updateDimensions();
|
36 | 35 | }
|
37 |
| - this.each(updateAnimationsArray); |
38 | 36 | if (firstCall) {
|
39 | 37 | $elements = this;
|
40 | 38 | window.onresize = onResize;
|
41 |
| - window.onscroll = onScroll |
| 39 | + window.onscroll = onScroll; |
| 40 | + elementsArr = []; |
| 41 | + animationsArr = []; |
42 | 42 | }
|
43 | 43 | else {
|
44 | 44 | $elements.add(this);
|
45 | 45 | }
|
| 46 | + updateAnimationsArray.call(this, elementsArr.length); |
46 | 47 | elementsArr = $elements.toArray();
|
47 | 48 | onScroll();
|
48 | 49 | }
|
|
78 | 79 | }
|
79 | 80 | return optionsArr;
|
80 | 81 | }
|
| 82 | + |
| 83 | + function rebuildAnimationsArray() { |
| 84 | + animationsArr = []; |
| 85 | + PinScene.scenes = []; |
| 86 | + updateAnimationsArray.call($elements); |
| 87 | + } |
| 88 | + |
| 89 | + function updateAnimationsArray(offset) { |
| 90 | + typeof offset === "number" || (offset = 0); |
| 91 | + this.each(function(i) { |
| 92 | + var idx = offset+i; |
| 93 | + animationsArr[idx] = createAnimations.call(this); |
| 94 | + }); |
| 95 | + } |
81 | 96 |
|
82 |
| - function updateAnimationsArray(idx) { |
| 97 | + function createAnimations() { |
83 | 98 | var $this = $(this),
|
84 | 99 | animations = [],
|
85 | 100 | optionsArr = parseOptions.call($this);
|
|
136 | 151 |
|
137 | 152 | if (typeof options.top != "undefined") {
|
138 | 153 | var topOptions = mergeOptions(options.top, globalOptions);
|
139 |
| - animation.top = new StyleScene($this, topOptions, 'top'); |
| 154 | + animation.top = new StyleScene($this, topOptions, 'top', $this.offsetParent().height(), "px"); |
140 | 155 | }
|
141 | 156 | if (typeof options.left != "undefined") {
|
142 | 157 | var leftOptions = mergeOptions(options.left, globalOptions);
|
143 |
| - animation.left = new StyleScene($this, leftOptions, 'left'); |
| 158 | + animation.left = new StyleScene($this, leftOptions, 'left', $this.offsetParent().width(), "px"); |
144 | 159 | }
|
145 | 160 | if (typeof options.width != "undefined") {
|
146 | 161 | var widthOptions = mergeOptions(options.width, globalOptions);
|
|
168 | 183 | }
|
169 | 184 | animations.push(animation);
|
170 | 185 | }
|
171 |
| - animationsArr[idx] = animations; |
| 186 | + return animations; |
172 | 187 | }
|
173 | 188 |
|
174 | 189 | function onResize() {
|
175 | 190 | if (!resizeTicking) {
|
176 | 191 | window.requestAnimationFrame(function() {
|
177 | 192 | updateDimensions();
|
178 |
| - $elements.each(updateAnimationsArray); |
| 193 | + rebuildAnimationsArray(); |
179 | 194 | });
|
180 | 195 | resizeTicking = true;
|
181 | 196 | }
|
|
202 | 217 | }
|
203 | 218 |
|
204 | 219 | function animateElements() {
|
205 |
| - scrollTop = window.scrollY; |
| 220 | + scroll = getScroll(); |
206 | 221 | for (var i= 0, len=elementsArr.length; i<len; i++) {
|
207 | 222 | animateElement.call(elementsArr[i], i);
|
208 | 223 | }
|
209 | 224 | scrollTicking = false;
|
210 | 225 | }
|
| 226 | + |
| 227 | + function getScroll() { |
| 228 | + return { |
| 229 | + left: window.pageXOffset || document.documentElement.scrollLeft, |
| 230 | + top: window.pageYOffset || document.documentElement.scrollTop |
| 231 | + }; |
| 232 | + } |
211 | 233 |
|
212 | 234 | function animateElement(idx) {
|
213 | 235 | var animations = animationsArr[idx],
|
|
235 | 257 | var offsetLeft = elem.offsetLeft,
|
236 | 258 | offsetTop = elem.offsetTop,
|
237 | 259 | lastElem = elem;
|
238 |
| - |
239 | 260 | while (elem = elem.offsetParent) {
|
240 | 261 | if (elem === document.body) { //from my observation, document.body always has scrollLeft/scrollTop == 0
|
241 | 262 | break;
|
|
244 | 265 | offsetTop += elem.offsetTop;
|
245 | 266 | lastElem = elem;
|
246 | 267 | }
|
247 |
| - if (lastElem && lastElem.style.position === 'fixed') { //slow - http://jsperf.com/offset-vs-getboundingclientrect/6 |
248 |
| - offsetLeft += window.pageXOffset || document.documentElement.scrollLeft; |
249 |
| - offsetTop += window.pageYOffset || document.documentElement.scrollTop; |
| 268 | + if (lastElem.style.position === 'fixed') { //slow - http://jsperf.com/offset-vs-getboundingclientrect/6 |
| 269 | + offsetLeft += scroll.left; |
| 270 | + offsetTop += scroll.top; |
250 | 271 | }
|
251 | 272 | return {
|
252 | 273 | left: offsetLeft,
|
253 | 274 | top: offsetTop
|
254 | 275 | };
|
255 | 276 | }
|
256 | 277 |
|
257 |
| - function convertToOffset(value, axis) { |
258 |
| - return getOffset(value)[axis === Scene.AXIS_X ? 'left' : 'top']; |
| 278 | + function convertToOffset(elem, axis) { |
| 279 | + return getOffset(elem)[axis === Scene.AXIS_X ? 'left' : 'top']; |
259 | 280 | }
|
260 | 281 |
|
261 | 282 | function convertToElement(value) {
|
|
364 | 385 | if (typeof duration === "undefined") {
|
365 | 386 | var scene = this;
|
366 | 387 | this.duration = function() {
|
367 |
| - var durationPx = (convertToOffset(scene.$el[0], scene.axis) + scene.$el.outerHeight()) - scene.start; |
| 388 | + var durationPx = (convertToOffset(scene.$el[0], scene.axis) + scene.$el.outerHeight(true)) - scene.start; |
368 | 389 | validateDurationPx(durationPx);
|
369 | 390 | return durationPx;
|
370 | 391 | };
|
|
413 | 434 | },
|
414 | 435 | updateState: function() {
|
415 | 436 | this.prevState = this.state;
|
416 |
| - if (scrollTop < this.start) { |
| 437 | + if (scroll.top < this.start) { |
417 | 438 | this.state = Scene.STATE_BEFORE;
|
418 | 439 | }
|
419 |
| - else if (scrollTop <= (this.start + this.durationPx)) { |
| 440 | + else if (scroll.top <= (this.start + this.durationPx)) { |
420 | 441 | this.state = Scene.STATE_DURING;
|
421 | 442 | }
|
422 | 443 | else {
|
|
426 | 447 | getOffset: function() {
|
427 | 448 | var offset = this.offset;
|
428 | 449 | if (isElement(this.triggerElement)) {
|
429 |
| - offset += convertToOffset(this.triggerElement, this.axis); |
| 450 | + var pinScene = PinScene.findByElement(this.triggerElement); |
| 451 | + offset += (pinScene && pinScene.state === Scene.STATE_DURING ? |
| 452 | + pinScene.start : |
| 453 | + convertToOffset(this.triggerElement, this.axis)); |
430 | 454 | }
|
431 | 455 | return offset;
|
432 | 456 | },
|
|
435 | 459 | return 0;
|
436 | 460 | }
|
437 | 461 | else if (this.state === Scene.STATE_DURING) {
|
438 |
| - var posPx = scrollTop - this.start, |
| 462 | + var posPx = scroll.top - this.start, |
439 | 463 | percent = posPx / this.durationPx,
|
440 | 464 | progress = this.ease.call(this, percent);
|
441 | 465 | return progress;
|
|
453 | 477 | }
|
454 | 478 | };
|
455 | 479 |
|
456 |
| - function ScalarScene($el, options, maxValue) { |
| 480 | + function ScalarScene($el, options, maxValue, defaultUnit) { |
457 | 481 | if (typeof maxValue != "undefined") {
|
458 | 482 | options.from = convertOption(options.from, maxValue);
|
459 | 483 | options.to = convertOption(options.to, maxValue);
|
460 | 484 | }
|
461 | 485 | Scene.call(this, $el, options);
|
| 486 | + this.unit = typeof this.to === "string" ? parseUnit(this.to) : defaultUnit; |
462 | 487 | }
|
463 | 488 | ScalarScene.prototype = inherit(Scene.prototype, {
|
464 | 489 | _getNewValue: function() {
|
|
468 | 493 | }
|
469 | 494 | if (typeof this.to === "string") {
|
470 | 495 | var from = parseFloat(this.from),
|
471 |
| - to = parseFloat(this.to), |
472 |
| - suffix = parseUnit(this.to); |
473 |
| - return interpolate(from, to, this.getProgress()) + suffix; |
| 496 | + to = parseFloat(this.to); |
| 497 | + return interpolate(from, to, this.getProgress()) + this.unit; |
474 | 498 | }
|
475 |
| - return interpolate(this.from, this.to, this.getProgress()); |
| 499 | + var suffix = (typeof this.unit === "undefined" ? 0 : this.unit) |
| 500 | + return interpolate(this.from, this.to, this.getProgress()) + suffix; |
476 | 501 | }
|
477 | 502 | });
|
478 | 503 |
|
479 |
| - function StyleScene($el, options, styleName, maxValue) { |
| 504 | + function StyleScene($el, options, styleName, maxValue, defaultUnit) { |
480 | 505 | this.styleName = styleName;
|
481 |
| - ScalarScene.call(this, $el, options, maxValue); |
| 506 | + ScalarScene.call(this, $el, options, maxValue, defaultUnit); |
482 | 507 | }
|
483 | 508 | StyleScene.prototype = inherit(ScalarScene.prototype, {
|
484 | 509 | _getOldValue: function(style) {
|
|
509 | 534 | isElement(options.to) || (options.to = $el[0]);
|
510 | 535 | typeof options.triggerHook != "undefined" || (options.triggerHook = 0);
|
511 | 536 | Scene.call(this, $el, options);
|
| 537 | + PinScene.scenes.push(this); |
512 | 538 | }
|
| 539 | + PinScene.scenes = []; |
| 540 | + PinScene.findByElement = function(elem) { |
| 541 | + var scenes = PinScene.scenes; |
| 542 | + for (var i=0, len=scenes.length; i<len; i++) { |
| 543 | + if (scenes[i].$el[0] === elem) { |
| 544 | + return scenes[i]; |
| 545 | + } |
| 546 | + } |
| 547 | + }; |
513 | 548 | PinScene.prototype = inherit(Scene.prototype, {
|
514 | 549 | updateStart: function() {
|
515 | 550 | if (this.state != Scene.STATE_DURING) {
|
|
525 | 560 | return {
|
526 | 561 | position: toStyle.position,
|
527 | 562 | top: toStyle.top,
|
528 |
| - left: toStyle.left |
| 563 | + left: toStyle.left, |
| 564 | + marginLeft: "", |
| 565 | + marginTop: "" |
529 | 566 | };
|
530 | 567 | },
|
531 | 568 | _getNewValue: function() {
|
532 | 569 | if (this.state == Scene.STATE_DURING) {
|
533 | 570 | return {
|
534 | 571 | position: 'fixed',
|
535 | 572 | top: this.from.pinTop + 'px',
|
536 |
| - left: this.from.pinLeft + 'px' |
| 573 | + left: this.from.pinLeft + 'px', |
| 574 | + marginLeft: 0, |
| 575 | + marginTop: 0 |
537 | 576 | };
|
538 | 577 | }
|
539 | 578 | return this.from;
|
540 | 579 | },
|
541 | 580 | _setValue: function(newValue) {
|
542 |
| - this.to.style.position = newValue.position; |
543 |
| - this.to.style.top = newValue.top; |
544 |
| - this.to.style.left = newValue.left; |
| 581 | + for (var styleName in newValue) { |
| 582 | + this.to.style[styleName] = newValue[styleName]; |
| 583 | + } |
545 | 584 | },
|
546 | 585 | _setFrom: function(defaultValue) {
|
547 | 586 | if (typeof this.from === "undefined") {
|
|
705 | 744 | parseInt(string.substr(5,2),16)
|
706 | 745 | ], result);
|
707 | 746 | }
|
708 |
| - return RGBColor.fromArray(string.replace(/^rgb(a)?\((.*)\)$/, '$2').split(/, /), result); |
| 747 | + return RGBColor.fromArray(string.replace(/^rgb(a)?\((.*)\)$/, '$2').split(","), result); |
709 | 748 | };
|
710 | 749 | RGBColor.fromHSV = function(hsv, result) {
|
711 | 750 | result || (result = new RGBColor());
|
|
0 commit comments