PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.78
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.78
51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / widgets / Parallax_Depth_Cards / script.js

script.js in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.78, at includes/widgets/Parallax_Depth_Cards/script.js

295 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 "use strict";
2
3 (function ($) {
4 const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
5
6 const toBool = (value, fallback = false) => {
7 if (value === undefined || value === null || value === "") {
8 return fallback;
9 }
10 if (value === true || value === "yes" || value === "1" || value === 1) {
11 return true;
12 }
13 if (value === false || value === "no" || value === "0" || value === 0) {
14 return false;
15 }
16 return fallback;
17 };
18
19 const toNumber = (value, fallback = 0) => {
20 const parsed = parseFloat(value);
21 return Number.isFinite(parsed) ? parsed : fallback;
22 };
23
24 const getPointer = (event, card) => {
25 const rect = card.getBoundingClientRect();
26 if (!rect.width || !rect.height) {
27 return { x: 0, y: 0 };
28 }
29
30 const x = (event.clientX - rect.left) / rect.width;
31 const y = (event.clientY - rect.top) / rect.height;
32 return {
33 x: clamp(x * 2 - 1, -1, 1),
34 y: clamp(y * 2 - 1, -1, 1),
35 };
36 };
37
38 const normalizeOptions = (dataset) => {
39 const intensity = clamp(toNumber(dataset.intensity, 60) / 100, 0, 1);
40 return {
41 tilt: toBool(dataset.tilt, true),
42 parallax: toBool(dataset.parallax, true),
43 trigger: dataset.trigger || "hover",
44 reduceMotion: toBool(dataset.reduceMotion, true),
45 disableTouch: toBool(dataset.disableTouch, true),
46 intensity,
47 maxTilt: clamp(toNumber(dataset.maxTilt, 12), 0, 30),
48 depthStrength: Math.max(toNumber(dataset.depthStrength, 18), 0),
49 smoothing: clamp(toNumber(dataset.smoothing, 0.12), 0.02, 0.4),
50 resetDuration: Math.max(toNumber(dataset.resetDuration, 350), 0),
51 };
52 };
53
54 const initParallaxDepthCards = (root) => {
55 const scope = root instanceof HTMLElement ? root : document;
56 const wrappers = scope.querySelectorAll(".king-addons-parallax-depth-cards");
57
58 wrappers.forEach((wrapper) => {
59 if (wrapper.dataset.pdcInit === "yes") {
60 return;
61 }
62 wrapper.dataset.pdcInit = "yes";
63
64 const options = normalizeOptions(wrapper.dataset || {});
65 if (!options.tilt && !options.parallax) {
66 return;
67 }
68
69 const prefersReduced =
70 window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
71 if (prefersReduced && options.reduceMotion) {
72 wrapper.classList.add("is-motion-disabled");
73 return;
74 }
75
76 const isCoarsePointer =
77 window.matchMedia && window.matchMedia("(hover: none), (pointer: coarse)").matches;
78 if (options.disableTouch && isCoarsePointer) {
79 wrapper.classList.add("is-motion-disabled");
80 return;
81 }
82
83 const cards = Array.from(wrapper.querySelectorAll(".king-addons-parallax-depth-cards__card"));
84 if (!cards.length) {
85 return;
86 }
87
88 const instances = new Map();
89 let activeInstance = null;
90
91 const resetActive = () => {
92 if (activeInstance) {
93 activeInstance.reset();
94 activeInstance = null;
95 }
96 };
97
98 const setActiveInstance = (instance) => {
99 if (activeInstance && activeInstance !== instance) {
100 activeInstance.reset();
101 }
102 activeInstance = instance;
103 };
104
105 const createInstance = (card) => {
106 const inner = card.querySelector(".king-addons-parallax-depth-cards__card-inner");
107 if (!inner) {
108 return null;
109 }
110
111 const layers = Array.from(
112 card.querySelectorAll(".king-addons-parallax-depth-cards__layer[data-depth]")
113 );
114
115 let targetX = 0;
116 let targetY = 0;
117 let currentX = 0;
118 let currentY = 0;
119 let rafId = 0;
120 let active = false;
121 let running = false;
122
123 const resetSmoothing =
124 options.resetDuration > 0
125 ? clamp(16 / options.resetDuration, 0.02, 0.25)
126 : options.smoothing;
127
128 const setWillChange = (enabled) => {
129 if (enabled) {
130 card.classList.add("is-active");
131 inner.style.willChange = "transform";
132 layers.forEach((layer) => {
133 layer.style.willChange = "transform";
134 });
135 return;
136 }
137
138 card.classList.remove("is-active");
139 inner.style.willChange = "";
140 layers.forEach((layer) => {
141 layer.style.willChange = "";
142 });
143 };
144
145 const applyTransforms = (x, y) => {
146 if (options.tilt) {
147 const tiltX = -y * options.maxTilt * options.intensity;
148 const tiltY = x * options.maxTilt * options.intensity;
149 inner.style.transform = `rotateX(${tiltX}deg) rotateY(${tiltY}deg)`;
150 } else {
151 inner.style.transform = "";
152 }
153
154 if (options.parallax) {
155 layers.forEach((layer) => {
156 const depth = toNumber(layer.dataset.depth, 0);
157 const offsetX = x * options.depthStrength * depth * options.intensity;
158 const offsetY = y * options.depthStrength * depth * options.intensity;
159 layer.style.transform = `translate3d(${offsetX}px, ${offsetY}px, 0)`;
160 });
161 } else {
162 layers.forEach((layer) => {
163 layer.style.transform = "";
164 });
165 }
166 };
167
168 const tick = () => {
169 const smoothing = active ? options.smoothing : resetSmoothing;
170 currentX += (targetX - currentX) * smoothing;
171 currentY += (targetY - currentY) * smoothing;
172
173 applyTransforms(currentX, currentY);
174
175 const isResting =
176 Math.abs(targetX - currentX) < 0.001 &&
177 Math.abs(targetY - currentY) < 0.001 &&
178 !active;
179
180 if (isResting) {
181 setWillChange(false);
182 running = false;
183 return;
184 }
185
186 rafId = window.requestAnimationFrame(tick);
187 };
188
189 const start = () => {
190 if (running) {
191 return;
192 }
193 running = true;
194 rafId = window.requestAnimationFrame(tick);
195 };
196
197 const update = (event) => {
198 const pointer = getPointer(event, card);
199 targetX = pointer.x;
200 targetY = pointer.y;
201 if (!active) {
202 active = true;
203 setWillChange(true);
204 }
205 start();
206 };
207
208 const reset = () => {
209 active = false;
210 targetX = 0;
211 targetY = 0;
212 start();
213 };
214
215 const destroy = () => {
216 window.cancelAnimationFrame(rafId);
217 setWillChange(false);
218 targetX = 0;
219 targetY = 0;
220 currentX = 0;
221 currentY = 0;
222 applyTransforms(0, 0);
223 };
224
225 return { update, reset, destroy };
226 };
227
228 cards.forEach((card) => {
229 const instance = createInstance(card);
230 if (!instance) {
231 return;
232 }
233
234 instances.set(card, instance);
235
236 if (options.trigger === "always") {
237 return;
238 }
239
240 card.addEventListener("pointerenter", (event) => {
241 setActiveInstance(instance);
242 instance.update(event);
243 });
244
245 card.addEventListener("pointermove", (event) => {
246 instance.update(event);
247 });
248
249 card.addEventListener("pointerleave", () => {
250 instance.reset();
251 });
252 });
253
254 if (options.trigger === "always") {
255 wrapper.addEventListener("pointermove", (event) => {
256 const card = event.target.closest(".king-addons-parallax-depth-cards__card");
257 if (!card || !wrapper.contains(card)) {
258 resetActive();
259 return;
260 }
261
262 const instance = instances.get(card);
263 if (!instance) {
264 return;
265 }
266
267 setActiveInstance(instance);
268 instance.update(event);
269 });
270
271 wrapper.addEventListener("pointerleave", () => {
272 resetActive();
273 });
274 }
275 });
276 };
277
278 if (window.elementorFrontend && elementorFrontend.hooks) {
279 $(window).on("elementor/frontend/init", () => {
280 elementorFrontend.hooks.addAction(
281 "frontend/element_ready/king-addons-parallax-depth-cards.default",
282 ($scope) => {
283 initParallaxDepthCards($scope[0]);
284 }
285 );
286 });
287 }
288
289 if (document.readyState === "loading") {
290 document.addEventListener("DOMContentLoaded", () => initParallaxDepthCards(document));
291 } else {
292 initParallaxDepthCards(document);
293 }
294 })(jQuery);
295