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 / floating-stats / frontend.js

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

103 lines 3.7 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 // Floating Stats — card reveal + numeric count-up animation
5
6 function parseNumber(str) {
7 // Extract leading number from strings like "10,000+" or "99.9%" or "4.9"
8 var clean = str.replace(/,/g, '').match(/[\d.]+/);
9 return clean ? parseFloat(clean[0]) : null;
10 }
11
12 function formatNumber(num, original) {
13 // Restore formatting similar to original
14 var hasComma = original.indexOf(',') !== -1;
15 var suffix = original.replace(/[\d,. ]/g, '');
16 var prefix = '';
17 // detect prefix like $ £ €
18 var prefixMatch = original.match(/^[^0-9]*/);
19 if (prefixMatch && prefixMatch[0]) prefix = prefixMatch[0];
20 var formatted = hasComma ? num.toLocaleString('en-US', { maximumFractionDigits: 1 }) : (Number.isInteger(num) ? num.toString() : num.toFixed(1));
21 return prefix + formatted + suffix;
22 }
23
24 function animateCountUp(el, duration) {
25 var original = el.textContent.trim();
26 var target = parseNumber(original);
27 if (target === null || target === 0) return;
28
29 var start = null;
30 var startVal = 0;
31
32 function step(timestamp) {
33 if (!start) start = timestamp;
34 var progress = Math.min((timestamp - start) / duration, 1);
35 // Ease out cubic
36 var eased = 1 - Math.pow(1 - progress, 3);
37 var current = startVal + (target - startVal) * eased;
38
39 // Format matches original (integer vs decimal)
40 var isInt = Number.isInteger(target);
41 var displayVal = isInt ? Math.round(current) : parseFloat(current.toFixed(1));
42 el.textContent = formatNumber(displayVal, original);
43
44 if (progress < 1) {
45 requestAnimationFrame(step);
46 } else {
47 el.textContent = original; // restore exact original
48 }
49 }
50
51 requestAnimationFrame(step);
52 }
53
54 function initFloatingStats() {
55 var wraps = document.querySelectorAll('.bkbg-floating-stats');
56 if (!wraps.length) return;
57
58 if (!('IntersectionObserver' in window)) {
59 wraps.forEach(function (wrap) {
60 wrap.querySelectorAll('.bkbg-floating-card').forEach(function (card) {
61 card.classList.add('is-visible');
62 });
63 });
64 return;
65 }
66
67 var cardObs = new IntersectionObserver(function (entries) {
68 entries.forEach(function (entry) {
69 if (!entry.isIntersecting) return;
70 entry.target.classList.add('is-visible');
71 cardObs.unobserve(entry.target);
72 });
73 }, { threshold: 0.2, rootMargin: '0px 0px -30px 0px' });
74
75 var wrapObs = new IntersectionObserver(function (entries) {
76 entries.forEach(function (entry) {
77 if (!entry.isIntersecting) return;
78 var wrap = entry.target;
79 var doCountUp = wrap.getAttribute('data-count-up') === '1';
80 if (doCountUp) {
81 wrap.querySelectorAll('.bkbg-floating-card__value').forEach(function (el) {
82 animateCountUp(el, 1600);
83 });
84 }
85 wrapObs.unobserve(wrap);
86 });
87 }, { threshold: 0.25 });
88
89 wraps.forEach(function (wrap) {
90 wrap.querySelectorAll('.bkbg-floating-card').forEach(function (card) {
91 cardObs.observe(card);
92 });
93 wrapObs.observe(wrap);
94 });
95 }
96
97 if (document.readyState === 'loading') {
98 document.addEventListener('DOMContentLoaded', initFloatingStats);
99 } else {
100 initFloatingStats();
101 }
102 }());
103