PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.11
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.11
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 / progress-circle / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.11, at blocks/progress-circle/frontend.js

157 lines 6.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* Progress Circle — frontend */
2 (function () {
3 'use strict';
4
5 var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
6
7 /* ── ease helpers ────────────────────────────────────────────────────────── */
8 function easeInOut(t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; }
9 function easeOut(t) { return 1 - Math.pow(1 - t, 3); }
10 function easeIn(t) { return t * t * t; }
11 function linear(t) { return t; }
12
13 function getEaseFn(name) {
14 if (name === 'ease-out') return easeOut;
15 if (name === 'ease-in') return easeIn;
16 if (name === 'linear') return linear;
17 return easeInOut; /* ease-in-out default */
18 }
19
20 /* ── animate one ring ───────────────────────────────────────────────────── */
21 function animateRing(ring, circumference, fromOffset, toOffset, duration, easeFn, onProgress) {
22 if (reducedMotion) {
23 ring.style.strokeDashoffset = toOffset;
24 if (onProgress) { onProgress(1); }
25 return;
26 }
27 var start = null;
28 function step(ts) {
29 if (!start) { start = ts; }
30 var elapsed = Math.min(ts - start, duration);
31 var t = easeFn(elapsed / duration);
32 ring.style.strokeDashoffset = fromOffset + (toOffset - fromOffset) * t;
33 if (onProgress) { onProgress(t); }
34 if (elapsed < duration) { requestAnimationFrame(step); }
35 else { ring.style.strokeDashoffset = toOffset; if (onProgress) { onProgress(1); } }
36 }
37 requestAnimationFrame(step);
38 }
39
40 /* ── animate counter text ───────────────────────────────────────────────── */
41 function animateCounter(el, from, to, suffix, duration, easeFn) {
42 if (reducedMotion) { el.textContent = to + suffix; return; }
43 var start = null;
44 function step(ts) {
45 if (!start) { start = ts; }
46 var elapsed = Math.min(ts - start, duration);
47 var t = easeFn(elapsed / duration);
48 el.textContent = Math.round(from + (to - from) * t) + suffix;
49 if (elapsed < duration) { requestAnimationFrame(step); }
50 else { el.textContent = to + suffix; }
51 }
52 requestAnimationFrame(step);
53 }
54
55 /* ── init one item ───────────────────────────────────────────────────────── */
56 function initItem(item, grid) {
57 if (item._bkpcInit) { return; }
58 item._bkpcInit = true;
59
60 var pct = parseFloat(item.getAttribute('data-pct')) || 0;
61 var size = parseFloat(item.getAttribute('data-size')) || 140;
62 var sw = parseFloat(item.getAttribute('data-sw')) || 10;
63 var color = item.getAttribute('data-color') || '#6c3fb5';
64 var track = item.getAttribute('data-track') || '#e5e7eb';
65 var linecap = item.getAttribute('data-linecap') || 'round';
66 var gradient = item.getAttribute('data-gradient') === '1';
67 var gradEnd = item.getAttribute('data-grad-end') || '#ec4899';
68 var shadow = item.getAttribute('data-shadow') === '1';
69
70 var animate = grid.getAttribute('data-animate') !== '0';
71 var duration = parseInt(grid.getAttribute('data-duration'), 10) || 1200;
72 var easing = grid.getAttribute('data-easing') || 'ease-in-out';
73 var counter = grid.getAttribute('data-counter') !== '0';
74 var suffix = grid.getAttribute('data-suffix') || '%';
75
76 var easeFn = getEaseFn(easing);
77 var r = (size - sw) / 2;
78 var circ = 2 * Math.PI * r;
79 var ring = item.querySelector('.bkpc-ring');
80 if (!ring) { return; }
81
82 /* patch gradient defs if needed */
83 if (gradient) {
84 var svg = item.querySelector('svg');
85 if (svg) {
86 var uid = 'bkpc-g-fe-' + Math.random().toString(36).slice(2, 7);
87 var defs = svg.querySelector('defs');
88 if (defs) {
89 var lg = defs.querySelector('linearGradient');
90 if (lg) {
91 lg.id = uid;
92 ring.style.stroke = 'url(#' + uid + ')';
93 ring.setAttribute('stroke', 'url(#' + uid + ')');
94 }
95 }
96 }
97 }
98
99 var targetOffset = circ - circ * (pct / 100);
100
101 if (animate) {
102 /* start rings at full offset (empty) */
103 ring.style.strokeDashoffset = circ;
104
105 /* counter element */
106 var pctEl = item.querySelector('.bkpc-pct, .bkpc-pct-inner');
107
108 animateRing(ring, circ, circ, targetOffset, duration, easeFn, null);
109 if (counter && pctEl) {
110 animateCounter(pctEl, 0, pct, suffix, duration, easeFn);
111 }
112 } else {
113 ring.style.strokeDashoffset = targetOffset;
114 }
115 }
116
117 /* ── init one grid ───────────────────────────────────────────────────────── */
118 function initGrid(grid) {
119 if (grid._bkpcInit) { return; }
120 grid._bkpcInit = true;
121
122 var animate = grid.getAttribute('data-animate') !== '0';
123 var items = Array.prototype.slice.call(grid.querySelectorAll('.bkpc-item[data-pct]'));
124
125 if (!animate || reducedMotion) {
126 items.forEach(function (item) { initItem(item, grid); });
127 return;
128 }
129
130 /* observe the grid */
131 if ('IntersectionObserver' in window) {
132 var obs = new IntersectionObserver(function (entries) {
133 entries.forEach(function (entry) {
134 if (entry.isIntersecting) {
135 items.forEach(function (item) { initItem(item, grid); });
136 obs.disconnect();
137 }
138 });
139 }, { threshold: 0.2 });
140 obs.observe(grid);
141 } else {
142 items.forEach(function (item) { initItem(item, grid); });
143 }
144 }
145
146 /* ── boot ────────────────────────────────────────────────────────────────── */
147 function init() {
148 document.querySelectorAll('.bkpc-grid[data-animate]').forEach(initGrid);
149 }
150
151 if (document.readyState === 'loading') {
152 document.addEventListener('DOMContentLoaded', init);
153 } else {
154 init();
155 }
156 }());
157