PluginProbe
Block Animations, Motion & Scroll Effects – Ghost Kit / 3.3.3
Block Animations, Motion & Scroll Effects – Ghost Kit v3.3.3
3.7.2 3.7.1 3.7.0 3.6.1 trunk 1.6.3 2.25.0 3.3.0 3.3.1 3.3.2 3.3.3 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.5.0 3.5.1 3.6.0
ghostkit / gutenberg / extend / effects / frontend.js

frontend.js in Block Animations, Motion & Scroll Effects – Ghost Kit 3.3.3, at gutenberg/extend/effects/frontend.js

277 lines 6.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import round from '../../utils/round';
2 import DEFAULTS from './reveal/defaults';
3
4 const {
5 GHOSTKIT: { events, instance },
6 Motion: { animate, spring, inView },
7 } = window;
8
9 const isReducedMotion = window.matchMedia(
10 '(prefers-reduced-motion: reduce)'
11 ).matches;
12
13 /**
14 * Effects Reveal.
15 */
16 events.on(document, 'init.blocks.gkt', () => {
17 document
18 .querySelectorAll('[data-gkt-effects]')
19 .forEach(function ($element) {
20 if (isReducedMotion) {
21 $element.removeAttribute('data-gkt-effects');
22 return;
23 }
24
25 const dataString = $element.getAttribute('data-gkt-effects');
26 let data;
27
28 try {
29 data = JSON.parse(dataString);
30 } catch (e) {
31 data = false;
32 }
33
34 $element.removeAttribute('data-gkt-effects');
35
36 if (!data) {
37 return;
38 }
39
40 events.trigger($element, 'prepare.effects.gkt', { data });
41
42 instance.set($element, 'effects', data);
43
44 if (!data?.reveal) {
45 return;
46 }
47
48 const config = {
49 ...DEFAULTS,
50 ...data.reveal,
51 };
52
53 events.trigger($element, 'prepare.effects.reveal.gkt', { config });
54
55 const stopInView = inView($element, () => {
56 $element.classList.remove('ghostkit-effects-reveal');
57
58 const options = {};
59
60 // Easing with Cubic Bezier.
61 if (config?.transition?.type === 'easing') {
62 options.duration = config.transition.duration;
63 options.delay = config.transition.delay;
64 options.easing = config.transition.easing;
65
66 // Easing with Spring.
67 } else if (config?.transition?.type === 'spring') {
68 options.delay = config.transition.delay;
69 options.easing = spring({
70 stiffness: config.transition.stiffness,
71 damping: config.transition.damping,
72 mass: config.transition.mass,
73 });
74
75 // Fix for Scale and Rotate.
76 // https://github.com/motiondivision/motionone/issues/221
77 if (config.scale !== 1) {
78 options.scale = {
79 easing: spring({
80 stiffness: config.transition.stiffness,
81 damping: config.transition.damping,
82 mass: config.transition.mass,
83 restSpeed: 0.01,
84 restDistance: 0.001,
85 }),
86 };
87 }
88 if (config.rotate !== 0) {
89 options.rotate = {
90 easing: spring({
91 stiffness: config.transition.stiffness,
92 damping: config.transition.damping,
93 mass: config.transition.mass,
94 restSpeed: 1,
95 restDistance: 0.1,
96 }),
97 };
98 }
99 }
100
101 const keyframes = {};
102
103 if (config.opacity !== 1) {
104 keyframes.opacity = [config.opacity, 1];
105 }
106 if (config.x !== 0) {
107 keyframes.x = [config.x, 0];
108 }
109 if (config.y !== 0) {
110 keyframes.y = [config.y, 0];
111 }
112 if (config.scale !== 1) {
113 keyframes.scale = [config.scale, 1];
114 }
115 if (config.rotate !== 0) {
116 keyframes.rotate = [config.rotate, 0];
117 }
118
119 const eventData = {
120 config,
121 keyframes,
122 options,
123 stopInView,
124 leaveCallback: () => {},
125 };
126
127 events.trigger($element, 'show.effects.reveal.gkt', eventData);
128
129 // Stop inView listener.
130 eventData.stopInView();
131
132 animate($element, keyframes, options).finished.then(() => {
133 events.trigger(
134 $element,
135 'showed.effects.reveal.gkt',
136 eventData
137 );
138 });
139
140 // This will fire when the element leaves the viewport
141 return eventData.leaveCallback;
142 });
143
144 events.trigger($element, 'prepared.effects.reveal.gkt', { config });
145 });
146 });
147
148 /**
149 * Counters (Number Box, Progress Bar).
150 */
151 events.on(document, 'init.blocks.gkt', () => {
152 document
153 .querySelectorAll('.ghostkit-count-up:not(.ghostkit-count-up-ready)')
154 .forEach(($counter) => {
155 $counter.classList.add('ghostkit-count-up-ready');
156
157 if (isReducedMotion) {
158 return;
159 }
160
161 const isProgress = $counter.classList.contains(
162 'ghostkit-progress-bar'
163 );
164 const from =
165 parseFloat($counter.getAttribute('data-count-from')) || 0;
166 const to =
167 parseFloat(
168 isProgress
169 ? $counter.getAttribute('aria-valuenow')
170 : $counter.textContent
171 ) || 0;
172 let $progressCountBadgeWrap;
173 let $progressCountBadge;
174 let digits = 0;
175
176 // prepare mask.
177 let mask = '';
178 if (!isProgress) {
179 mask = $counter.textContent.replace(to, '${val}');
180 }
181 if (!/\${val}/.test(mask)) {
182 mask = '${val}';
183 }
184
185 if (isProgress) {
186 $progressCountBadgeWrap = $counter
187 .closest('.ghostkit-progress')
188 .querySelector('.ghostkit-progress-bar-count');
189
190 if ($progressCountBadgeWrap) {
191 $progressCountBadge = $progressCountBadgeWrap.querySelector(
192 ':scope > div > span:nth-child(2)'
193 );
194
195 $progressCountBadgeWrap.style.width = '0%';
196 $progressCountBadge.textContent = '0';
197 }
198
199 $counter.style.width = '0%';
200 } else {
201 $counter.textContent = mask.replace('${val}', from);
202
203 // Calculate digits after the decimal point.
204 digits = Math.max(
205 (`${from}`.split('.')?.[1] || '').length,
206 (`${to}`.split('.')?.[1] || '').length
207 );
208 }
209
210 const config = {
211 from,
212 to,
213 duration: 0.8,
214 easing: [0.6, 0, 0.3, 1],
215 cb(progress) {
216 const position = (to - from) * progress + from;
217
218 if (isProgress) {
219 if ($progressCountBadge) {
220 $progressCountBadge.textContent =
221 Math.ceil(position);
222 }
223 } else {
224 $counter.textContent = mask.replace(
225 '${val}',
226 round(position, digits)
227 );
228 }
229 },
230 };
231
232 events.trigger($counter, 'prepare.counter.gkt', { config });
233
234 // Animate counter.
235 const stopInView = inView(
236 $counter,
237 () => {
238 stopInView();
239
240 events.trigger($counter, 'count.counter.gkt', { config });
241
242 if (isProgress) {
243 [$counter, $progressCountBadgeWrap].forEach((el) => {
244 if (el) {
245 animate(
246 el,
247 {
248 width: `${to}%`,
249 },
250 {
251 duration: config.duration,
252 easing: config.easing,
253 }
254 );
255 }
256 });
257 }
258
259 animate(
260 (progress) => {
261 config.cb(progress);
262 },
263 {
264 duration: config.duration,
265 easing: config.easing,
266 }
267 ).finished.then(() => {
268 events.trigger($counter, 'counted.counter.gkt', {
269 config,
270 });
271 });
272 },
273 { margin: '-50px' }
274 );
275 });
276 });
277