PageSourceSearch

https://www.vistra.com/themes/custom/vistra2024/js/scroll-reveal.js?tm819w

js vistra.com collected 2026-10-02 03:53:18 UTC 4,687 bytes, 169 lines download raw bytes

1/**
2 * Scroll reveal
3 *
4 * When the scroll element is in viewport the css values of gridTemplateColumns are updated according to
5 * vertical scroll values, to reveal the element in the middle.
6 *
7 * The header, 'opacityElement', also fades in and out with scroll.
8 */
9
10const revealElement = $('.scroll-reveal__hero');
11const opacityElement = $('.opacity-0-to-100');
12
13const initialColumns = ['1fr', '0fr', '1fr'];
14const minGridSize = 0;
15let maxGridSize = 1.8;
16let easingRate = 1.8;
17
18const largeScreenSize = 1224;
19const mediumScreenSize = 1024;
20const smallScreenSize = 768;
21
22let currentScroll = window.scrollY;
23let currentGridSize = 0;
24let currentOpacity = 0;
25let animationFrameID = null;
26
27/**
28 * Fully open reveal element
29 */
30function openGrid() {
31  revealElement.css('grid-template-columns', '1fr ' + maxGridSize + 'r 1fr');
32  opacityElement.css('opacity', '1');
33}
34
35/**
36 * Open/close reveal element by amount based on scroll
37 *
38 * @param {number} scrollDiff
39 */
40function updateGrid(scrollDiff) {
41
42  let transitionRate = 0.015;
43  let scrollUpOffset = -100;
44
45  const windowWidth = $(window).width();
46  if (windowWidth < smallScreenSize) {
47    maxGridSize = 35;
48    easingRate = 30;
49    transitionRate = 0.5;
50    scrollUpOffset = 20;
51  } else if (windowWidth < mediumScreenSize) {
52    maxGridSize = 4.5;
53    easingRate = 3;
54    transitionRate = 0.05;
55  } else if (windowWidth < largeScreenSize) {
56    maxGridSize = 3;
57    easingRate = 3;
58    transitionRate = 0.03;
59  } else {
60    maxGridSize = 1.8;
61  }
62
63  const scrollDirection = scrollDiff > 0 ? 'down' : 'up';
64  let columns = initialColumns.slice(); // Clone initialColumns array
65
66  // Open when scrolling down.
67  if (scrollDirection === 'down') {
68    // Speed of transition, with easing
69    const transitionFactor = transitionRate - ((currentGridSize / easingRate) * 0.01);
70    const transitionAmount = Math.abs(scrollDiff) * transitionFactor;
71
72    // Open, while scrolling down.
73    const newGridSize = currentGridSize + transitionAmount
74
75    if (newGridSize <= maxGridSize) {
76      columns[1] = newGridSize + 'fr';
77      currentGridSize = newGridSize;
78    } else {
79      columns[1] = maxGridSize + "fr";
80    }
81
82    if (currentOpacity + transitionAmount <= 1) {
83      currentOpacity = currentOpacity + transitionAmount;
84    }
85
86    // Update css
87    revealElement.css('grid-template-columns', columns.join(' '));
88    opacityElement.css('opacity', currentOpacity);
89
90
91  } else {
92    // Close when scrolling up.
93    // Start close when element is partly out of viewport
94    if (!isElementInViewport(revealElement, scrollUpOffset)) {
95
96      // Speed of transition, with easing
97      const transitionFactor = transitionRate - ((currentGridSize) / easingRate) * 0.01;
98
99      const transitionAmount = Math.abs(scrollDiff) * transitionFactor;
100      const newGridSize = currentGridSize - transitionAmount;
101      if (newGridSize > minGridSize) {
102        columns[1] = newGridSize + 'fr';
103        currentGridSize = newGridSize;
104      } else {
105        columns[1] = '0fr';
106      }
107
108      // Only begin fade out when reveal is almost 3/4 closed
109      if (newGridSize <= 0.5 && (currentOpacity - transitionAmount > 0)) {
110        currentOpacity = currentOpacity - transitionAmount;
111      }
112
113      // Update css
114      revealElement.css('grid-template-columns', columns.join(' '));
115      opacityElement.css('opacity', currentOpacity);
116    }
117  }
118}
119
120/**
121 * Whether element is fully visible, according to offset
122 *
123 * @param {Element} element
124 * @param {number} offset
125 * @returns {boolean|boolean}
126 */
127function isElementInViewport(element, offset = 0) {
128  let rect = element[0].getBoundingClientRect();
129  return (
130    rect.top >= 0 &&
131    rect.bottom <= (window.innerHeight + offset || document.documentElement.clientHeight + offset)
132  );
133}
134
135/**
136 * Handle scroll event using requestAnimationFrame for smoother animations
137 */
138function handleScroll() {
139  // If there's already a scheduled animation frame, exit
140  if (animationFrameID) return;
141
142  // Schedule animation frame
143  animationFrameID = requestAnimationFrame(function () {
144    const newScroll = window.scrollY;
145    const scrollDiff = newScroll - currentScroll;
146    if (isElementInViewport(revealElement, 180)) {
147      updateGrid(scrollDiff);
148      currentScroll = newScroll;
149    }
150    // Reset animationFrameID to null for the next scroll event
151    animationFrameID = null;
152  });
153
154}
155
156Drupal.behaviors.scrollReveal = {
157  attach: function (context, settings) {
158    if (once('scrollReveal', 'html').length) {
159      // Open fully if visible on page load
160      if (isElementInViewport(revealElement, -100)) {
161        openGrid();
162      }
163      window.addEventListener('scroll', handleScroll);
164    }
165  }
166};
167
168
169

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.