|
63 | 63 | var options = $.extend(dataOptions[i] || {}, jsOptions[i] || {});
|
64 | 64 | typeof options.start == "undefined" || (options.start = convertToElement(options.start));
|
65 | 65 | typeof options.start != "undefined" || (options.start = this[0]);
|
66 |
| - typeof options.trigger != "undefined" || (options.trigger = "100%"); |
67 | 66 | optionsArr.push(options);
|
68 | 67 | }
|
69 | 68 | return optionsArr;
|
|
181 | 180 | return $.extend({}, globalOptions, options);
|
182 | 181 | }
|
183 | 182 |
|
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; |
191 | 195 | }
|
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 | + }; |
193 | 204 | }
|
194 | 205 |
|
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']; |
202 | 209 | }
|
| 210 | + return value; |
203 | 211 | }
|
204 | 212 |
|
205 | 213 | function convertToElement(value) {
|
|
233 | 241 | }
|
234 | 242 | return value;
|
235 | 243 | }
|
| 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 | + } |
236 | 254 |
|
237 | 255 | function interpolate(from, to, progress) {
|
238 | 256 | return (to - from) * progress + from;
|
|
243 | 261 | this.axis = options.axis;
|
244 | 262 | this.from = options.from;
|
245 | 263 | 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); |
247 | 267 | this.start = convertToElement(options.start) || options.start;
|
| 268 | + |
248 | 269 | if (typeof options.ease == "function") {
|
249 | 270 | this.ease = options.ease;
|
250 | 271 | }
|
|
254 | 275 | }
|
255 | 276 |
|
256 | 277 | 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(), |
258 | 279 | durationPx = convertOption(options.duration, maxDuration);
|
259 | 280 | this.duration = function() {
|
260 | 281 | return durationPx;
|
|
263 | 284 | else {
|
264 | 285 | var scene = this;
|
265 | 286 | 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; |
267 | 288 | };
|
268 | 289 | }
|
269 | 290 | }
|
| 291 | + Scene.AXIS_X = 'x'; |
| 292 | + Scene.AXIS_Y = 'y'; |
270 | 293 | Scene.STATE_BEFORE = 'before';
|
271 | 294 | Scene.STATE_DURING = 'during';
|
272 | 295 | Scene.STATE_AFTER = 'after';
|
|
280 | 303 | _needsUpdate: function() {
|
281 | 304 | return this.prevState === Scene.STATE_DURING ||
|
282 | 305 | 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"); |
285 | 311 | },
|
286 | 312 | 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); |
288 | 314 | },
|
289 | 315 | updateDuration: function() {
|
290 | 316 | this.durationPx = this.duration.call(this);
|
|
368 | 394 |
|
369 | 395 | function PinScene($el, options) {
|
370 | 396 | options.to = convertToElement(options.to) || $el[0];
|
| 397 | + typeof options.trigger != "undefined" || (options.trigger = 0); |
371 | 398 | Scene.call(this, $el, options);
|
372 | 399 | }
|
373 | 400 | PinScene.prototype = $.extend(Object.create(Scene.prototype), {
|
374 | 401 | _needsUpdate: function() {
|
375 |
| - return this.prevState != this.state; |
| 402 | + return (typeof this.prevState != "undefined" || this.state == Scene.STATE_DURING) && |
| 403 | + this.prevState != this.state; |
376 | 404 | },
|
377 | 405 | _getOldValue: function(style) {
|
378 | 406 | var toStyle = getComputedStyle(this.to);
|
|
384 | 412 | },
|
385 | 413 | _getNewValue: function() {
|
386 | 414 | if (this.state == Scene.STATE_DURING) {
|
387 |
| - var top = parseFloat(this.from.top), |
388 |
| - left = parseFloat(this.from.left); |
389 | 415 | return {
|
390 | 416 | 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' |
393 | 419 | };
|
394 | 420 | }
|
395 | 421 | return this.from;
|
|
398 | 424 | this.to.style.position = newValue.position;
|
399 | 425 | this.to.style.top = newValue.top;
|
400 | 426 | 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 | + } |
401 | 441 | }
|
402 | 442 | });
|
403 | 443 |
|
|
0 commit comments