PageSourceSearch

https://chengpao.com.tw/Scripts/scrollReveal.js

js chengpao.com.tw collected 2026-10-06 15:08:11 UTC 18,483 bytes, 588 lines download raw bytes

1/*
2                       _ _ _____                      _   _
3                      | | |  __ \                    | | (_)
4    ___  ___ _ __ ___ | | | |__) |_____   _____  __ _| |  _ ___
5   / __|/ __| '__/ _ \| | |  _  // _ \ \ / / _ \/ _` | | | / __|
6   \__ \ (__| | | (_) | | | | \ \  __/\ V /  __/ (_| | |_| \__ \
7   |___/\___|_|  \___/|_|_|_|  \_\___| \_/ \___|\__,_|_(_) |___/ v2.3.0
8                                                        _/ |
9                                                       |__/
10
11================================================================================
12
13   scrollReveal.js (c) 2015 Julian Lloyd ( @julianlloyd )
14   Licensed under MIT ( http://www.opensource.org/licenses/mit-license.php )
15
16==============================================================================*/
17
18window.scrollReveal = (function( window ){
19
20  'use strict';
21
22  var _requestAnimFrame;
23  var _extend;
24  var _handler;
25  var self;
26
27  function scrollReveal( config ){
28
29    self         = this;
30    self.elems   = {};
31    self.serial  = 1;
32    self.blocked = false;
33    self.config  = _extend( self.defaults, config );
34
35    if ( self.isMobile() && !self.config.mobile || !self.isSupported() ){
36      self.destroy();
37      return;
38    }
39
40    if ( self.config.viewport === window.document.documentElement ){
41
42      window.addEventListener( 'scroll', _handler, false );
43      window.addEventListener( 'resize', _handler, false );
44
45    } else {
46      self.config.viewport.addEventListener( 'scroll', _handler, false );
47    }
48
49    self.init( true );
50  }
51
52  scrollReveal.prototype = {
53
54    defaults: {
55
56      enter:    'bottom',
57      move:     '8px',
58      over:     '0.6s',
59      wait:     '0s',
60      easing:   'ease',
61
62      scale:    { direction: 'up', power: '5%' },
63      rotate:   { x: 0, y: 0, z: 0 },
64
65      opacity:  0,
66      mobile:   false,
67      reset:    false,
68
69      //        Expects a reference to a DOM node (the <html> node by default)
70      //        which is used as the context when checking element visibility.
71      viewport: window.document.documentElement,
72
73      //        'always' — delay every time an animation resets
74      //        'onload' - delay only for animations triggered by first load
75      //        'once'   — delay only the first time an animation reveals
76      delay:    'once',
77
78      //        vFactor changes when an element is considered in the viewport.
79      //        The default value of 0.60 means 60% of an element must be
80      //        visible for its reveal animation to trigger.
81      vFactor:  0.60,
82
83      complete: function( el ){} // Note: reset animations do not complete.
84    },
85
86    // Queries the DOM, builds scrollReveal elements and triggers animation.
87    // @param {boolean} flag — a hook for controlling delay on first load.
88    init: function( flag ){
89
90      var serial;
91      var elem;
92      var query;
93
94      query = Array.prototype.slice.call( self.config.viewport.querySelectorAll('[data-sr]') );
95      query.forEach(function( el ){
96
97        serial      = self.serial++;
98        elem        = self.elems[ serial ] = { domEl: el };
99        elem.config = self.configFactory( elem );
100        elem.styles = self.styleFactory( elem );
101        elem.seen   = false;
102
103        el.removeAttribute('data-sr');
104
105        el.setAttribute( 'style',
106            elem.styles.inline
107          + elem.styles.initial
108        );
109      })
110
111      self.scrolled = self.scrollY();
112      self.animate( flag );
113    },
114
115    // Applies and removes appropriate styles.
116    // @param {boolean} flag — a hook for controlling delay on first load.
117    animate: function( flag ){
118
119      var key;
120      var elem;
121      var visible;
122
123      // Begin element store digest.
124      for ( key in self.elems ){
125        if ( self.elems.hasOwnProperty( key ) ){
126
127          elem    = self.elems[ key ];
128          visible = self.isElemInViewport( elem );
129
130          if ( visible ){
131
132            if ( self.config.delay === 'always'
133            || ( self.config.delay === 'onload' && flag )
134            || ( self.config.delay === 'once'   && !elem.seen ) ){
135
136              // Use delay.
137              elem.domEl.setAttribute( 'style',
138                  elem.styles.inline
139                + elem.styles.target
140                + elem.styles.transition
141              );
142
143            } else {
144
145              // Don’t use delay.
146              elem.domEl.setAttribute( 'style',
147                  elem.styles.inline
148                + elem.styles.target
149                + elem.styles.reset
150              );
151            }
152
153            elem.seen = true;
154
155            if ( !elem.config.reset && !elem.animating ){
156              elem.animating = true;
157              complete( key );
158            }
159
160          } else if ( !visible && elem.config.reset ){
161
162            elem.domEl.setAttribute( 'style',
163                elem.styles.inline
164              + elem.styles.initial
165              + elem.styles.reset
166            );
167          }
168        }
169      }
170
171      // Digest complete, now un-block the event handler.
172      self.blocked = false;
173
174      // Cleans the DOM and removes completed elements from self.elems.
175      // @param {integer} key — self.elems property key.
176      function complete( key ){
177
178        var elem = self.elems[ key ];
179
180        setTimeout(function(){
181
182          elem.domEl.setAttribute( 'style', elem.styles.inline );
183          elem.config.complete( elem.domEl );
184          delete self.elems[ key ];
185
186        }, elem.styles.duration );
187      }
188    },
189
190    // Parses an elements data-sr attribute, and returns a configuration object.
191    // @param {object} elem — An object from self.elems.
192    // @return {object}
193    configFactory: function( elem ){
194
195      var parsed = {};
196      var config = {};
197      var words  = elem.domEl.getAttribute('data-sr').split( /[, ]+/ );
198
199      words.forEach(function( keyword, i ){
200        switch ( keyword ){
201
202          case 'enter':
203
204            parsed.enter = words[ i + 1 ];
205            break;
206
207          case 'wait':
208
209            parsed.wait = words[ i + 1 ];
210            break;
211
212          case 'move':
213
214            parsed.move = words[ i + 1 ];
215            break;
216
217          case 'ease':
218
219            parsed.move = words[ i + 1 ];
220            parsed.ease = 'ease';
221            break;
222
223          case 'ease-in':
224
225            if ( words[ i + 1 ] == 'up' || words[ i + 1 ] == 'down' ){
226
227              parsed.scale.direction = words[ i + 1 ];
228              parsed.scale.power     = words[ i + 2 ];
229              parsed.easing          = 'ease-in';
230              break;
231            }
232
233            parsed.move   = words[ i + 1 ];
234            parsed.easing = 'ease-in';
235            break;
236
237          case 'ease-in-out':
238
239            if ( words[ i + 1 ] == 'up' || words[ i + 1 ] == 'down' ){
240
241              parsed.scale.direction = words[ i + 1 ];
242              parsed.scale.power     = words[ i + 2 ];
243              parsed.easing          = 'ease-in-out';
244              break;
245            }
246
247            parsed.move   = words[ i + 1 ];
248            parsed.easing = 'ease-in-out';
249            break;
250
251          case 'ease-out':
252
253            if ( words[ i + 1 ] == 'up' || words[ i + 1 ] == 'down' ){
254
255              parsed.scale.direction = words[ i + 1 ];
256              parsed.scale.power     = words[ i + 2 ];
257              parsed.easing          = 'ease-out';
258              break;
259            }
260
261            parsed.move   = words[ i + 1 ];
262            parsed.easing = 'ease-out';
263            break;
264
265          case 'hustle':
266
267            if ( words[ i + 1 ] == 'up' || words[ i + 1 ] == 'down' ){
268
269              parsed.scale.direction = words[ i + 1 ];
270              parsed.scale.power     = words[ i + 2 ];
271              parsed.easing          = 'cubic-bezier( 0.6, 0.2, 0.1, 1 )';
272              break;
273            }
274
275            parsed.move   = words[ i + 1 ];
276            parsed.easing = 'cubic-bezier( 0.6, 0.2, 0.1, 1 )';
277            break;
278
279          case 'over':
280
281            parsed.over = words[ i + 1 ];
282            break;
283
284          case 'flip':
285          case 'pitch':
286            parsed.rotate   = parsed.rotate || {};
287            parsed.rotate.x = words[ i + 1 ];
288            break;
289
290          case 'spin':
291          case 'yaw':
292            parsed.rotate   = parsed.rotate || {};
293            parsed.rotate.y = words[ i + 1 ];
294            break;
295
296          case 'roll':
297            parsed.rotate   = parsed.rotate || {};
298            parsed.rotate.z = words[ i + 1 ];
299            break;
300
301          case 'reset':
302
303            if ( words[ i - 1 ] == 'no' ){
304              parsed.reset = false;
305            } else {
306              parsed.reset = true;
307            }
308            break;
309
310          case 'scale':
311
312            parsed.scale = {};
313
314            if ( words[ i + 1 ] == 'up' || words[ i + 1 ] == 'down' ){
315
316              parsed.scale.direction = words[ i + 1 ];
317              parsed.scale.power     = words[ i + 2 ];
318              break;
319            }
320
321            parsed.scale.power = words[ i + 1 ];
322            break;
323
324          case 'vFactor':
325          case 'vF':
326            parsed.vFactor = words[ i + 1 ];
327            break;
328
329          case 'opacity':
330            parsed.opacity = words[ i + 1 ];
331            break;
332
333          default:
334            return;
335        }
336      });
337
338      // Build default config object, then apply any keywords parsed from the
339      // data-sr attribute.
340      config = _extend( config, self.config );
341      config = _extend( config, parsed );
342
343      if ( config.enter === 'top' || config.enter === 'bottom' ){
344        config.axis = 'Y';
345      } else if ( config.enter === 'left' || config.enter === 'right' ){
346        config.axis = 'X';
347      }
348
349      // Let’s make sure our our pixel distances are negative for top and left.
350      // e.g. "enter top and move 25px" starts at 'top: -25px' in CSS.
351      if ( config.enter === 'top' || config.enter === 'left' ){
352        config.move = '-' + config.move;
353      }
354
355      return config;
356    },
357
358    // Generates styles based on an elements configuration property.
359    // @param {object} elem — An object from self.elems.
360    // @return {object}
361    styleFactory: function( elem ){
362
363      var inline;
364      var initial;
365      var reset;
366      var target;
367      var transition;
368
369      var cfg      = elem.config;
370      var duration = ( parseFloat( cfg.over ) + parseFloat( cfg.wait ) ) * 1000;
371
372      // Want to disable delay on mobile devices? Uncomment the line below.
373      // if ( self.isMobile() && self.config.mobile ) cfg.wait = 0;
374
375      if ( elem.domEl.getAttribute('style') ){
376        inline = elem.domEl.getAttribute('style') + '; visibility: visible; ';
377      } else {
378        inline = 'visibility: visible; ';
379      }
380
381      transition = '-webkit-transition: -webkit-transform ' + cfg.over + ' ' + cfg.easing + ' ' + cfg.wait + ', opacity ' + cfg.over + ' ' + cfg.easing + ' ' + cfg.wait + '; ' +
382                           'transition: transform '         + cfg.over + ' ' + cfg.easing + ' ' + cfg.wait + ', opacity ' + cfg.over + ' ' + cfg.easing + ' ' + cfg.wait + '; ' +
383                  '-webkit-perspective: 1000;' +
384          '-webkit-backface-visibility: hidden;';
385
386      reset      = '-webkit-transition: -webkit-transform ' + cfg.over + ' ' + cfg.easing + ' 0s, opacity ' + cfg.over + ' ' + cfg.easing + ' 0s; ' +
387                           'transition: transform '         + cfg.over + ' ' + cfg.easing + ' 0s, opacity ' + cfg.over + ' ' + cfg.easing + ' 0s; ' +
388                  '-webkit-perspective: 1000; ' +
389          '-webkit-backface-visibility: hidden; ';
390
391      initial = 'transform:';
392      target  = 'transform:';
393      build();
394
395      // Build again for webkit…
396      initial += '-webkit-transform:';
397      target  += '-webkit-transform:';
398      build();
399
400      return {
401        transition: transition,
402        initial:    initial,
403        target:     target,
404        reset:      reset,
405        inline:     inline,
406        duration:   duration
407      };
408
409      // Constructs initial and target styles.
410      function build(){
411
412        if ( parseInt( cfg.move ) !== 0 ){
413          initial += ' translate' + cfg.axis + '(' + cfg.move + ')';
414          target  += ' translate' + cfg.axis + '(0)';
415        }
416
417        if ( parseInt( cfg.scale.power ) !== 0 ){
418
419          if ( cfg.scale.direction === 'up' ){
420            cfg.scale.value = 1 - ( parseFloat( cfg.scale.power ) * 0.01 );
421          } else if ( cfg.scale.direction === 'down' ){
422            cfg.scale.value = 1 + ( parseFloat( cfg.scale.power ) * 0.01 );
423          }
424
425          initial += ' scale(' + cfg.scale.value + ')';
426          target  += ' scale(1)';
427        }
428
429        if ( cfg.rotate.x ){
430          initial += ' rotateX(' + cfg.rotate.x + ')';
431          target  += ' rotateX(0)';
432        }
433
434        if ( cfg.rotate.y ){
435          initial += ' rotateY(' + cfg.rotate.y + ')';
436          target  += ' rotateY(0)';
437        }
438
439        if ( cfg.rotate.z ){
440          initial += ' rotateZ(' + cfg.rotate.z + ')';
441          target  += ' rotateZ(0)';
442        }
443
444        initial += '; opacity: ' + cfg.opacity + '; ';
445        target  += '; opacity: 1; ';
446      }
447    },
448
449    getViewportH: function(){
450
451      var client = self.config.viewport['clientHeight'];
452      var inner  = window['innerHeight'];
453
454      if ( self.config.viewport === window.document.documentElement ){
455        return ( client < inner ) ? inner : client;
456      }
457
458      return client;
459    },
460
461    scrollY: function(){
462      if ( self.config.viewport === window.document.documentElement ){
463        return window.pageYOffset;
464      } else {
465        return self.config.viewport.scrollTop + self.config.viewport.offsetTop;
466      }
467    },
468
469    getOffset: function( el ){
470
471      var offsetTop  = 0;
472      var offsetLeft = 0;
473
474      do {
475        if ( !isNaN( el.offsetTop ) ){
476          offsetTop  += el.offsetTop;
477        }
478        if ( !isNaN( el.offsetLeft ) ){
479          offsetLeft += el.offsetLeft;
480        }
481      } while ( el = el.offsetParent );
482
483      return {
484        top: offsetTop,
485        left: offsetLeft
486      };
487    },
488
489    isElemInViewport: function( elem ){
490
491      var elHeight = elem.domEl.offsetHeight;
492      var elTop    = self.getOffset( elem.domEl ).top;
493      var elBottom = elTop + elHeight;
494      var vFactor  = elem.config.vFactor || 0;
495
496      return ( confirmBounds() || isPositionFixed() );
497
498      function confirmBounds(){
499
500        var top        = elTop + elHeight * vFactor;
501        var bottom     = elBottom - elHeight * vFactor;
502        var viewBottom = self.scrolled + self.getViewportH();
503        var viewTop    = self.scrolled;
504
505        return ( top < viewBottom ) && ( bottom > viewTop );
506      }
507
508      function isPositionFixed(){
509
510        var el    = elem.domEl;
511        var style = el.currentStyle || window.getComputedStyle( el, null );
512
513        return style.position === 'fixed';
514      }
515    },
516
517    isMobile: function(){
518
519      var agent = navigator.userAgent || navigator.vendor || window.opera;
520
521      return (/(ipad|playbook|silk|android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test( agent )||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|
521sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( agent.substr( 0, 4 ))) ? true : false;
522    },
523
524    isSupported: function(){
525
526      var sensor    = document.createElement('sensor');
527      var cssPrefix = 'Webkit,Moz,O,'.split(',');
528      var tests     = ( 'transition ' + cssPrefix.join('transition,') ).split(',');
529
530      for ( var i = 0; i < tests.length; i++ ){
531        if ( !sensor.style[tests[i]] === '' ){
532          return false;
533        }
534      }
535
536      return true;
537    },
538
539    destroy: function(){
540
541      var node = self.config.viewport;
542      var query = Array.prototype.slice.call( node.querySelectorAll('[data-sr]') );
543
544      query.forEach(function( el ){
545        el.removeAttribute('data-sr');
546      });
547    }
548  } // End of the scrollReveal prototype ======================================|
549
550  _handler = function( e ){
551
552    if ( !self.blocked ){
553
554      self.blocked  = true;
555      self.scrolled = self.scrollY();
556
557      _requestAnimFrame(function(){
558        self.animate();
559      });
560    }
561  }
562
563  _extend = function( target, src ){
564
565    for ( var prop in src ){
566      if ( src.hasOwnProperty( prop ) ){
567        target[ prop ] = src[ prop ];
568      }
569    }
570
571    return target;
572  }
573
574  // RequestAnimationFrame polyfill.
575  _requestAnimFrame = (function(){
576
577    return window.requestAnimationFrame        ||
578           window.webkitRequestAnimationFrame  ||
579           window.mozRequestAnimationFrame     ||
580
581          function( callback ){
582            window.setTimeout( callback, 1000 / 60 );
583          };
584  }());
585
586  return scrollReveal;
587
588})( window );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.