PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.49
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.49
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 / features / Animated_Gradient_Mesh_Background / script.js

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

488 lines 20.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (() => {
2 const PRESETS = {
3 aurora: {
4 base: "#0b1020",
5 colors: ["#0b1020", "#1c3c68", "#3dd6d0"],
6 gradient: "linear-gradient(135deg, #0b1020 0%, #1c3c68 45%, #3dd6d0 100%)",
7 mesh: "radial-gradient(circle at 15% 20%, rgba(61, 214, 208, 0.65) 0%, rgba(61, 214, 208, 0) 55%), radial-gradient(circle at 80% 25%, rgba(88, 120, 255, 0.6) 0%, rgba(88, 120, 255, 0) 55%), radial-gradient(circle at 45% 85%, rgba(8, 14, 30, 0.9) 0%, rgba(8, 14, 30, 0) 60%)",
8 },
9 sunset: {
10 base: "#1d0c08",
11 colors: ["#2c0f09", "#ff7a59", "#ffd082"],
12 gradient: "linear-gradient(120deg, #2c0f09 0%, #ff7a59 45%, #ffd082 100%)",
13 mesh: "radial-gradient(circle at 20% 25%, rgba(255, 122, 89, 0.65) 0%, rgba(255, 122, 89, 0) 60%), radial-gradient(circle at 80% 30%, rgba(255, 208, 130, 0.6) 0%, rgba(255, 208, 130, 0) 60%), radial-gradient(circle at 50% 80%, rgba(55, 15, 8, 0.85) 0%, rgba(55, 15, 8, 0) 60%)",
14 },
15 ocean: {
16 base: "#061a2e",
17 colors: ["#061a2e", "#0d5c7f", "#39d2c0"],
18 gradient: "linear-gradient(140deg, #061a2e 0%, #0d5c7f 45%, #39d2c0 100%)",
19 mesh: "radial-gradient(circle at 18% 30%, rgba(57, 210, 192, 0.6) 0%, rgba(57, 210, 192, 0) 60%), radial-gradient(circle at 80% 20%, rgba(13, 92, 127, 0.7) 0%, rgba(13, 92, 127, 0) 60%), radial-gradient(circle at 50% 85%, rgba(5, 16, 34, 0.9) 0%, rgba(5, 16, 34, 0) 60%)",
20 },
21 "violet-mist": {
22 base: "#190c2d",
23 colors: ["#1c0d33", "#7a3bff", "#f4b9ff"],
24 gradient: "linear-gradient(135deg, #1c0d33 0%, #7a3bff 45%, #f4b9ff 100%)",
25 mesh: "radial-gradient(circle at 20% 25%, rgba(244, 185, 255, 0.6) 0%, rgba(244, 185, 255, 0) 60%), radial-gradient(circle at 80% 30%, rgba(122, 59, 255, 0.6) 0%, rgba(122, 59, 255, 0) 60%), radial-gradient(circle at 45% 80%, rgba(20, 8, 40, 0.9) 0%, rgba(20, 8, 40, 0) 60%)",
26 },
27 "lime-neon": {
28 base: "#0a1506",
29 colors: ["#0a1506", "#59ff6f", "#d7ff5c"],
30 gradient: "linear-gradient(135deg, #0a1506 0%, #59ff6f 45%, #d7ff5c 100%)",
31 mesh: "radial-gradient(circle at 25% 25%, rgba(89, 255, 111, 0.6) 0%, rgba(89, 255, 111, 0) 60%), radial-gradient(circle at 75% 30%, rgba(215, 255, 92, 0.6) 0%, rgba(215, 255, 92, 0) 60%), radial-gradient(circle at 50% 85%, rgba(10, 21, 6, 0.85) 0%, rgba(10, 21, 6, 0) 60%)",
32 },
33 "mono-glass": {
34 base: "#0f1115",
35 colors: ["#0f1115", "#3d3f44", "#a8aab0"],
36 gradient: "linear-gradient(135deg, #0f1115 0%, #3d3f44 45%, #a8aab0 100%)",
37 mesh: "radial-gradient(circle at 20% 30%, rgba(168, 170, 176, 0.55) 0%, rgba(168, 170, 176, 0) 60%), radial-gradient(circle at 80% 25%, rgba(61, 63, 68, 0.6) 0%, rgba(61, 63, 68, 0) 60%), radial-gradient(circle at 50% 80%, rgba(15, 17, 22, 0.9) 0%, rgba(15, 17, 22, 0) 60%)",
38 },
39 "forest-dew": {
40 base: "#0a1a12",
41 colors: ["#0a1a12", "#1e5a3a", "#7ee8a5"],
42 gradient: "linear-gradient(135deg, #0a1a12 0%, #1e5a3a 45%, #7ee8a5 100%)",
43 mesh: "radial-gradient(circle at 20% 25%, rgba(126, 232, 165, 0.6) 0%, rgba(126, 232, 165, 0) 60%), radial-gradient(circle at 75% 35%, rgba(30, 90, 58, 0.7) 0%, rgba(30, 90, 58, 0) 60%), radial-gradient(circle at 50% 80%, rgba(10, 26, 18, 0.9) 0%, rgba(10, 26, 18, 0) 60%)",
44 },
45 "candy-pop": {
46 base: "#2d0a2a",
47 colors: ["#2d0a2a", "#ff6b9d", "#ffc857"],
48 gradient: "linear-gradient(135deg, #2d0a2a 0%, #ff6b9d 45%, #ffc857 100%)",
49 mesh: "radial-gradient(circle at 25% 20%, rgba(255, 107, 157, 0.65) 0%, rgba(255, 107, 157, 0) 60%), radial-gradient(circle at 80% 30%, rgba(255, 200, 87, 0.6) 0%, rgba(255, 200, 87, 0) 60%), radial-gradient(circle at 50% 85%, rgba(45, 10, 42, 0.9) 0%, rgba(45, 10, 42, 0) 60%)",
50 },
51 "midnight-blue": {
52 base: "#050a1a",
53 colors: ["#050a1a", "#0a2463", "#247ba0"],
54 gradient: "linear-gradient(135deg, #050a1a 0%, #0a2463 45%, #247ba0 100%)",
55 mesh: "radial-gradient(circle at 20% 30%, rgba(36, 123, 160, 0.6) 0%, rgba(36, 123, 160, 0) 60%), radial-gradient(circle at 80% 20%, rgba(10, 36, 99, 0.7) 0%, rgba(10, 36, 99, 0) 60%), radial-gradient(circle at 50% 85%, rgba(5, 10, 26, 0.9) 0%, rgba(5, 10, 26, 0) 60%)",
56 },
57 "rose-gold": {
58 base: "#1a0f0f",
59 colors: ["#1a0f0f", "#b76e79", "#f7d1ba"],
60 gradient: "linear-gradient(135deg, #1a0f0f 0%, #b76e79 45%, #f7d1ba 100%)",
61 mesh: "radial-gradient(circle at 25% 25%, rgba(247, 209, 186, 0.6) 0%, rgba(247, 209, 186, 0) 60%), radial-gradient(circle at 75% 30%, rgba(183, 110, 121, 0.65) 0%, rgba(183, 110, 121, 0) 60%), radial-gradient(circle at 50% 80%, rgba(26, 15, 15, 0.9) 0%, rgba(26, 15, 15, 0) 60%)",
62 },
63 };
64
65 const DIRECTION_MAP = {
66 "left-right": { from: [0, 50], to: [100, 50] },
67 "right-left": { from: [100, 50], to: [0, 50] },
68 "top-bottom": { from: [50, 0], to: [50, 100] },
69 "bottom-top": { from: [50, 100], to: [50, 0] },
70 "diag-lr": { from: [0, 0], to: [100, 100] },
71 "diag-rl": { from: [100, 0], to: [0, 100] },
72 };
73
74 const supportsCssVars = () =>
75 window.CSS && CSS.supports && CSS.supports("--kng-agm-test: 0");
76 const supportsGradients = () =>
77 window.CSS && CSS.supports && CSS.supports("background-image", "linear-gradient(#000, #fff)");
78
79 const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
80
81 const parsePayload = (element) => {
82 const raw = element.getAttribute("data-kng-agm");
83 if (!raw) {
84 return null;
85 }
86 try {
87 return JSON.parse(raw);
88 } catch (error) {
89 return null;
90 }
91 };
92
93 const isEditor = () =>
94 Boolean(window.elementorFrontend && elementorFrontend.isEditMode && elementorFrontend.isEditMode());
95
96 const getPreset = (key) => PRESETS[key] || PRESETS.aurora;
97
98 const parseColor = (value) => {
99 if (!value || typeof value !== "string") {
100 return null;
101 }
102 const hex = value.trim();
103 if (hex.startsWith("#")) {
104 const clean = hex.replace("#", "");
105 if (clean.length === 3) {
106 const r = parseInt(clean[0] + clean[0], 16);
107 const g = parseInt(clean[1] + clean[1], 16);
108 const b = parseInt(clean[2] + clean[2], 16);
109 return { r, g, b };
110 }
111 if (clean.length === 6) {
112 const r = parseInt(clean.slice(0, 2), 16);
113 const g = parseInt(clean.slice(2, 4), 16);
114 const b = parseInt(clean.slice(4, 6), 16);
115 return { r, g, b };
116 }
117 }
118
119 const rgbMatch = value.match(/rgba?\(([^)]+)\)/i);
120 if (rgbMatch) {
121 const parts = rgbMatch[1].split(",").map((part) => parseFloat(part.trim()));
122 if (parts.length >= 3) {
123 return { r: parts[0], g: parts[1], b: parts[2] };
124 }
125 }
126 return null;
127 };
128
129 const toRgba = (value, alpha) => {
130 const parsed = parseColor(value);
131 if (!parsed) {
132 return value;
133 }
134 return `rgba(${parsed.r}, ${parsed.g}, ${parsed.b}, ${alpha})`;
135 };
136
137 const buildGradient = (colors) =>
138 `linear-gradient(135deg, ${colors[0]} 0%, ${colors[1]} 45%, ${colors[2]} 100%)`;
139
140 const buildMesh = (colors) => {
141 const primary = toRgba(colors[0], 0.6);
142 const secondary = toRgba(colors[1], 0.6);
143 const deep = toRgba(colors[2], 0.9);
144 const primaryClear = toRgba(colors[0], 0);
145 const secondaryClear = toRgba(colors[1], 0);
146 const deepClear = toRgba(colors[2], 0);
147 return `radial-gradient(circle at 20% 25%, ${primary} 0%, ${primaryClear} 60%), radial-gradient(circle at 80% 30%, ${secondary} 0%, ${secondaryClear} 60%), radial-gradient(circle at 50% 85%, ${deep} 0%, ${deepClear} 60%)`;
148 };
149
150 const resolveColors = (layer, preset) => {
151 if (layer && Array.isArray(layer.colors) && layer.colors.length >= 3) {
152 return layer.colors;
153 }
154 return preset.colors;
155 };
156
157 const buildLayerBackground = (layer, preset) => {
158 const colors = resolveColors(layer, preset);
159 return layer.type === "mesh" ? buildMesh(colors) : buildGradient(colors);
160 };
161
162 const speedToDuration = (speed) => {
163 const safe = Math.max(1, Number(speed) || 40);
164 return `${Math.max(4, Math.round(400 / safe))}s`;
165 };
166
167 const getTarget = (element) => {
168 if (element.classList.contains("elementor-column")) {
169 return element.querySelector(".elementor-widget-wrap") || element;
170 }
171 return element;
172 };
173
174 const clearLayers = (target) => {
175 target.querySelectorAll(".kng-agm-layer").forEach((layer) => layer.remove());
176 const exportPanel = target.querySelector(".kng-agm-export");
177 if (exportPanel) {
178 exportPanel.remove();
179 }
180 };
181
182 const isMobile = () => window.matchMedia("(max-width: 767px)").matches;
183 const isTablet = () => window.matchMedia("(max-width: 1024px)").matches;
184
185 const shouldDisableForDevice = (payload) => {
186 if (payload.disable?.mobile && isMobile()) {
187 return true;
188 }
189 if (payload.disable?.tablet && !isMobile() && isTablet()) {
190 return true;
191 }
192 return false;
193 };
194
195 const applyFallback = (target, preset) => {
196 target.style.backgroundColor = preset.base;
197 target.style.backgroundImage = "none";
198 };
199
200 const buildLayerElement = (layer, payload) => {
201 const preset = getPreset(layer.preset || payload.preset);
202 const element = document.createElement("span");
203 element.className = "kng-agm-layer";
204 element.style.backgroundImage = buildLayerBackground(layer, preset);
205 element.style.opacity = layer.opacity ?? 0.7;
206 element.style.filter = `blur(${layer.blur || 0}px)`;
207 element.style.backgroundSize = `${layer.size || 200}% ${layer.size || 200}%`;
208 element.style.backgroundPosition = "0% 50%";
209 element.style.mixBlendMode = layer.blendMode || payload.blendMode || "normal";
210 element.style.animationDuration = speedToDuration(layer.speed || payload.speed || 40);
211 element.style.animationTimingFunction = "ease-in-out";
212 element.style.animationIterationCount = "infinite";
213 element.dataset.direction = layer.direction || payload.direction || "left-right";
214 if (!payload.animate || (payload.scroll && payload.scroll.mode !== "off")) {
215 element.classList.add("is-static");
216 element.style.animation = "none";
217 }
218 return element;
219 };
220
221 const buildExportCss = (payload) => {
222 const preset = getPreset(payload.preset || "aurora");
223 const type = payload.type === "mesh" ? "mesh" : "gradient";
224 const baseGradient = type === "mesh" ? preset.mesh : preset.gradient;
225 const direction = payload.direction || "left-right";
226 const range = DIRECTION_MAP[direction] || DIRECTION_MAP["left-right"];
227 const duration = speedToDuration(payload.speed || 40);
228 const opacity = payload.opacity ?? 1;
229 const blur = payload.blur ?? 0;
230 const blendMode = payload.blendMode || "normal";
231 const animate = payload.animate && (!payload.scroll || payload.scroll.mode === "off");
232 const noise = payload.noise || {};
233
234 let css = "";
235
236 if (payload.export?.includeFallback) {
237 css += `.kng-animated-bg{background:${preset.base};}\n`;
238 }
239
240 css += `.kng-animated-bg{position:relative;overflow:hidden;}\n`;
241 css += `.kng-animated-bg::before{content:\"\";position:absolute;inset:0;background-color:${preset.base};background-image:${baseGradient};background-size:200% 200%;background-position:${range.from[0]}% ${range.from[1]}%;opacity:${opacity};filter:blur(${blur}px);mix-blend-mode:${blendMode};}\n`;
242
243 if (animate) {
244 css += `.kng-animated-bg::before{animation:kng-agm-shift ${duration} ease-in-out infinite;}\n`;
245 }
246
247 if (noise.enabled) {
248 css += `.kng-animated-bg::after{content:\"\";position:absolute;inset:0;pointer-events:none;opacity:${noise.amount ?? 0.18};background-image:repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.14) 1px, transparent 1px, transparent 3px),repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.08) 1px, transparent 1px, transparent 4px);mix-blend-mode:soft-light;}\n`;
249 }
250
251 css += `@keyframes kng-agm-shift{0%{background-position:${range.from[0]}% ${range.from[1]}%;}100%{background-position:${range.to[0]}% ${range.to[1]}%;}}\n`;
252
253 if (payload.export?.includeReducedMotion) {
254 css += `@media (prefers-reduced-motion: reduce){.kng-animated-bg::before{animation:none;}}\n`;
255 }
256
257 return css.trim();
258 };
259
260 const initExportPanel = (target, payload) => {
261 if (!payload.export?.enabled || !isEditor()) {
262 return;
263 }
264 const css = buildExportCss(payload);
265 if (!css) {
266 return;
267 }
268
269 const panel = document.createElement("div");
270 panel.className = "kng-agm-export";
271 panel.innerHTML = `<button type=\"button\">Copy CSS</button><textarea readonly>${css}</textarea>`;
272
273 target.insertBefore(panel, target.firstChild);
274
275 const button = panel.querySelector("button");
276 const textarea = panel.querySelector("textarea");
277 if (!button || !textarea) {
278 return;
279 }
280
281 button.addEventListener("click", () => {
282 textarea.select();
283 try {
284 navigator.clipboard?.writeText(textarea.value);
285 } catch (error) {
286 document.execCommand("copy");
287 }
288 button.textContent = "Copied";
289 setTimeout(() => {
290 button.textContent = "Copy CSS";
291 }, 1200);
292 });
293 };
294
295 const scrollRegistry = new Map();
296 let scrollBound = false;
297 let rafPending = false;
298
299 const getScrollProgress = (element, mode, start, end) => {
300 const rangeStart = clamp((start || 0) / 100, 0, 1);
301 const rangeEnd = clamp((end || 100) / 100, 0, 1);
302 let raw = 0;
303
304 if (mode === "page") {
305 const scrollTop = window.pageYOffset || document.documentElement.scrollTop || 0;
306 const maxScroll = Math.max(document.documentElement.scrollHeight - window.innerHeight, 1);
307 raw = scrollTop / maxScroll;
308 } else {
309 const rect = element.getBoundingClientRect();
310 const viewHeight = window.innerHeight || 1;
311 const total = rect.height + viewHeight;
312 raw = total > 0 ? (viewHeight - rect.top) / total : 0;
313 }
314
315 if (rangeEnd <= rangeStart) {
316 return clamp(raw, 0, 1);
317 }
318
319 return clamp((raw - rangeStart) / (rangeEnd - rangeStart), 0, 1);
320 };
321
322 const applyEasing = (value, easing) => {
323 if (easing === "ease") {
324 return value * value * (3 - 2 * value);
325 }
326 if (easing === "parallax") {
327 return clamp(value * 1.15, 0, 1);
328 }
329 return value;
330 };
331
332 const resolvePosition = (direction, progress) => {
333 const map = DIRECTION_MAP[direction] || DIRECTION_MAP["left-right"];
334 const x = map.from[0] + (map.to[0] - map.from[0]) * progress;
335 const y = map.from[1] + (map.to[1] - map.from[1]) * progress;
336 return { x, y };
337 };
338
339 const updateScrollEntry = (entry) => {
340 const { element, payload, layers } = entry;
341 if (!element.isConnected) {
342 scrollRegistry.delete(element);
343 return;
344 }
345
346 const scroll = payload.scroll || {};
347 let progress = getScrollProgress(element, scroll.mode, scroll.start, scroll.end);
348 progress = applyEasing(progress, scroll.easing || "linear");
349
350 if (scroll.mode === "hybrid") {
351 progress = clamp(progress * 0.35, 0, 1);
352 }
353
354 const baseDirection = payload.direction || "left-right";
355 const basePos = resolvePosition(baseDirection, progress);
356 element.style.setProperty("--kng-agm-scroll-x", `${basePos.x}%`);
357 element.style.setProperty("--kng-agm-scroll-y", `${basePos.y}%`);
358
359 layers.forEach((layer) => {
360 const dir = layer.dataset.direction || baseDirection;
361 const pos = resolvePosition(dir, progress);
362 layer.style.backgroundPosition = `${pos.x}% ${pos.y}%`;
363 });
364 };
365
366 const updateScrollAll = () => {
367 rafPending = false;
368 scrollRegistry.forEach(updateScrollEntry);
369 };
370
371 const scheduleScrollUpdate = () => {
372 if (rafPending) {
373 return;
374 }
375 rafPending = true;
376 window.requestAnimationFrame(updateScrollAll);
377 };
378
379 const registerScroll = (element, payload, layers) => {
380 scrollRegistry.set(element, { element, payload, layers });
381 if (!scrollBound) {
382 window.addEventListener("scroll", scheduleScrollUpdate, { passive: true });
383 window.addEventListener("resize", scheduleScrollUpdate);
384 scrollBound = true;
385 }
386 scheduleScrollUpdate();
387 };
388
389 const unregisterScroll = (element) => {
390 scrollRegistry.delete(element);
391 };
392
393 const initElement = (element) => {
394 const payload = parsePayload(element);
395 const target = getTarget(element);
396
397 if (!payload || !payload.enabled) {
398 element.classList.remove("kng-agm-scroll-sync");
399 unregisterScroll(element);
400 clearLayers(target);
401 return;
402 }
403
404 if (shouldDisableForDevice(payload)) {
405 element.classList.remove("kng-agm-scroll-sync");
406 unregisterScroll(element);
407 clearLayers(target);
408 return;
409 }
410
411 const preset = getPreset(payload.preset || "aurora");
412 if (!supportsCssVars() || !supportsGradients()) {
413 element.classList.remove("kng-agm-scroll-sync");
414 unregisterScroll(element);
415 applyFallback(target, preset);
416 return;
417 }
418
419 clearLayers(target);
420
421 let layersData = Array.isArray(payload.layers) ? payload.layers.slice() : [];
422 if (payload.adaptive && isMobile()) {
423 layersData = layersData.slice(0, 2);
424 target.style.setProperty("--kng-agm-noise-opacity", Math.min(payload.noise?.amount ?? 0.18, 0.1));
425 target.style.setProperty("--kng-agm-blur", "0px");
426 }
427
428 const layers = [];
429 if (layersData.length) {
430 const fragment = document.createDocumentFragment();
431 layersData.forEach((layer) => {
432 const elementLayer = buildLayerElement(layer, payload);
433 layers.push(elementLayer);
434 fragment.appendChild(elementLayer);
435 });
436 target.insertBefore(fragment, target.firstChild);
437 }
438
439 const scrollMode = payload.scroll?.mode || "off";
440 const allowScroll = scrollMode !== "off" && !(payload.scroll?.reduceMobile && isMobile());
441 if (allowScroll) {
442 element.classList.add("kng-agm-scroll-sync");
443 registerScroll(element, payload, layers);
444 } else {
445 element.classList.remove("kng-agm-scroll-sync");
446 unregisterScroll(element);
447 }
448
449 initExportPanel(target, payload);
450 };
451
452 const initAll = (root) => {
453 if (!root) {
454 return;
455 }
456 const elements = root.querySelectorAll("[data-kng-agm]");
457 elements.forEach(initElement);
458 };
459
460 const initGlobal = () => {
461 initAll(document);
462
463 if (window.elementorFrontend && elementorFrontend.hooks) {
464 const handler = ($scope) => {
465 const scopeElement = $scope[0] || $scope;
466 if (!scopeElement) {
467 return;
468 }
469 if (scopeElement.hasAttribute && scopeElement.hasAttribute("data-kng-agm")) {
470 initElement(scopeElement);
471 } else {
472 initAll(scopeElement);
473 }
474 };
475
476 elementorFrontend.hooks.addAction("frontend/element_ready/section", handler);
477 elementorFrontend.hooks.addAction("frontend/element_ready/container", handler);
478 elementorFrontend.hooks.addAction("frontend/element_ready/column", handler);
479 }
480 };
481
482 if (document.readyState === "loading") {
483 document.addEventListener("DOMContentLoaded", initGlobal);
484 } else {
485 initGlobal();
486 }
487 })();
488