column-progressive-animation.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. ( function( v, undefined ) {
  2. 'use strict';
  3. // this one can be initialized late (on load) instead of on DOMContentLoaded
  4. window.addEventListener( 'load', function() {
  5. var columns = document.querySelectorAll( '[data-progressive-animation]' );
  6. if ( columns.length && ! document.body.classList.contains( 'fl-builder-active' ) ) {
  7. vamtam_greensock_wait( function() {
  8. v.addScrollHandler( {
  9. defaultOptions: {
  10. origin: 'center center',
  11. type: 'progressive',
  12. exit: true,
  13. delay: 0,
  14. mobile: false,
  15. pin: false,
  16. pinTrigger: 'center',
  17. },
  18. blockAnimations: false,
  19. canActivate: function( mobile ) {
  20. return mobile || ! v.MEDIA.layout[ 'layout-below-max' ];
  21. },
  22. buildTimeline: function( target, withExit ) {
  23. var timeline = new vamtamgs.TimelineLite( { paused: true } );
  24. var type = target.getAttribute( 'data-progressive-animation' );
  25. if ( type === 'dummy' ) {
  26. timeline.fromTo( target, 1, { opacity: 1 }, {opacity: 1 }, '0' );
  27. withExit && timeline.to( target, 1, { opacity: 1 }, '1' );
  28. } else if ( type === 'rotate' ) {
  29. timeline.fromTo( target, 1, { rotation: -180 }, { rotation: 0 }, '0' );
  30. withExit && timeline.to( target, 1, { rotation: 180 }, '1' );
  31. } else if ( type === 'fade' ) {
  32. timeline.fromTo( target, 1, {
  33. opacity: 0,
  34. }, {
  35. opacity: 1,
  36. }, '0' );
  37. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  38. withExit && timeline.to( target, 1, { y: -100 }, '1.6' );
  39. // Move + Fade //
  40. } else if ( type === 'move-from-top' ) {
  41. timeline.fromTo( target, 1, {
  42. y: -160,
  43. opacity: 0,
  44. }, {
  45. y: 0,
  46. opacity: 1,
  47. }, '0' );
  48. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  49. withExit && timeline.to( target, 1, { y: -100 }, '1.6' );
  50. } else if ( type === 'move-from-bottom' ) {
  51. timeline.fromTo( target, 1, {
  52. y: 100,
  53. opacity: 0,
  54. }, {
  55. y: 0,
  56. opacity: 1,
  57. }, '0' );
  58. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  59. withExit && timeline.to( target, 1, { y: -50 }, '1.6' );
  60. } else if ( type === 'move-from-left' ) {
  61. timeline.fromTo( target, 1, {
  62. x: -160,
  63. opacity: 0,
  64. }, {
  65. x: 0,
  66. opacity: 1,
  67. }, '0' );
  68. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  69. withExit && timeline.to( target, 1, { x: -100 }, '1.6' );
  70. } else if ( type === 'move-from-right' ) {
  71. timeline.fromTo( target, 1, {
  72. x: 160,
  73. opacity: 0,
  74. }, {
  75. x: 0,
  76. opacity: 1,
  77. }, '0' );
  78. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  79. withExit && timeline.to( target, 1, { x: 100 }, '1.6' );
  80. // Scale //
  81. } else if ( type === 'scale-in' ) {
  82. timeline.fromTo( target, 1, {
  83. opacity: 0,
  84. scaleX: 0.0,
  85. scaleY: 0.0,
  86. }, {
  87. opacity: 1,
  88. scaleX: 1,
  89. scaleY: 1,
  90. }, '0' );
  91. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  92. } else if ( type === 'scale-out' ) {
  93. timeline.fromTo( target, 1, {
  94. opacity: 0,
  95. scaleX: 2,
  96. scaleY: 2,
  97. }, {
  98. opacity: 1,
  99. scaleX: 1,
  100. scaleY: 1,
  101. }, '0' );
  102. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  103. // Move + Scale //
  104. // Zoom In //
  105. } else if ( type === 'move-scale-in-from-top' ) {
  106. timeline.fromTo( target, 1, {
  107. y: -160,
  108. opacity: 0,
  109. scaleX: 0.6,
  110. scaleY: 0.6,
  111. }, {
  112. y: 0,
  113. opacity: 1,
  114. scaleX: 1,
  115. scaleY: 1,
  116. }, '0' );
  117. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  118. withExit && timeline.to( target, 1, { y: -100 }, '1.6' );
  119. } else if ( type === 'move-scale-in-from-bottom' ) {
  120. timeline.fromTo( target, 1, {
  121. y: 160,
  122. opacity: 0,
  123. scaleX: 0.6,
  124. scaleY: 0.6,
  125. }, {
  126. y: 0,
  127. opacity: 1,
  128. scaleX: 1,
  129. scaleY: 1,
  130. }, '0' );
  131. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  132. withExit && timeline.to( target, 1, { y: -100 }, '1.6' );
  133. } else if ( type === 'move-scale-in-from-left' ) {
  134. timeline.fromTo( target, 1, {
  135. x: -160,
  136. opacity: 0,
  137. scaleX: 0.6,
  138. scaleY: 0.6,
  139. }, {
  140. x: 0,
  141. opacity: 1,
  142. scaleX: 1,
  143. scaleY: 1,
  144. }, '0' );
  145. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  146. withExit && timeline.to( target, 1, { x: -100 }, '1.6' );
  147. } else if ( type === 'move-scale-in-from-right' ) {
  148. timeline.fromTo( target, 1, {
  149. x: 160,
  150. opacity: 0,
  151. scaleX: 0.6,
  152. scaleY: 0.6,
  153. }, {
  154. x: 0,
  155. opacity: 1,
  156. scaleX: 1,
  157. scaleY: 1,
  158. }, '0' );
  159. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  160. withExit && timeline.to( target, 1, { x: 100 }, '1.6' );
  161. // Zoom Out //
  162. } else if ( type === 'move-scale-out-from-top' ) {
  163. timeline.fromTo( target, 1, {
  164. y: -160,
  165. opacity: 0,
  166. scaleX: 1.6,
  167. scaleY: 1.6,
  168. }, {
  169. y: 0,
  170. opacity: 1,
  171. scaleX: 1,
  172. scaleY: 1,
  173. }, '0' );
  174. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  175. withExit && timeline.to( target, 1, { y: -100 }, '1.6' );
  176. } else if ( type === 'move-scale-out-from-bottom' ) {
  177. timeline.fromTo( target, 1, {
  178. y: 160,
  179. opacity: 0,
  180. scaleX: 1.6,
  181. scaleY: 1.6,
  182. }, {
  183. y: 0,
  184. opacity: 1,
  185. scaleX: 1,
  186. scaleY: 1,
  187. }, '0' );
  188. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  189. withExit && timeline.to( target, 1, { y: -100 }, '1.6' );
  190. } else if ( type === 'move-scale-out-from-left' ) {
  191. timeline.fromTo( target, 1, {
  192. x: -160,
  193. opacity: 0,
  194. scaleX: 1.6,
  195. scaleY: 1.6,
  196. }, {
  197. x: 0,
  198. opacity: 1,
  199. scaleX: 1,
  200. scaleY: 1,
  201. }, '0' );
  202. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  203. withExit && timeline.to( target, 1, { x: -100 }, '1.6' );
  204. } else if ( type === 'move-scale-out-from-right' ) {
  205. timeline.fromTo( target, 1, {
  206. x: 160,
  207. opacity: 0,
  208. scaleX: 1.6,
  209. scaleY: 1.6,
  210. }, {
  211. x: 0,
  212. opacity: 1,
  213. scaleX: 1,
  214. scaleY: 1,
  215. }, '0' );
  216. withExit && timeline.to( target, 0.4, { opacity: 0 }, '1.6' );
  217. withExit && timeline.to( target, 1, { x: 100 }, '1.6' );
  218. // Rotate //
  219. } else if ( type === 'rotate-from-top-right' ) {
  220. timeline.fromTo( target, 1, {
  221. y: -200,
  222. x: 120,
  223. rotation: -10,
  224. opacity: 0,
  225. }, {
  226. y: 0,
  227. x: 0,
  228. rotation: 0,
  229. opacity: 1,
  230. }, '0' );
  231. withExit && timeline.fromTo( target, 1, { immediateRender: false, y: 0 }, { y: -70 }, '1.6' );
  232. } else if ( type === 'page-title' ) {
  233. var line = target.querySelector( '.page-header-line' );
  234. var desc = target.querySelector( '.desc' );
  235. var shadow = document.getElementById( 'sub-header' ).querySelector( '.text-shadow' );
  236. timeline.fromTo( target.querySelector( 'h1' ), 0.9, { y: 0, opacity: 1 }, { y: -10, opacity: 0, ease: vamtamgs.Quad.easeIn }, '0.1' );
  237. desc && timeline.fromTo( desc, 1, { y: 0, opacity: 1 }, { y: 30, opacity: 0, ease: vamtamgs.Quad.easeIn }, '0' );
  238. shadow && timeline.fromTo( shadow, 1, { opacity: 0.3 }, { opacity: 0.7, ease: vamtamgs.Quad.easeIn }, '0' );
  239. line && timeline.to( line, 1, { scaleX: 0, y: 30, opacity: 0, ease: vamtamgs.Quad.easeIn }, '0' );
  240. } else if ( type === 'custom' ) {
  241. timeline.to( target, 1, { className: target.getAttribute( 'data-progressive-animation-custom' ) }, '1' );
  242. }
  243. return timeline;
  244. },
  245. getPinTrigger: function( column ) {
  246. if ( column.options.pinTrigger === 'center' ) {
  247. return this.winHeight / 2 - column.height / 2;
  248. }
  249. if ( column.options.pinTrigger === 'bottom' ) {
  250. return this.winHeight - column.height;
  251. }
  252. if ( column.options.pinTrigger === 'top' ) {
  253. return 0;
  254. }
  255. },
  256. calculatePinDuration: function( column ) {
  257. if ( v.MEDIA.layout[ 'layout-below-max' ] ) {
  258. column.vamtamProgressiveTimeline.pinDuration = 0;
  259. } else if ( column.vamtamProgressiveTimeline.options.pin === 'parent' ) {
  260. var closestRow = column.closest( '.fl-row-content' );
  261. column.vamtamProgressiveTimeline.pinDuration = closestRow.offsetHeight - ( column.vamtamProgressiveTimeline.top - v.offset( closestRow ).top );
  262. } else {
  263. column.vamtamProgressiveTimeline.pinDuration = + column.vamtamProgressiveTimeline.options.pin;
  264. }
  265. },
  266. onresize: function() {
  267. var self = this;
  268. this.winHeight = window.innerHeight;
  269. this.blockAnimations = true;
  270. requestAnimationFrame( function() {
  271. // if the timeline was previously initialized - reset the progress to 0
  272. for ( var i = 0; i < columns.length; i++ ) {
  273. if ( columns[i].vamtamProgressiveTimeline.timeline ) {
  274. columns[i].vamtamProgressiveTimeline.timeline.progress( 0 );
  275. }
  276. if ( columns[i].vamtamProgressiveTimeline.wrapper ) {
  277. Object.assign( columns[i].vamtamProgressiveTimeline.pusher.style, {
  278. top: '',
  279. width: '',
  280. height: '',
  281. } );
  282. Object.assign( columns[i].vamtamProgressiveTimeline.wrapper.style, {
  283. top: '',
  284. width: '',
  285. height: '',
  286. position: '',
  287. } );
  288. }
  289. }
  290. requestAnimationFrame( function() {
  291. var cpos = window.pageYOffset;
  292. var i;
  293. var chromeWrapperFix = [];
  294. // measure
  295. for ( i = 0; i < columns.length; i++ ) {
  296. var columnTop = v.offset( columns[i] ).top;
  297. Object.assign( columns[i].vamtamProgressiveTimeline, {
  298. top: columnTop,
  299. height: columns[i].offsetHeight,
  300. width: columns[i].offsetWidth,
  301. } );
  302. self.calculatePinDuration( columns[i] );
  303. }
  304. // mutate
  305. for ( i = 0; i < columns.length; i++ ) {
  306. var data = columns[i].vamtamProgressiveTimeline;
  307. if ( self.canActivate( data.options.mobile ) ) {
  308. data.timeline = self.buildTimeline(
  309. columns[i],
  310. data.options.type === 'progressive' && data.options.exit
  311. );
  312. if ( data.pusher ) {
  313. data.pusher.parentElement.minHeight = data.options.pin + 'px';
  314. data.pusher.style.height = data.pinDuration + 'px';
  315. if ( ! data.pusher.classList.contains( 'fl-col' ) ) {
  316. data.pusher.style.width = data.width + 'px';
  317. }
  318. data.wrapper.style.height = data.height + 'px';
  319. data.wrapper.style.top = self.getPinTrigger( data ) + 'px';
  320. data.wrapper.classList.add( 'vamtam-pin-active' );
  321. }
  322. } else if ( data.timeline ) {
  323. data.timeline.seek( 1 );
  324. data.timeline = null;
  325. if ( data.pusher ) {
  326. data.pusher.parentElement.minHeight = '';
  327. data.wrapper.classList.remove( 'vamtam-pin-active' );
  328. /*
  329. Fix a weird Chrome bug where the wrapper
  330. behaves as if it has visibility: hidden
  331. after disabling the pin for narrow screens
  332. */
  333. data.wrapper.style.display = 'block';
  334. chromeWrapperFix.push( data.wrapper );
  335. /* End Chrome fix */
  336. }
  337. }
  338. }
  339. (function( wrappers ) {
  340. requestAnimationFrame( function() {
  341. wrappers.forEach( function( wrapper ) {
  342. wrapper.style.display = '';
  343. } );
  344. } );
  345. })( chromeWrapperFix );
  346. self.blockAnimations = false;
  347. self.measure( cpos );
  348. self.mutate( cpos );
  349. } );
  350. } );
  351. },
  352. init: function() {
  353. this.winHeight = window.innerHeight;
  354. var i, closestRow;
  355. // measure
  356. for ( i = 0; i < columns.length; i++ ) {
  357. var options = Object.assign( {}, this.defaultOptions, JSON.parse( columns[i].getAttribute( 'data-vamtam-animation-options' ) ) || {} );
  358. var columnTop = v.offset( columns[i] ).top;
  359. columns[i].vamtamProgressiveTimeline = {
  360. top: columnTop,
  361. height: columns[i].offsetHeight,
  362. width: columns[i].offsetWidth,
  363. options: options
  364. };
  365. this.calculatePinDuration( columns[i] );
  366. columns[i].style.transformOrigin = columns[i].vamtamProgressiveTimeline.options.origin;
  367. if ( this.canActivate( options.mobile ) ) {
  368. columns[i].vamtamProgressiveTimeline.timeline = this.buildTimeline(
  369. columns[i],
  370. options.type === 'progressive' && options.exit
  371. );
  372. } else {
  373. columns[i].vamtamProgressiveTimeline.timeline = null;
  374. }
  375. }
  376. // mutate
  377. for ( i = 0; i < columns.length; i++ ) {
  378. var data = columns[i].vamtamProgressiveTimeline;
  379. closestRow = columns[i].closest( '.fl-row' );
  380. closestRow && closestRow.classList.add( 'vamtam-animation-inside' );
  381. if ( data.options.pin !== false ) {
  382. closestRow.classList.add( 'vamtam-pin-inside' );
  383. data.pusher = document.createElement( 'div' );
  384. data.pusher.classList.add( 'vamtam-pin-pusher' );
  385. // by default Beaver Builder sets the width of the .fl-col element
  386. // we need to move the .fl-col class to the wrapper
  387. // and set the width of the original column to 100%
  388. if ( columns[i].classList.contains( 'fl-col' ) ) {
  389. data.pusher.classList.add( 'fl-col' );
  390. data.pusher.classList.add( 'fl-node-' + columns[i].attributes['data-node'].value );
  391. data.pusher.style.width = '';
  392. }
  393. data.wrapper = document.createElement( 'div' );
  394. data.wrapper.classList.add( 'vamtam-pin-wrapper' );
  395. data.wrapper.style.willChange = 'transform, position';
  396. data.wrapper.style.height = data.height + 'px';
  397. data.wrapper.style.top = this.getPinTrigger( data ) + 'px';
  398. columns[i].before( data.pusher );
  399. data.wrapper.appendChild( columns[i] );
  400. data.pusher.appendChild( data.wrapper );
  401. columns[i].style.width = '100%';
  402. data.pusher.parentElement.style.position = 'relative';
  403. if ( data.timeline && data.options.pin !== 'parent' ) {
  404. data.pusher.parentElement.style.minHeight = data.options.pin + 'px';
  405. }
  406. if ( this.canActivate( data.options.mobile ) ) {
  407. Object.assign( data.pusher.style, {
  408. width: data.width + 'px',
  409. height: data.pinDuration + 'px',
  410. });
  411. data.wrapper.classList.add( 'vamtam-pin-active' );
  412. }
  413. }
  414. }
  415. window.addEventListener( 'resize', window.VAMTAM.debounce( this.onresize, 100 ).bind( this ), false );
  416. },
  417. measure: function() {
  418. },
  419. mutate: function( cpos ) {
  420. if ( this.blockAnimations ) {
  421. return;
  422. }
  423. for ( var i = 0; i < columns.length; i++ ) {
  424. var data = columns[i].vamtamProgressiveTimeline;
  425. if ( data.timeline && cpos + this.winHeight > data.top ) {
  426. // natural column vertical middle
  427. var from = data.top + data.height / 2;
  428. var progress;
  429. if ( data.options.pin !== false ) {
  430. var pinTrigger;
  431. if ( data.options.pinTrigger === 'center' ) {
  432. pinTrigger = cpos + this.winHeight / 2;
  433. } else if ( data.options.pinTrigger === 'bottom' ) {
  434. pinTrigger = cpos + this.winHeight - data.height / 2;
  435. } else if ( data.options.pinTrigger === 'top' ) {
  436. pinTrigger = cpos + data.height / 2;
  437. }
  438. // pin length starts when the "natural column vertical middle"
  439. // aligns with the trigger (middle of the viewport, top/bottom of viewport - half column height)
  440. //
  441. // it ends after data.pinDuration - data.height px
  442. var pinTo = from + data.pinDuration - data.height;
  443. progress = 2 * ( pinTrigger - from ) / ( pinTo - from ) - 1;
  444. } else {
  445. progress = 1 - ( ( from - cpos ) / Math.min( this.winHeight / 2, from ) );
  446. }
  447. progress -= data.options.delay;
  448. if ( data.options.type === 'progressive' ) {
  449. if ( data.timeline.totalDuration() > 1 || ! columns[i].vamtamProgressiveTimeline.options.exit ) {
  450. // two part (entry/exit) animation
  451. // note that the exit is optional
  452. progress = Math.min( 1, Math.max( -1, progress ) ); // clip
  453. // progress + 1 is used so that we can avoid negative position params
  454. //
  455. // [0; 1] -> entrance animation
  456. // [1; 2] -> exit animation
  457. //
  458. // it's then divided by two, since the progress() method takes a [0; 1] fraction as its argument
  459. progress = progress + 1;
  460. } else {
  461. // only exit animation
  462. progress = Math.min( 1, Math.max( 0, progress ) ); // clip
  463. }
  464. data.timeline.seek( progress );
  465. } else {
  466. if ( ! data.used && progress >= 0 ) {
  467. data.used = true;
  468. data.timeline.timeScale( 2 ).play();
  469. } else if ( data.used ) {
  470. // if the animation was played once - make sure that the timeline is at its end
  471. data.timeline.seek( 1 );
  472. }
  473. }
  474. }
  475. }
  476. }
  477. } );
  478. } );
  479. }
  480. }, { passive: true });
  481. } )( window.VAMTAM );