PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / confetti-button / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/confetti-button/frontend.js

292 lines 11.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 document.addEventListener('DOMContentLoaded', function () {
2 function typoCssVarsForEl(typo, prefix, el) {
3 if (!typo || typeof typo !== 'object') return;
4 var map = {
5 family:'font-family', weight:'font-weight', style:'font-style',
6 decoration:'text-decoration', transform:'text-transform',
7 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
8 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
9 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
10 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m'
11 };
12 Object.keys(map).forEach(function(k) {
13 if (typo[k] !== undefined && typo[k] !== '') {
14 var v = typo[k];
15 if (['sizeDesktop','sizeTablet','sizeMobile'].indexOf(k) !== -1) v = v + (typo.sizeUnit || 'px');
16 else if (['lineHeightDesktop','lineHeightTablet','lineHeightMobile'].indexOf(k) !== -1) v = v + (typo.lineHeightUnit || '');
17 else if (['letterSpacingDesktop','letterSpacingTablet','letterSpacingMobile'].indexOf(k) !== -1) v = v + (typo.letterSpacingUnit || 'px');
18 else if (['wordSpacingDesktop','wordSpacingTablet','wordSpacingMobile'].indexOf(k) !== -1) v = v + (typo.wordSpacingUnit || 'px');
19 el.style.setProperty(prefix + map[k], String(v));
20 }
21 });
22 }
23
24 document.querySelectorAll('.bkbg-cb-app').forEach(function (root) {
25 var opts = JSON.parse(root.dataset.opts || '{}');
26 initConfettiButton(root, opts);
27 });
28 });
29
30 function initConfettiButton(root, a) {
31 var text = a.text || '🎉 Celebrate!';
32 var colors = a.colors || ['#ff6b6b','#ffd93d','#6bcb77','#4d96ff','#c77dff','#ff9f1c'];
33
34 /* wrap */
35 var wrap = document.createElement('div');
36 wrap.className = 'bkbg-cb-wrap';
37 wrap.style.cssText = 'text-align:' + (a.align || 'center') + ';padding:24px;';
38
39 /* button */
40 var isLink = a.url && a.url.length > 0;
41 var btn = document.createElement(isLink ? 'a' : 'button');
42 btn.className = 'bkbg-cb-btn';
43
44 if (isLink) {
45 btn.href = a.url;
46 if (a.openInNewTab) { btn.target = '_blank'; btn.rel = 'noopener noreferrer'; }
47 }
48
49 var label = (a.iconBefore ? a.iconBefore + ' ' : '') + text + (a.iconAfter ? ' ' + a.iconAfter : '');
50 /* Icon before */
51 var IP = window.bkbgIconPicker;
52 var beforeType = a.iconBeforeType || 'custom-char';
53 var afterType = a.iconAfterType || 'custom-char';
54 var hasBefore = beforeType !== 'custom-char' ? true : !!a.iconBefore;
55 var hasAfter = afterType !== 'custom-char' ? true : !!a.iconAfter;
56
57 if (hasBefore) {
58 var bSpan = document.createElement('span');
59 bSpan.className = 'bkbg-cb-icon-before';
60 if (beforeType !== 'custom-char' && IP) {
61 bSpan.appendChild(IP.buildFrontendIcon(beforeType, a.iconBefore, a.iconBeforeDashicon, a.iconBeforeImageUrl, a.iconBeforeDashiconColor));
62 } else {
63 bSpan.textContent = a.iconBefore;
64 }
65 btn.appendChild(bSpan);
66 }
67 var lblSpan = document.createElement('span');
68 lblSpan.textContent = text;
69 btn.appendChild(lblSpan);
70 if (hasAfter) {
71 var aSpan = document.createElement('span');
72 aSpan.className = 'bkbg-cb-icon-after';
73 if (afterType !== 'custom-char' && IP) {
74 aSpan.appendChild(IP.buildFrontendIcon(afterType, a.iconAfter, a.iconAfterDashicon, a.iconAfterImageUrl, a.iconAfterDashiconColor));
75 } else {
76 aSpan.textContent = a.iconAfter;
77 }
78 btn.appendChild(aSpan);
79 }
80
81 var shadow = a.showShadow ? '0 8px 24px ' + (a.buttonBg || '#6366f1') + '55, 0 4px 8px rgba(0,0,0,0.15)' : 'none';
82
83 btn.style.cssText = [
84 'display:' + (a.fullWidth ? 'block' : 'inline-block'),
85 a.fullWidth ? 'width:100%' : (a.maxWidth ? 'max-width:' + a.maxWidth + 'px' : ''),
86 'background:' + (a.buttonBg || '#6366f1'),
87 'color:' + (a.buttonColor || '#ffffff'),
88 'border-radius:' + (a.buttonRadius || 12) + 'px',
89 'padding:' + (a.buttonPaddingV || 16) + 'px ' + (a.buttonPaddingH || 40) + 'px',
90 'box-shadow:' + shadow,
91 'font-family:inherit',
92 'cursor:pointer',
93 'border:none',
94 'outline:none',
95 'text-decoration:none',
96 'position:relative',
97 'overflow:hidden',
98 ].filter(Boolean).join(';');
99
100 typoCssVarsForEl(a.typoButton, '--bkcb-btn-', root);
101
102 /* hover effect */
103 btn.addEventListener('mouseenter', function () {
104 btn.style.background = a.buttonHoverBg || '#4f46e5';
105 btn.style.transform = 'translateY(-2px)';
106 });
107 btn.addEventListener('mouseleave', function () {
108 btn.style.background = a.buttonBg || '#6366f1';
109 btn.style.transform = 'translateY(0)';
110 });
111
112 /* ripple */
113 btn.addEventListener('click', function (e) {
114 var ripple = document.createElement('span');
115 ripple.className = 'bkbg-cb-ripple';
116 var size = Math.max(btn.offsetWidth, btn.offsetHeight);
117 ripple.style.width = ripple.style.height = size + 'px';
118 var rect = btn.getBoundingClientRect();
119 ripple.style.left = (e.clientX - rect.left - size / 2) + 'px';
120 ripple.style.top = (e.clientY - rect.top - size / 2) + 'px';
121 btn.appendChild(ripple);
122 setTimeout(function () { ripple.remove(); }, 600);
123 });
124
125 wrap.appendChild(btn);
126
127 /* sub text */
128 if (a.showSubText && a.subText) {
129 var sub = document.createElement('p');
130 sub.className = 'bkbg-cb-sub';
131 sub.textContent = a.subText;
132 wrap.appendChild(sub);
133 }
134
135 root.appendChild(wrap);
136
137 /* confetti engine */
138 var firing = false;
139
140 btn.addEventListener('click', function (e) {
141 if (firing && !a.repeat) return;
142 e.preventDefault(); /* prevent nav during confetti if URL set */
143
144 btn.classList.add('bkbg-cb-firing');
145 setTimeout(function () { btn.classList.remove('bkbg-cb-firing'); }, 200);
146
147 var btnRect = btn.getBoundingClientRect();
148 var originX = (btnRect.left + btnRect.width / 2) / window.innerWidth;
149 var originY = (btnRect.top + btnRect.height / 2) / window.innerHeight;
150
151 fireConfetti(a, colors, originX, originY);
152 firing = true;
153
154 setTimeout(function () {
155 firing = false;
156 if (isLink && a.url) {
157 if (a.openInNewTab) window.open(a.url, '_blank');
158 else window.location.href = a.url;
159 }
160 }, 300);
161 });
162 }
163
164 function fireConfetti(a, colors, originX, originY) {
165 var canvas = document.createElement('canvas');
166 canvas.className = 'bkbg-cb-canvas';
167 document.body.appendChild(canvas);
168 canvas.width = window.innerWidth;
169 canvas.height = window.innerHeight;
170 var ctx = canvas.getContext('2d');
171 var style = a.confettiStyle || 'burst';
172 var count = a.particleCount || 120;
173 var spread = (a.spread || 70) * Math.PI / 180;
174 var grav = a.gravity || 1;
175 var pSize = a.particleSize || 8;
176 var shapes = a.shapes || 'mixed';
177 var duration = a.duration || 3000;
178
179 var particles = [];
180
181 function randomBetween(lo, hi) { return lo + Math.random() * (hi - lo); }
182
183 function makeParticle(ox, oy, angleBase) {
184 var angle = angleBase + randomBetween(-spread / 2, spread / 2);
185 var speed = randomBetween(4, 14);
186 var shap;
187 if (shapes === 'rect') shap = 'rect';
188 else if (shapes === 'circle') shap = 'circle';
189 else if (shapes === 'star') shap = 'star';
190 else shap = Math.random() > 0.5 ? 'rect' : 'circle';
191
192 return {
193 x: ox * canvas.width,
194 y: oy * canvas.height,
195 vx: Math.cos(angle) * speed,
196 vy: Math.sin(angle) * speed,
197 w: randomBetween(pSize * 0.5, pSize * 1.5),
198 h: randomBetween(pSize * 0.5, pSize * 1.5),
199 color: colors[Math.floor(Math.random() * colors.length)],
200 shape: shap,
201 rotation: randomBetween(0, Math.PI * 2),
202 rotSpeed: randomBetween(-0.15, 0.15),
203 opacity: 1,
204 life: randomBetween(0.6, 1),
205 };
206 }
207
208 /* emit per style */
209 if (style === 'burst' || style === 'cannon') {
210 var base = style === 'cannon' ? -Math.PI / 2 : 0;
211 var spreadFull = style === 'cannon' ? spread : Math.PI * 2;
212 for (var i = 0; i < count; i++) {
213 var ang = base + (i / count) * spreadFull;
214 particles.push(makeParticle(originX, originY, ang));
215 }
216 } else if (style === 'rain') {
217 for (var i = 0; i < count; i++) {
218 particles.push(makeParticle(Math.random(), -0.05, Math.PI / 2));
219 }
220 } else if (style === 'sides') {
221 for (var i = 0; i < count; i++) {
222 var side = i % 2 === 0 ? 0 : 1;
223 particles.push(makeParticle(side, randomBetween(0.3, 0.7), side === 0 ? 0 : Math.PI));
224 }
225 } else if (style === 'spiral') {
226 for (var i = 0; i < count; i++) {
227 var ang2 = (i / count) * Math.PI * 4;
228 particles.push(makeParticle(originX, originY, ang2));
229 }
230 } else {
231 for (var i = 0; i < count; i++) {
232 particles.push(makeParticle(originX, originY, (i / count) * Math.PI * 2));
233 }
234 }
235
236 var startTime = performance.now();
237 var raf;
238
239 function drawStar(ctx, x, y, r) {
240 ctx.save();
241 ctx.beginPath();
242 for (var i = 0; i < 5; i++) {
243 var a = (i * 4 * Math.PI / 5) - Math.PI / 2;
244 if (i === 0) ctx.moveTo(x + r * Math.cos(a), y + r * Math.sin(a));
245 else ctx.lineTo(x + r * Math.cos(a), y + r * Math.sin(a));
246 }
247 ctx.closePath();
248 ctx.fill();
249 ctx.restore();
250 }
251
252 function tick(ts) {
253 var elapsed = ts - startTime;
254 ctx.clearRect(0, 0, canvas.width, canvas.height);
255
256 particles.forEach(function (p) {
257 if (p.opacity <= 0) return;
258 p.x += p.vx;
259 p.y += p.vy;
260 p.vy += grav * 0.3;
261 p.vx *= 0.99;
262 p.rotation += p.rotSpeed;
263 p.opacity = Math.max(0, p.life - elapsed / duration);
264
265 ctx.save();
266 ctx.globalAlpha = p.opacity;
267 ctx.fillStyle = p.color;
268 ctx.translate(p.x, p.y);
269 ctx.rotate(p.rotation);
270
271 if (p.shape === 'circle') {
272 ctx.beginPath();
273 ctx.arc(0, 0, p.w / 2, 0, Math.PI * 2);
274 ctx.fill();
275 } else if (p.shape === 'star') {
276 drawStar(ctx, 0, 0, p.w / 2);
277 } else {
278 ctx.fillRect(-p.w / 2, -p.h / 2, p.w, p.h);
279 }
280 ctx.restore();
281 });
282
283 if (elapsed < duration + 800) {
284 raf = requestAnimationFrame(tick);
285 } else {
286 canvas.remove();
287 }
288 }
289
290 raf = requestAnimationFrame(tick);
291 }
292