scrollreveal.es.js 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. /*! @license ScrollReveal v4.0.1
  2. Copyright 2018 Fisssion LLC.
  3. Licensed under the GNU General Public License 3.0 for
  4. compatible open source projects and non-commercial use.
  5. For commercial sites, themes, projects, and applications,
  6. keep your source code private/proprietary by purchasing
  7. a commercial license from https://scrollrevealjs.org/
  8. */
  9. import $ from 'tealight';
  10. import { parse, multiply, rotateX, rotateY, rotateZ, scale, translateX, translateY } from 'rematrix';
  11. import raf from 'miniraf';
  12. var defaults = {
  13. delay: 0,
  14. distance: '0',
  15. duration: 600,
  16. easing: 'cubic-bezier(0.5, 0, 0, 1)',
  17. interval: 0,
  18. opacity: 0,
  19. origin: 'bottom',
  20. rotate: {
  21. x: 0,
  22. y: 0,
  23. z: 0
  24. },
  25. scale: 1,
  26. cleanup: true,
  27. container: document.documentElement,
  28. desktop: true,
  29. mobile: true,
  30. reset: false,
  31. useDelay: 'always',
  32. viewFactor: 0.0,
  33. viewOffset: {
  34. top: 0,
  35. right: 0,
  36. bottom: 0,
  37. left: 0
  38. },
  39. afterReset: function afterReset() {},
  40. afterReveal: function afterReveal() {},
  41. beforeReset: function beforeReset() {},
  42. beforeReveal: function beforeReveal() {}
  43. }
  44. function failure() {
  45. var root = document.documentElement;
  46. root.classList.remove('sr');
  47. return {
  48. clean: function clean() {},
  49. destroy: function destroy() {},
  50. reveal: function reveal() {},
  51. sync: function sync() {},
  52. get noop() {
  53. return true
  54. }
  55. }
  56. }
  57. function success() {
  58. var html = document.documentElement;
  59. var body = document.body;
  60. html.classList.add('sr');
  61. if (body) {
  62. body.style.height = '100%';
  63. } else {
  64. document.addEventListener('DOMContentLoaded', function () {
  65. body.style.height = '100%';
  66. });
  67. }
  68. }
  69. var mount = { success: success, failure: failure }
  70. function isObject(x) {
  71. return (
  72. x !== null &&
  73. x instanceof Object &&
  74. (x.constructor === Object ||
  75. Object.prototype.toString.call(x) === '[object Object]')
  76. )
  77. }
  78. function each(collection, callback) {
  79. if (isObject(collection)) {
  80. var keys = Object.keys(collection);
  81. return keys.forEach(function (key) { return callback(collection[key], key, collection); })
  82. }
  83. if (collection instanceof Array) {
  84. return collection.forEach(function (item, i) { return callback(item, i, collection); })
  85. }
  86. throw new TypeError('Expected either an array or object literal.')
  87. }
  88. function logger(message) {
  89. var details = [], len = arguments.length - 1;
  90. while ( len-- > 0 ) details[ len ] = arguments[ len + 1 ];
  91. if (this.constructor.debug && console) {
  92. var report = "%cScrollReveal: " + message;
  93. details.forEach(function (detail) { return (report += "\n — " + detail); });
  94. console.log(report, 'color: #ea654b;'); // eslint-disable-line no-console
  95. }
  96. }
  97. function rinse() {
  98. var this$1 = this;
  99. var struct = function () { return ({
  100. active: [],
  101. stale: []
  102. }); };
  103. var elementIds = struct();
  104. var sequenceIds = struct();
  105. var containerIds = struct();
  106. /**
  107. * Take stock of active element IDs.
  108. */
  109. try {
  110. each($('[data-sr-id]'), function (node) {
  111. var id = parseInt(node.getAttribute('data-sr-id'));
  112. elementIds.active.push(id);
  113. });
  114. } catch (e) {
  115. throw e
  116. }
  117. /**
  118. * Destroy stale elements.
  119. */
  120. each(this.store.elements, function (element) {
  121. if (elementIds.active.indexOf(element.id) === -1) {
  122. elementIds.stale.push(element.id);
  123. }
  124. });
  125. each(elementIds.stale, function (staleId) { return delete this$1.store.elements[staleId]; });
  126. /**
  127. * Take stock of active container and sequence IDs.
  128. */
  129. each(this.store.elements, function (element) {
  130. if (containerIds.active.indexOf(element.containerId) === -1) {
  131. containerIds.active.push(element.containerId);
  132. }
  133. if (element.hasOwnProperty('sequence')) {
  134. if (sequenceIds.active.indexOf(element.sequence.id) === -1) {
  135. sequenceIds.active.push(element.sequence.id);
  136. }
  137. }
  138. });
  139. /**
  140. * Destroy stale containers.
  141. */
  142. each(this.store.containers, function (container) {
  143. if (containerIds.active.indexOf(container.id) === -1) {
  144. containerIds.stale.push(container.id);
  145. }
  146. });
  147. each(containerIds.stale, function (staleId) {
  148. var stale = this$1.store.containers[staleId].node;
  149. stale.removeEventListener('scroll', this$1.delegate);
  150. stale.removeEventListener('resize', this$1.delegate);
  151. delete this$1.store.containers[staleId];
  152. });
  153. /**
  154. * Destroy stale sequences.
  155. */
  156. each(this.store.sequences, function (sequence) {
  157. if (sequenceIds.active.indexOf(sequence.id) === -1) {
  158. sequenceIds.stale.push(sequence.id);
  159. }
  160. });
  161. each(sequenceIds.stale, function (staleId) { return delete this$1.store.sequences[staleId]; });
  162. }
  163. function clean(target) {
  164. var this$1 = this;
  165. var dirty;
  166. try {
  167. each($(target), function (node) {
  168. var id = node.getAttribute('data-sr-id');
  169. if (id !== null) {
  170. dirty = true;
  171. var element = this$1.store.elements[id];
  172. if (element.callbackTimer) {
  173. window.clearTimeout(element.callbackTimer.clock);
  174. }
  175. node.setAttribute('style', element.styles.inline.generated);
  176. node.removeAttribute('data-sr-id');
  177. delete this$1.store.elements[id];
  178. }
  179. });
  180. } catch (e) {
  181. return logger.call(this, 'Clean failed.', e.message)
  182. }
  183. if (dirty) {
  184. try {
  185. rinse.call(this);
  186. } catch (e) {
  187. return logger.call(this, 'Clean failed.', e.message)
  188. }
  189. }
  190. }
  191. function destroy() {
  192. var this$1 = this;
  193. /**
  194. * Remove all generated styles and element ids
  195. */
  196. each(this.store.elements, function (element) {
  197. element.node.setAttribute('style', element.styles.inline.generated);
  198. element.node.removeAttribute('data-sr-id');
  199. });
  200. /**
  201. * Remove all event listeners.
  202. */
  203. each(this.store.containers, function (container) {
  204. var target =
  205. container.node === document.documentElement ? window : container.node;
  206. target.removeEventListener('scroll', this$1.delegate);
  207. target.removeEventListener('resize', this$1.delegate);
  208. });
  209. /**
  210. * Clear all data from the store
  211. */
  212. this.store = {
  213. containers: {},
  214. elements: {},
  215. history: [],
  216. sequences: {}
  217. };
  218. }
  219. var getPrefixedCssProp = (function () {
  220. var properties = {};
  221. var style = document.documentElement.style;
  222. function getPrefixedCssProperty(name, source) {
  223. if ( source === void 0 ) source = style;
  224. if (name && typeof name === 'string') {
  225. if (properties[name]) {
  226. return properties[name]
  227. }
  228. if (typeof source[name] === 'string') {
  229. return (properties[name] = name)
  230. }
  231. if (typeof source[("-webkit-" + name)] === 'string') {
  232. return (properties[name] = "-webkit-" + name)
  233. }
  234. throw new RangeError(("Unable to find \"" + name + "\" style property."))
  235. }
  236. throw new TypeError('Expected a string.')
  237. }
  238. getPrefixedCssProperty.clearCache = function () { return (properties = {}); };
  239. return getPrefixedCssProperty
  240. })();
  241. function style(element) {
  242. var computed = window.getComputedStyle(element.node);
  243. var position = computed.position;
  244. var config = element.config;
  245. /**
  246. * Generate inline styles
  247. */
  248. var inline = {};
  249. var inlineStyle = element.node.getAttribute('style') || '';
  250. var inlineMatch = inlineStyle.match(/[\w-]+\s*:\s*[^;]+\s*/gi) || [];
  251. inline.computed = inlineMatch ? inlineMatch.map(function (m) { return m.trim(); }).join('; ') + ';' : '';
  252. inline.generated = inlineMatch.some(function (m) { return m.match(/visibility\s?:\s?visible/i); })
  253. ? inline.computed
  254. : inlineMatch.concat( ['visibility: visible']).map(function (m) { return m.trim(); }).join('; ') + ';';
  255. /**
  256. * Generate opacity styles
  257. */
  258. var computedOpacity = parseFloat(computed.opacity);
  259. var configOpacity = !isNaN(parseFloat(config.opacity))
  260. ? parseFloat(config.opacity)
  261. : parseFloat(computed.opacity);
  262. var opacity = {
  263. computed: computedOpacity !== configOpacity ? ("opacity: " + computedOpacity + ";") : '',
  264. generated: computedOpacity !== configOpacity ? ("opacity: " + configOpacity + ";") : ''
  265. };
  266. /**
  267. * Generate transformation styles
  268. */
  269. var transformations = [];
  270. if (parseFloat(config.distance)) {
  271. var axis = config.origin === 'top' || config.origin === 'bottom' ? 'Y' : 'X';
  272. /**
  273. * Let’s make sure our our pixel distances are negative for top and left.
  274. * e.g. { origin: 'top', distance: '25px' } starts at `top: -25px` in CSS.
  275. */
  276. var distance = config.distance;
  277. if (config.origin === 'top' || config.origin === 'left') {
  278. distance = /^-/.test(distance) ? distance.substr(1) : ("-" + distance);
  279. }
  280. var ref = distance.match(/(^-?\d+\.?\d?)|(em$|px$|%$)/g);
  281. var value = ref[0];
  282. var unit = ref[1];
  283. switch (unit) {
  284. case 'em':
  285. distance = parseInt(computed.fontSize) * value;
  286. break
  287. case 'px':
  288. distance = value;
  289. break
  290. case '%':
  291. /**
  292. * Here we use `getBoundingClientRect` instead of
  293. * the existing data attached to `element.geometry`
  294. * because only the former includes any transformations
  295. * current applied to the element.
  296. *
  297. * If that behavior ends up being unintuitive, this
  298. * logic could instead utilize `element.geometry.height`
  299. * and `element.geoemetry.width` for the distaince calculation
  300. */
  301. distance =
  302. axis === 'Y'
  303. ? element.node.getBoundingClientRect().height * value / 100
  304. : element.node.getBoundingClientRect().width * value / 100;
  305. break
  306. default:
  307. throw new RangeError('Unrecognized or missing distance unit.')
  308. }
  309. if (axis === 'Y') {
  310. transformations.push(translateY(distance));
  311. } else {
  312. transformations.push(translateX(distance));
  313. }
  314. }
  315. if (config.rotate.x) { transformations.push(rotateX(config.rotate.x)); }
  316. if (config.rotate.y) { transformations.push(rotateY(config.rotate.y)); }
  317. if (config.rotate.z) { transformations.push(rotateZ(config.rotate.z)); }
  318. if (config.scale !== 1) {
  319. if (config.scale === 0) {
  320. /**
  321. * The CSS Transforms matrix interpolation specification
  322. * basically disallows transitions of non-invertible
  323. * matrixes, which means browsers won't transition
  324. * elements with zero scale.
  325. *
  326. * That’s inconvenient for the API and developer
  327. * experience, so we simply nudge their value
  328. * slightly above zero; this allows browsers
  329. * to transition our element as expected.
  330. *
  331. * `0.0002` was the smallest number
  332. * that performed across browsers.
  333. */
  334. transformations.push(scale(0.0002));
  335. } else {
  336. transformations.push(scale(config.scale));
  337. }
  338. }
  339. var transform = {};
  340. if (transformations.length) {
  341. transform.property = getPrefixedCssProp('transform');
  342. /**
  343. * The default computed transform value should be one of:
  344. * undefined || 'none' || 'matrix()' || 'matrix3d()'
  345. */
  346. transform.computed = {
  347. raw: computed[transform.property],
  348. matrix: parse(computed[transform.property])
  349. };
  350. transformations.unshift(transform.computed.matrix);
  351. var product = transformations.reduce(multiply);
  352. transform.generated = {
  353. initial: ((transform.property) + ": matrix3d(" + (product.join(', ')) + ");"),
  354. final: ((transform.property) + ": matrix3d(" + (transform.computed.matrix.join(
  355. ', '
  356. )) + ");")
  357. };
  358. } else {
  359. transform.generated = {
  360. initial: '',
  361. final: ''
  362. };
  363. }
  364. /**
  365. * Generate transition styles
  366. */
  367. var transition = {};
  368. if (opacity.generated || transform.generated.initial) {
  369. transition.property = getPrefixedCssProp('transition');
  370. transition.computed = computed[transition.property];
  371. transition.fragments = [];
  372. var delay = config.delay;
  373. var duration = config.duration;
  374. var easing = config.easing;
  375. if (opacity.generated) {
  376. transition.fragments.push({
  377. delayed: ("opacity " + (duration / 1000) + "s " + easing + " " + (delay / 1000) + "s"),
  378. instant: ("opacity " + (duration / 1000) + "s " + easing + " 0s")
  379. });
  380. }
  381. if (transform.generated.initial) {
  382. transition.fragments.push({
  383. delayed: ((transform.property) + " " + (duration / 1000) + "s " + easing + " " + (delay /
  384. 1000) + "s"),
  385. instant: ((transform.property) + " " + (duration / 1000) + "s " + easing + " 0s")
  386. });
  387. }
  388. /**
  389. * The default computed transition property should be one of:
  390. * undefined || '' || 'all 0s ease 0s' || 'all 0s 0s cubic-bezier()'
  391. */
  392. if (transition.computed && !transition.computed.match(/all 0s/)) {
  393. transition.fragments.unshift({
  394. delayed: transition.computed,
  395. instant: transition.computed
  396. });
  397. }
  398. var composed = transition.fragments.reduce(
  399. function (composition, fragment, i) {
  400. composition.delayed +=
  401. i === 0 ? fragment.delayed : (", " + (fragment.delayed));
  402. composition.instant +=
  403. i === 0 ? fragment.instant : (", " + (fragment.instant));
  404. return composition
  405. },
  406. {
  407. delayed: '',
  408. instant: ''
  409. }
  410. );
  411. transition.generated = {
  412. delayed: ((transition.property) + ": " + (composed.delayed) + ";"),
  413. instant: ((transition.property) + ": " + (composed.instant) + ";")
  414. };
  415. } else {
  416. transition.generated = {
  417. delayed: '',
  418. instant: ''
  419. };
  420. }
  421. return {
  422. inline: inline,
  423. opacity: opacity,
  424. position: position,
  425. transform: transform,
  426. transition: transition
  427. }
  428. }
  429. function animate(element, force) {
  430. if ( force === void 0 ) force = {};
  431. var pristine = force.pristine || this.pristine;
  432. var delayed =
  433. element.config.useDelay === 'always' ||
  434. (element.config.useDelay === 'onload' && pristine) ||
  435. (element.config.useDelay === 'once' && !element.seen);
  436. var shouldReveal = element.visible && !element.revealed;
  437. var shouldReset = !element.visible && element.revealed && element.config.reset;
  438. if (force.reveal || shouldReveal) {
  439. return triggerReveal.call(this, element, delayed)
  440. }
  441. if (force.reset || shouldReset) {
  442. return triggerReset.call(this, element)
  443. }
  444. }
  445. function triggerReveal(element, delayed) {
  446. var styles = [
  447. element.styles.inline.generated,
  448. element.styles.opacity.computed,
  449. element.styles.transform.generated.final
  450. ];
  451. if (delayed) {
  452. styles.push(element.styles.transition.generated.delayed);
  453. } else {
  454. styles.push(element.styles.transition.generated.instant);
  455. }
  456. element.revealed = element.seen = true;
  457. element.node.setAttribute('style', styles.filter(function (s) { return s !== ''; }).join(' '));
  458. registerCallbacks.call(this, element, delayed);
  459. }
  460. function triggerReset(element) {
  461. var styles = [
  462. element.styles.inline.generated,
  463. element.styles.opacity.generated,
  464. element.styles.transform.generated.initial,
  465. element.styles.transition.generated.instant
  466. ];
  467. element.revealed = false;
  468. element.node.setAttribute('style', styles.filter(function (s) { return s !== ''; }).join(' '));
  469. registerCallbacks.call(this, element);
  470. }
  471. function registerCallbacks(element, isDelayed) {
  472. var this$1 = this;
  473. var duration = isDelayed
  474. ? element.config.duration + element.config.delay
  475. : element.config.duration;
  476. var beforeCallback = element.revealed
  477. ? element.config.beforeReveal
  478. : element.config.beforeReset;
  479. var afterCallback = element.revealed
  480. ? element.config.afterReveal
  481. : element.config.afterReset;
  482. var elapsed = 0;
  483. if (element.callbackTimer) {
  484. elapsed = Date.now() - element.callbackTimer.start;
  485. window.clearTimeout(element.callbackTimer.clock);
  486. }
  487. beforeCallback(element.node);
  488. element.callbackTimer = {
  489. start: Date.now(),
  490. clock: window.setTimeout(function () {
  491. afterCallback(element.node);
  492. element.callbackTimer = null;
  493. if (element.revealed && !element.config.reset && element.config.cleanup) {
  494. clean.call(this$1, element.node);
  495. }
  496. }, duration - elapsed)
  497. };
  498. }
  499. var nextUniqueId = (function () {
  500. var uid = 0;
  501. return function () { return uid++; }
  502. })();
  503. function sequence(element, pristine) {
  504. if ( pristine === void 0 ) pristine = this.pristine;
  505. /**
  506. * We first check if the element should reset.
  507. */
  508. if (!element.visible && element.revealed && element.config.reset) {
  509. return animate.call(this, element, { reset: true })
  510. }
  511. var seq = this.store.sequences[element.sequence.id];
  512. var i = element.sequence.index;
  513. if (seq) {
  514. var visible = new SequenceModel(seq, 'visible', this.store);
  515. var revealed = new SequenceModel(seq, 'revealed', this.store);
  516. seq.models = { visible: visible, revealed: revealed };
  517. /**
  518. * If the sequence has no revealed members,
  519. * then we reveal the first visible element
  520. * within that sequence.
  521. *
  522. * The sequence then cues a recursive call
  523. * in both directions.
  524. */
  525. if (!revealed.body.length) {
  526. var nextId = seq.members[visible.body[0]];
  527. var nextElement = this.store.elements[nextId];
  528. if (nextElement) {
  529. cue.call(this, seq, visible.body[0], -1, pristine);
  530. cue.call(this, seq, visible.body[0], +1, pristine);
  531. return animate.call(this, nextElement, { reveal: true, pristine: pristine })
  532. }
  533. }
  534. /**
  535. * If our element isn’t resetting, we check the
  536. * element sequence index against the head, and
  537. * then the foot of the sequence.
  538. */
  539. if (
  540. !seq.blocked.head &&
  541. i === [].concat( revealed.head ).pop() &&
  542. i >= [].concat( visible.body ).shift()
  543. ) {
  544. cue.call(this, seq, i, -1, pristine);
  545. return animate.call(this, element, { reveal: true, pristine: pristine })
  546. }
  547. if (
  548. !seq.blocked.foot &&
  549. i === [].concat( revealed.foot ).shift() &&
  550. i <= [].concat( visible.body ).pop()
  551. ) {
  552. cue.call(this, seq, i, +1, pristine);
  553. return animate.call(this, element, { reveal: true, pristine: pristine })
  554. }
  555. }
  556. }
  557. function Sequence(interval) {
  558. var i = Math.abs(interval);
  559. if (!isNaN(i)) {
  560. this.id = nextUniqueId();
  561. this.interval = Math.max(i, 16);
  562. this.members = [];
  563. this.models = {};
  564. this.blocked = {
  565. head: false,
  566. foot: false
  567. };
  568. } else {
  569. throw new RangeError('Invalid sequence interval.')
  570. }
  571. }
  572. function SequenceModel(seq, prop, store) {
  573. var this$1 = this;
  574. this.head = [];
  575. this.body = [];
  576. this.foot = [];
  577. each(seq.members, function (id, index) {
  578. var element = store.elements[id];
  579. if (element && element[prop]) {
  580. this$1.body.push(index);
  581. }
  582. });
  583. if (this.body.length) {
  584. each(seq.members, function (id, index) {
  585. var element = store.elements[id];
  586. if (element && !element[prop]) {
  587. if (index < this$1.body[0]) {
  588. this$1.head.push(index);
  589. } else {
  590. this$1.foot.push(index);
  591. }
  592. }
  593. });
  594. }
  595. }
  596. function cue(seq, i, direction, pristine) {
  597. var this$1 = this;
  598. var blocked = ['head', null, 'foot'][1 + direction];
  599. var nextId = seq.members[i + direction];
  600. var nextElement = this.store.elements[nextId];
  601. seq.blocked[blocked] = true;
  602. setTimeout(function () {
  603. seq.blocked[blocked] = false;
  604. if (nextElement) {
  605. sequence.call(this$1, nextElement, pristine);
  606. }
  607. }, seq.interval);
  608. }
  609. function initialize() {
  610. var this$1 = this;
  611. rinse.call(this);
  612. each(this.store.elements, function (element) {
  613. var styles = [element.styles.inline.generated];
  614. if (element.visible) {
  615. styles.push(element.styles.opacity.computed);
  616. styles.push(element.styles.transform.generated.final);
  617. } else {
  618. styles.push(element.styles.opacity.generated);
  619. styles.push(element.styles.transform.generated.initial);
  620. }
  621. element.node.setAttribute('style', styles.filter(function (s) { return s !== ''; }).join(' '));
  622. });
  623. each(this.store.containers, function (container) {
  624. var target =
  625. container.node === document.documentElement ? window : container.node;
  626. target.addEventListener('scroll', this$1.delegate);
  627. target.addEventListener('resize', this$1.delegate);
  628. });
  629. /**
  630. * Manually invoke delegate once to capture
  631. * element and container dimensions, container
  632. * scroll position, and trigger any valid reveals
  633. */
  634. this.delegate();
  635. /**
  636. * Wipe any existing `setTimeout` now
  637. * that initialization has completed.
  638. */
  639. this.initTimeout = null;
  640. }
  641. function isMobile(agent) {
  642. if ( agent === void 0 ) agent = navigator.userAgent;
  643. return /Android|iPhone|iPad|iPod/i.test(agent)
  644. }
  645. function deepAssign(target) {
  646. var sources = [], len = arguments.length - 1;
  647. while ( len-- > 0 ) sources[ len ] = arguments[ len + 1 ];
  648. if (isObject(target)) {
  649. each(sources, function (source) {
  650. each(source, function (data, key) {
  651. if (isObject(data)) {
  652. if (!target[key] || !isObject(target[key])) {
  653. target[key] = {};
  654. }
  655. deepAssign(target[key], data);
  656. } else {
  657. target[key] = data;
  658. }
  659. });
  660. });
  661. return target
  662. } else {
  663. throw new TypeError('Target must be an object literal.')
  664. }
  665. }
  666. function reveal(target, options, syncing) {
  667. var this$1 = this;
  668. if ( options === void 0 ) options = {};
  669. if ( syncing === void 0 ) syncing = false;
  670. var containerBuffer = [];
  671. var sequence$$1;
  672. var interval = options.interval || defaults.interval;
  673. try {
  674. if (interval) {
  675. sequence$$1 = new Sequence(interval);
  676. }
  677. var nodes = $(target);
  678. if (!nodes.length) {
  679. throw new Error('Invalid reveal target.')
  680. }
  681. var elements = nodes.reduce(function (elementBuffer, elementNode) {
  682. var element = {};
  683. var existingId = elementNode.getAttribute('data-sr-id');
  684. if (existingId) {
  685. deepAssign(element, this$1.store.elements[existingId]);
  686. /**
  687. * In order to prevent previously generated styles
  688. * from throwing off the new styles, the style tag
  689. * has to be reverted to its pre-reveal state.
  690. */
  691. element.node.setAttribute('style', element.styles.inline.computed);
  692. } else {
  693. element.id = nextUniqueId();
  694. element.node = elementNode;
  695. element.seen = false;
  696. element.revealed = false;
  697. element.visible = false;
  698. }
  699. var config = deepAssign({}, element.config || this$1.defaults, options);
  700. if ((!config.mobile && isMobile()) || (!config.desktop && !isMobile())) {
  701. if (existingId) {
  702. clean.call(this$1, element);
  703. }
  704. return elementBuffer // skip elements that are disabled
  705. }
  706. var containerNode = $(config.container)[0];
  707. if (!containerNode) {
  708. throw new Error('Invalid container.')
  709. }
  710. if (!containerNode.contains(elementNode)) {
  711. return elementBuffer // skip elements found outside the container
  712. }
  713. var containerId;
  714. {
  715. containerId = getContainerId(
  716. containerNode,
  717. containerBuffer,
  718. this$1.store.containers
  719. );
  720. if (containerId === null) {
  721. containerId = nextUniqueId();
  722. containerBuffer.push({ id: containerId, node: containerNode });
  723. }
  724. }
  725. element.config = config;
  726. element.containerId = containerId;
  727. element.styles = style(element);
  728. if (sequence$$1) {
  729. element.sequence = {
  730. id: sequence$$1.id,
  731. index: sequence$$1.members.length
  732. };
  733. sequence$$1.members.push(element.id);
  734. }
  735. elementBuffer.push(element);
  736. return elementBuffer
  737. }, []);
  738. /**
  739. * Modifying the DOM via setAttribute needs to be handled
  740. * separately from reading computed styles in the map above
  741. * for the browser to batch DOM changes (limiting reflows)
  742. */
  743. each(elements, function (element) {
  744. this$1.store.elements[element.id] = element;
  745. element.node.setAttribute('data-sr-id', element.id);
  746. });
  747. } catch (e) {
  748. return logger.call(this, 'Reveal failed.', e.message)
  749. }
  750. /**
  751. * Now that element set-up is complete...
  752. * Let’s commit any container and sequence data we have to the store.
  753. */
  754. each(containerBuffer, function (container) {
  755. this$1.store.containers[container.id] = {
  756. id: container.id,
  757. node: container.node
  758. };
  759. });
  760. if (sequence$$1) {
  761. this.store.sequences[sequence$$1.id] = sequence$$1;
  762. }
  763. /**
  764. * If reveal wasn't invoked by sync, we want to
  765. * make sure to add this call to the history.
  766. */
  767. if (syncing !== true) {
  768. this.store.history.push({ target: target, options: options });
  769. /**
  770. * Push initialization to the event queue, giving
  771. * multiple reveal calls time to be interpreted.
  772. */
  773. if (this.initTimeout) {
  774. window.clearTimeout(this.initTimeout);
  775. }
  776. this.initTimeout = window.setTimeout(initialize.bind(this), 0);
  777. }
  778. }
  779. function getContainerId(node) {
  780. var collections = [], len = arguments.length - 1;
  781. while ( len-- > 0 ) collections[ len ] = arguments[ len + 1 ];
  782. var id = null;
  783. each(collections, function (collection) {
  784. each(collection, function (container) {
  785. if (id === null && container.node === node) {
  786. id = container.id;
  787. }
  788. });
  789. });
  790. return id
  791. }
  792. /**
  793. * Re-runs the reveal method for each record stored in history,
  794. * for capturing new content asynchronously loaded into the DOM.
  795. */
  796. function sync() {
  797. var this$1 = this;
  798. each(this.store.history, function (record) {
  799. reveal.call(this$1, record.target, record.options, true);
  800. });
  801. initialize.call(this);
  802. }
  803. var polyfill = function (x) { return (x > 0) - (x < 0) || +x; };
  804. var mathSign = Math.sign || polyfill
  805. function getGeometry(target, isContainer) {
  806. /**
  807. * We want to ignore padding and scrollbars for container elements.
  808. * More information here: https://goo.gl/vOZpbz
  809. */
  810. var height = isContainer ? target.node.clientHeight : target.node.offsetHeight;
  811. var width = isContainer ? target.node.clientWidth : target.node.offsetWidth;
  812. var offsetTop = 0;
  813. var offsetLeft = 0;
  814. var node = target.node;
  815. do {
  816. if (!isNaN(node.offsetTop)) {
  817. offsetTop += node.offsetTop;
  818. }
  819. if (!isNaN(node.offsetLeft)) {
  820. offsetLeft += node.offsetLeft;
  821. }
  822. node = node.offsetParent;
  823. } while (node)
  824. return {
  825. bounds: {
  826. top: offsetTop,
  827. right: offsetLeft + width,
  828. bottom: offsetTop + height,
  829. left: offsetLeft
  830. },
  831. height: height,
  832. width: width
  833. }
  834. }
  835. function getScrolled(container) {
  836. var top, left;
  837. if (container.node === document.documentElement) {
  838. top = window.pageYOffset;
  839. left = window.pageXOffset;
  840. } else {
  841. top = container.node.scrollTop;
  842. left = container.node.scrollLeft;
  843. }
  844. return { top: top, left: left }
  845. }
  846. function isElementVisible(element) {
  847. if ( element === void 0 ) element = {};
  848. var container = this.store.containers[element.containerId];
  849. if (!container) { return }
  850. var viewFactor = Math.max(0, Math.min(1, element.config.viewFactor));
  851. var viewOffset = element.config.viewOffset;
  852. var elementBounds = {
  853. top: element.geometry.bounds.top + element.geometry.height * viewFactor,
  854. right: element.geometry.bounds.right - element.geometry.width * viewFactor,
  855. bottom: element.geometry.bounds.bottom - element.geometry.height * viewFactor,
  856. left: element.geometry.bounds.left + element.geometry.width * viewFactor
  857. };
  858. var containerBounds = {
  859. top: container.geometry.bounds.top + container.scroll.top + viewOffset.top,
  860. right: container.geometry.bounds.right + container.scroll.left - viewOffset.right,
  861. bottom:
  862. container.geometry.bounds.bottom + container.scroll.top - viewOffset.bottom,
  863. left: container.geometry.bounds.left + container.scroll.left + viewOffset.left
  864. };
  865. return (
  866. (elementBounds.top < containerBounds.bottom &&
  867. elementBounds.right > containerBounds.left &&
  868. elementBounds.bottom > containerBounds.top &&
  869. elementBounds.left < containerBounds.right) ||
  870. element.styles.position === 'fixed'
  871. )
  872. }
  873. function delegate(
  874. event,
  875. elements
  876. ) {
  877. var this$1 = this;
  878. if ( event === void 0 ) event = { type: 'init' };
  879. if ( elements === void 0 ) elements = this.store.elements;
  880. raf(function () {
  881. var stale = event.type === 'init' || event.type === 'resize';
  882. each(this$1.store.containers, function (container) {
  883. if (stale) {
  884. container.geometry = getGeometry.call(this$1, container, true);
  885. }
  886. var scroll = getScrolled.call(this$1, container);
  887. if (container.scroll) {
  888. container.direction = {
  889. x: mathSign(scroll.left - container.scroll.left),
  890. y: mathSign(scroll.top - container.scroll.top)
  891. };
  892. }
  893. container.scroll = scroll;
  894. });
  895. /**
  896. * Due to how the sequencer is implemented, it’s
  897. * important that we update the state of all
  898. * elements, before any animation logic is
  899. * evaluated (in the second loop below).
  900. */
  901. each(elements, function (element) {
  902. if (stale) {
  903. element.geometry = getGeometry.call(this$1, element);
  904. }
  905. element.visible = isElementVisible.call(this$1, element);
  906. });
  907. each(elements, function (element) {
  908. if (element.sequence) {
  909. sequence.call(this$1, element);
  910. } else {
  911. animate.call(this$1, element);
  912. }
  913. });
  914. this$1.pristine = false;
  915. });
  916. }
  917. function transformSupported() {
  918. var style = document.documentElement.style;
  919. return 'transform' in style || 'WebkitTransform' in style
  920. }
  921. function transitionSupported() {
  922. var style = document.documentElement.style;
  923. return 'transition' in style || 'WebkitTransition' in style
  924. }
  925. var version = "4.0.1";
  926. var boundDelegate;
  927. var boundDestroy;
  928. var boundReveal;
  929. var boundClean;
  930. var boundSync;
  931. var config;
  932. var debug;
  933. var instance;
  934. function ScrollReveal(options) {
  935. if ( options === void 0 ) options = {};
  936. var invokedWithoutNew =
  937. typeof this === 'undefined' ||
  938. Object.getPrototypeOf(this) !== ScrollReveal.prototype;
  939. if (invokedWithoutNew) {
  940. return new ScrollReveal(options)
  941. }
  942. if (!ScrollReveal.isSupported()) {
  943. logger.call(this, 'Instantiation failed.', 'This browser is not supported.');
  944. return mount.failure()
  945. }
  946. var buffer;
  947. try {
  948. buffer = config
  949. ? deepAssign({}, config, options)
  950. : deepAssign({}, defaults, options);
  951. } catch (e) {
  952. logger.call(this, 'Invalid configuration.', e.message);
  953. return mount.failure()
  954. }
  955. try {
  956. var container = $(buffer.container)[0];
  957. if (!container) {
  958. throw new Error('Invalid container.')
  959. }
  960. } catch (e) {
  961. logger.call(this, e.message);
  962. return mount.failure()
  963. }
  964. config = buffer;
  965. if ((!config.mobile && isMobile()) || (!config.desktop && !isMobile())) {
  966. logger.call(
  967. this,
  968. 'This device is disabled.',
  969. ("desktop: " + (config.desktop)),
  970. ("mobile: " + (config.mobile))
  971. );
  972. return mount.failure()
  973. }
  974. mount.success();
  975. this.store = {
  976. containers: {},
  977. elements: {},
  978. history: [],
  979. sequences: {}
  980. };
  981. this.pristine = true;
  982. boundDelegate = boundDelegate || delegate.bind(this);
  983. boundDestroy = boundDestroy || destroy.bind(this);
  984. boundReveal = boundReveal || reveal.bind(this);
  985. boundClean = boundClean || clean.bind(this);
  986. boundSync = boundSync || sync.bind(this);
  987. Object.defineProperty(this, 'delegate', { get: function () { return boundDelegate; } });
  988. Object.defineProperty(this, 'destroy', { get: function () { return boundDestroy; } });
  989. Object.defineProperty(this, 'reveal', { get: function () { return boundReveal; } });
  990. Object.defineProperty(this, 'clean', { get: function () { return boundClean; } });
  991. Object.defineProperty(this, 'sync', { get: function () { return boundSync; } });
  992. Object.defineProperty(this, 'defaults', { get: function () { return config; } });
  993. Object.defineProperty(this, 'version', { get: function () { return version; } });
  994. Object.defineProperty(this, 'noop', { get: function () { return false; } });
  995. return instance ? instance : (instance = this)
  996. }
  997. ScrollReveal.isSupported = function () { return transformSupported() && transitionSupported(); };
  998. Object.defineProperty(ScrollReveal, 'debug', {
  999. get: function () { return debug || false; },
  1000. set: function (value) { return (debug = typeof value === 'boolean' ? value : debug); }
  1001. });
  1002. ScrollReveal();
  1003. export default ScrollReveal;