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

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

197 lines 8.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 var svgNS = 'http://www.w3.org/2000/svg';
5 var reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
6
7 document.querySelectorAll('.bkbg-hw-app').forEach(function (app) {
8 var opts = {};
9 try { opts = JSON.parse(app.dataset.opts || '{}'); } catch (e) { }
10
11 var text = opts.text || '';
12 var fontFamily = opts.fontFamily || "'Dancing Script', cursive";
13 var fontSize = opts.fontSize || 72;
14 var fontWeight = opts.fontWeight || '700';
15 var svgWidth = opts.svgWidth || 900;
16 var svgHeight = opts.svgHeight || 140;
17 var textX = opts.textX || 50;
18 var textY = opts.textY || 110;
19 var textAnchor = opts.textAnchor || 'middle';
20 var strokeWidth = opts.strokeWidth || 2;
21 var duration = opts.duration || 3;
22 var delay = opts.delay || 0.3;
23 var trigger = opts.trigger || 'scroll';
24 var fillOnComplete = opts.fillOnComplete !== false;
25 var fillDuration = opts.fillDuration || 0.8;
26 var repeatAnim = opts.repeatAnimation || false;
27 var easing = opts.easing || 'ease-in-out';
28 var textColor = opts.textColor || '#0f172a';
29 var strokeColor = opts.strokeColor || '#6366f1';
30 var align2 = opts.align2 || 'center';
31 var sectionBg = opts.sectionBg || '';
32 var showUnderline = opts.showUnderline || false;
33 var underlineColor = opts.underlineColor || '#6366f1';
34 var underlineWidth = opts.underlineWidth || 4;
35 var underlineOffsetY = opts.underlineOffsetY || 12;
36 var underlineDuration = opts.underlineDuration || 0.6;
37
38 if (sectionBg) app.style.background = sectionBg;
39
40 /* typography CSS vars */
41 if (window.typoCssVarsForEl) window.typoCssVarsForEl(opts.typoText, '--bkbg-hw-tt-', app);
42
43 // Build SVG
44 var svg = document.createElementNS(svgNS, 'svg');
45 svg.setAttribute('viewBox', '0 0 ' + svgWidth + ' ' + svgHeight);
46 svg.setAttribute('width', '100%');
47 svg.style.cssText = 'display:block;overflow:visible;max-width:' + svgWidth + 'px;' +
48 'margin:' + (align2 === 'center' ? '0 auto' : align2 === 'right' ? '0 0 0 auto' : '0');
49
50 // Text element
51 var textEl = document.createElementNS(svgNS, 'text');
52 textEl.className.baseVal = 'bkbg-hw-text';
53 textEl.setAttribute('x', textX + '%');
54 textEl.setAttribute('y', textY);
55 textEl.setAttribute('text-anchor', textAnchor);
56 textEl.setAttribute('stroke', strokeColor);
57 textEl.setAttribute('stroke-width', strokeWidth);
58 textEl.textContent = text;
59
60 svg.appendChild(textEl);
61
62 var wrap = document.createElement('div');
63 wrap.className = 'bkbg-hw-wrap';
64 wrap.style.textAlign = align2;
65 wrap.appendChild(svg);
66 app.appendChild(wrap);
67
68 // Need text in DOM before measuring
69 // Use requestAnimationFrame to let browser render
70 requestAnimationFrame(function () {
71 var len = 5000; // large fallback
72 try { len = textEl.getComputedTextLength() || 5000; } catch (e) { }
73 len = Math.max(len, textEl.getTotalLength ? textEl.getTotalLength() : len);
74
75 // Also build underline path if requested
76 var ulEl = null;
77 var ulLen = 0;
78 if (showUnderline) {
79 // Get bounding box to draw underline across actual text width
80 var bbox = { x: 0, width: svgWidth };
81 try { bbox = textEl.getBBox(); } catch (e) { }
82 var ulY = parseFloat(textY) + underlineOffsetY;
83 var ulX1 = bbox.x;
84 var ulX2 = bbox.x + bbox.width;
85
86 ulEl = document.createElementNS(svgNS, 'path');
87 ulEl.className.baseVal = 'bkbg-hw-underline';
88 ulEl.setAttribute('d', 'M ' + ulX1 + ' ' + ulY + ' L ' + ulX2 + ' ' + ulY);
89 ulEl.setAttribute('stroke', underlineColor);
90 ulEl.setAttribute('stroke-width', underlineWidth);
91 ulEl.setAttribute('stroke-linecap', 'round');
92 svg.appendChild(ulEl);
93
94 ulLen = (ulX2 - ulX1) || svgWidth;
95 }
96
97 function setupInitialState() {
98 if (reducedMotion) {
99 // Show immediately
100 textEl.setAttribute('fill', textColor);
101 textEl.setAttribute('stroke', 'none');
102 if (ulEl) {
103 ulEl.style.strokeDasharray = '';
104 ulEl.style.strokeDashoffset = '';
105 }
106 return;
107 }
108
109 // Set dash = full length = hidden
110 textEl.style.strokeDasharray = len;
111 textEl.style.strokeDashoffset = len;
112 textEl.setAttribute('fill', 'transparent');
113 textEl.style.transition = 'none';
114
115 if (ulEl) {
116 ulEl.style.strokeDasharray = ulLen;
117 ulEl.style.strokeDashoffset = ulLen;
118 ulEl.style.transition = 'none';
119 }
120 }
121
122 function startAnimation() {
123 requestAnimationFrame(function () {
124 requestAnimationFrame(function () {
125 // Draw stroke
126 textEl.style.transition = 'stroke-dashoffset ' + duration + 's ' + easing + ' ' + delay + 's';
127 textEl.style.strokeDashoffset = 0;
128
129 // Underline draws after text
130 if (ulEl) {
131 var ulDelay = delay + duration * 0.9;
132 ulEl.style.transition = 'stroke-dashoffset ' + underlineDuration + 's ease-out ' + ulDelay + 's';
133 ulEl.style.strokeDashoffset = 0;
134 }
135
136 // Fill text after stroke completes
137 if (fillOnComplete) {
138 var fillDelay = delay + duration;
139 setTimeout(function () {
140 textEl.classList.add('bkbg-hw-filled');
141 textEl.style.fill = textColor;
142 textEl.style.fillOpacity = '1';
143 textEl.style.transitionProperty = 'fill';
144 textEl.style.transitionDuration = fillDuration + 's';
145 textEl.style.transitionTimingFunction = 'ease-in-out';
146 }, fillDelay * 1000);
147 }
148 });
149 });
150 }
151
152 function resetAnimation() {
153 textEl.style.transition = 'none';
154 textEl.style.strokeDashoffset = len;
155 textEl.setAttribute('fill', 'transparent');
156 textEl.classList.remove('bkbg-hw-filled');
157 if (ulEl) {
158 ulEl.style.transition = 'none';
159 ulEl.style.strokeDashoffset = ulLen;
160 }
161 }
162
163 setupInitialState();
164
165 if (reducedMotion) return; // Don't animate for reduced motion users
166
167 if (trigger === 'load') {
168 startAnimation();
169 } else if (trigger === 'click') {
170 app.style.cursor = 'pointer';
171 var played = false;
172 app.addEventListener('click', function () {
173 if (played) resetAnimation();
174 setTimeout(startAnimation, played ? 50 : 0);
175 played = true;
176 });
177 } else {
178 // scroll (IntersectionObserver)
179 var played2 = false;
180 var io = new IntersectionObserver(function (entries) {
181 entries.forEach(function (entry) {
182 if (entry.isIntersecting && !played2) {
183 played2 = true;
184 startAnimation();
185 if (!repeatAnim) io.unobserve(app);
186 } else if (!entry.isIntersecting && repeatAnim && played2) {
187 played2 = false;
188 resetAnimation();
189 }
190 });
191 }, { threshold: 0.3 });
192 io.observe(app);
193 }
194 });
195 });
196 })();
197