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 / particle-text / frontend.js

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

257 lines 10.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2
3 var _typoKeys = {
4 family:'font-family', weight:'font-weight', style:'font-style',
5 decoration:'text-decoration', transform:'text-transform',
6 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
7 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
8 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
9 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m'
10 };
11 function typoCssVarsForEl(el, obj, prefix) {
12 if (!obj || typeof obj !== 'object') return;
13 Object.keys(_typoKeys).forEach(function (k) {
14 var v = obj[k];
15 if (v === undefined || v === '' || v === null) return;
16 if (k === 'sizeDesktop' || k === 'sizeTablet' || k === 'sizeMobile') v = v + (obj.sizeUnit || 'px');
17 else if (k === 'lineHeightDesktop' || k === 'lineHeightTablet' || k === 'lineHeightMobile') v = v + (obj.lineHeightUnit || '');
18 else if (k === 'letterSpacingDesktop' || k === 'letterSpacingTablet' || k === 'letterSpacingMobile') v = v + (obj.letterSpacingUnit || 'px');
19 else if (k === 'wordSpacingDesktop' || k === 'wordSpacingTablet' || k === 'wordSpacingMobile') v = v + (obj.wordSpacingUnit || 'px');
20 el.style.setProperty(prefix + _typoKeys[k], String(v));
21 });
22 }
23
24 function easeOut(t) { return 1 - Math.pow(1 - t, 3); }
25 function easeIn(t) { return t * t * t; }
26 function linear(t) { return t; }
27 function spring(t) { var c4 = (2*Math.PI)/3; return t===0?0:t===1?1:Math.pow(2,-10*t)*Math.sin((t*10-0.75)*c4)+1; }
28
29 function getEaseFn(name) {
30 return { 'ease-out':easeOut, 'ease-in':easeIn, 'linear':linear, 'spring':spring }[name] || easeOut;
31 }
32
33 function hexToRgb(hex) {
34 var r=parseInt((hex||'#7c3aed').replace('#','').substr(0,2),16);
35 var g=parseInt((hex||'#7c3aed').replace('#','').substr(2,2),16);
36 var b=parseInt((hex||'#7c3aed').replace('#','').substr(4,2),16);
37 return [r,g,b];
38 }
39
40 function lerpRgb(c1, c2, t) {
41 return [
42 Math.round(c1[0]+(c2[0]-c1[0])*t),
43 Math.round(c1[1]+(c2[1]-c1[1])*t),
44 Math.round(c1[2]+(c2[2]-c1[2])*t)
45 ];
46 }
47
48 function initParticleText(appEl) {
49 var opts;
50 try { opts = JSON.parse(appEl.getAttribute('data-opts') || '{}'); } catch(e){ opts={}; }
51
52 var a = {
53 text: opts.text || 'BLOCKENBERG',
54 subtext: opts.subtext || '',
55 sampleFontSize:opts.sampleFontSize|| 100,
56 fontFamily: opts.fontFamily || 'system-ui, sans-serif',
57 fontWeight: opts.fontWeight || '900',
58 particleSize: opts.particleSize || 2,
59 particleGap: opts.particleGap || 4,
60 particleColor: opts.particleColor || '#7c3aed',
61 particleColor2:opts.particleColor2||'#ec4899',
62 useGradient: opts.useGradient !== false,
63 bgColor: opts.bgColor || '#0a0a0f',
64 subtextColor: opts.subtextColor || '#94a3b8',
65 subtextSize: opts.subtextSize || 18,
66 canvasHeight: opts.canvasHeight || 300,
67 scatterRadius: opts.scatterRadius || 500,
68 enterDuration: opts.enterDuration || 1200,
69 easing: opts.easing || 'ease-out',
70 hoverEffect: opts.hoverEffect !== false,
71 hoverRadius: opts.hoverRadius || 80,
72 paddingTop: opts.paddingTop || 80,
73 paddingBottom: opts.paddingBottom || 80,
74 subtextTypo: opts.subtextTypo || {}
75 };
76
77 /* Section */
78 var section = document.createElement('section');
79 section.className = 'bkbg-pt-section';
80 section.style.setProperty('--bkbg-pt-bg', a.bgColor);
81 section.style.setProperty('--bkbg-pt-pt', a.paddingTop+'px');
82 section.style.setProperty('--bkbg-pt-pb', a.paddingBottom+'px');
83 section.style.setProperty('--bkbg-pt-sub-size', a.subtextSize+'px');
84 section.style.setProperty('--bkbg-pt-sub-color',a.subtextColor);
85
86 typoCssVarsForEl(section, a.subtextTypo, '--bkbg-pt-st-');
87
88 var wrap = document.createElement('div');
89 wrap.className = 'bkbg-pt-canvas-wrap';
90
91 var canvas = document.createElement('canvas');
92 canvas.className = 'bkbg-pt-canvas';
93 canvas.style.height = a.canvasHeight + 'px';
94
95 wrap.appendChild(canvas);
96 section.appendChild(wrap);
97
98 var subtextEl = null;
99 if (a.subtext) {
100 subtextEl = document.createElement('p');
101 subtextEl.className = 'bkbg-pt-subtext';
102 subtextEl.textContent = a.subtext;
103 section.appendChild(subtextEl);
104 }
105
106 appEl.replaceWith(section);
107
108 /* ------- Canvas setup ------- */
109 var ctx = canvas.getContext('2d');
110 var pixelRatio = window.devicePixelRatio || 1;
111 var W, H;
112 var particles = [];
113 var easeFn = getEaseFn(a.easing);
114 var c1 = hexToRgb(a.particleColor);
115 var c2 = hexToRgb(a.particleColor2);
116 var rafId = null;
117 var startTime = null;
118 var assembled = false;
119 var mx = -9999, my = -9999;
120
121 function sampleText() {
122 /* Offscreen canvas to sample text pixels */
123 var off = document.createElement('canvas');
124 var fs = a.sampleFontSize;
125 off.width = Math.max(800, W);
126 off.height = fs * 2.5;
127 var oc = off.getContext('2d');
128 oc.clearRect(0, 0, off.width, off.height);
129 oc.font = a.fontWeight + ' ' + fs + 'px ' + a.fontFamily;
130 oc.fillStyle = '#fff';
131 oc.textAlign = 'center';
132 oc.textBaseline = 'middle';
133 oc.fillText(a.text, off.width / 2, off.height / 2);
134
135 var data = oc.getImageData(0, 0, off.width, off.height).data;
136 var pts = [];
137 var gap = a.particleGap;
138 for (var y = 0; y < off.height; y += gap) {
139 for (var x = 0; x < off.width; x += gap) {
140 var idx = (y * off.width + x) * 4;
141 if (data[idx + 3] > 128) {
142 /* Map from off-canvas coords to main canvas coords */
143 var cx2 = (x / off.width) * W;
144 var cy2 = (y / off.height) * H;
145 pts.push({ tx: cx2, ty: cy2 });
146 }
147 }
148 }
149 return pts;
150 }
151
152 function buildParticles() {
153 var targets = sampleText();
154 particles = targets.map(function(p) {
155 var angle = Math.random() * Math.PI * 2;
156 var dist = Math.random() * a.scatterRadius + 50;
157 return {
158 tx: p.tx, ty: p.ty,
159 x: p.tx + Math.cos(angle) * dist,
160 y: p.ty + Math.sin(angle) * dist,
161 ox: p.tx, oy: p.ty, /* original target */
162 scatterX: p.tx + Math.cos(angle) * dist,
163 scatterY: p.ty + Math.sin(angle) * dist,
164 color: Math.random()
165 };
166 });
167 }
168
169 function resize() {
170 W = wrap.clientWidth || 800;
171 H = a.canvasHeight;
172 canvas.width = W * pixelRatio;
173 canvas.height = H * pixelRatio;
174 canvas.style.width = W + 'px';
175 canvas.style.height = H + 'px';
176 ctx.scale(pixelRatio, pixelRatio);
177 buildParticles();
178 }
179
180 function drawFrame(now) {
181 ctx.clearRect(0, 0, W, H);
182
183 var elapsed = now - (startTime || now);
184 var t = Math.min(1, elapsed / a.enterDuration);
185 var et = easeFn(t);
186
187 for (var i = 0; i < particles.length; i++) {
188 var p = particles[i];
189
190 if (!assembled) {
191 p.x = p.scatterX + (p.tx - p.scatterX) * et;
192 p.y = p.scatterY + (p.ty - p.scatterY) * et;
193 }
194
195 /* Hover repulsion */
196 if (a.hoverEffect && assembled) {
197 var dx = p.x - mx, dy = p.y - my;
198 var dist = Math.sqrt(dx*dx + dy*dy);
199 if (dist < a.hoverRadius) {
200 var force = (a.hoverRadius - dist) / a.hoverRadius;
201 p.x += (dx / dist) * force * 30;
202 p.y += (dy / dist) * force * 30;
203 } else {
204 p.x += (p.tx - p.x) * 0.12;
205 p.y += (p.ty - p.y) * 0.12;
206 }
207 }
208
209 var rgb = a.useGradient ? lerpRgb(c1, c2, p.color) : c1;
210 var alpha = assembled ? 1 : (0.4 + et * 0.6);
211 ctx.fillStyle = 'rgba('+rgb[0]+','+rgb[1]+','+rgb[2]+','+alpha+')';
212 ctx.fillRect(p.x - a.particleSize/2, p.y - a.particleSize/2, a.particleSize, a.particleSize);
213 }
214
215 if (!assembled && t >= 1) {
216 assembled = true;
217 if (subtextEl) subtextEl.classList.add('bkbg-pt-visible');
218 }
219 }
220
221 function loop(now) {
222 if (!startTime) startTime = now;
223 drawFrame(now);
224 rafId = requestAnimationFrame(loop);
225 }
226
227 /* Start when visible */
228 resize();
229 window.addEventListener('resize', function() { resize(); if (assembled) { assembled = false; startTime = null; } });
230
231 if ('IntersectionObserver' in window) {
232 var obs = new IntersectionObserver(function(entries) {
233 if (entries[0].isIntersecting) {
234 if (!rafId) { startTime = null; assembled = false; rafId = requestAnimationFrame(loop); }
235 } else {
236 if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
237 }
238 }, { threshold: 0.1 });
239 obs.observe(section);
240 } else {
241 rafId = requestAnimationFrame(loop);
242 }
243
244 /* Mouse tracking for hover effect */
245 if (a.hoverEffect) {
246 canvas.addEventListener('mousemove', function(e) {
247 var rect = canvas.getBoundingClientRect();
248 mx = e.clientX - rect.left;
249 my = e.clientY - rect.top;
250 });
251 canvas.addEventListener('mouseleave', function() { mx = -9999; my = -9999; });
252 }
253 }
254
255 document.querySelectorAll('.bkbg-pt-app').forEach(initParticleText);
256 })();
257