| 1 |
function typoCssVarsForEl(typo, prefix, el) { |
| 2 |
if (!typo || typeof typo !== 'object') return; |
| 3 |
var map = { |
| 4 |
family: 'font-family', weight: 'font-weight', style: 'font-style', |
| 5 |
transform: 'text-transform', decoration: 'text-decoration', |
| 6 |
sizeDesktop: 'font-size-d', sizeTablet: 'font-size-t', sizeMobile: 'font-size-m', |
| 7 |
sizeUnit: null, |
| 8 |
lineHeightDesktop: 'line-height-d', lineHeightTablet: 'line-height-t', lineHeightMobile: 'line-height-m', |
| 9 |
lineHeightUnit: null, |
| 10 |
letterSpacingDesktop: 'letter-spacing-d', letterSpacingTablet: 'letter-spacing-t', letterSpacingMobile: 'letter-spacing-m', |
| 11 |
letterSpacingUnit: null, |
| 12 |
wordSpacingDesktop: 'word-spacing-d', wordSpacingTablet: 'word-spacing-t', wordSpacingMobile: 'word-spacing-m', |
| 13 |
wordSpacingUnit: null |
| 14 |
}; |
| 15 |
var sizeU = typo.sizeUnit || 'px'; |
| 16 |
var lhU = typo.lineHeightUnit || ''; |
| 17 |
var lsU = typo.letterSpacingUnit || 'px'; |
| 18 |
var wsU = typo.wordSpacingUnit || 'px'; |
| 19 |
Object.keys(map).forEach(function (k) { |
| 20 |
var css = map[k]; if (!css) return; |
| 21 |
var v = typo[k]; if (v === undefined || v === '' || v === null) return; |
| 22 |
if (/size|spacing/i.test(k)) { |
| 23 |
var u = /letterSpacing/.test(k) ? lsU : /wordSpacing/.test(k) ? wsU : /lineHeight/.test(k) ? lhU : sizeU; |
| 24 |
v = v + u; |
| 25 |
} |
| 26 |
el.style.setProperty(prefix + css, v); |
| 27 |
}); |
| 28 |
} |
| 29 |
|
| 30 |
wp.domReady(function () { |
| 31 |
document.querySelectorAll('.bkbg-cu-app').forEach(function (app) { |
| 32 |
var opts = {}; |
| 33 |
try { opts = JSON.parse(app.getAttribute('data-opts') || '{}'); } catch (e) {} |
| 34 |
|
| 35 |
var target = parseFloat(opts.value) || 0; |
| 36 |
var prefix = opts.prefix || ''; |
| 37 |
var suffix = opts.suffix || ''; |
| 38 |
var duration = parseInt(opts.duration) || 2000; |
| 39 |
var separator = opts.separator !== undefined ? opts.separator : ','; |
| 40 |
var decimals = parseInt(opts.decimals) || 0; |
| 41 |
|
| 42 |
// Build DOM |
| 43 |
var wrap = document.createElement('div'); |
| 44 |
wrap.className = 'bkbg-cu-wrap'; |
| 45 |
if (opts.bgColor) wrap.style.background = opts.bgColor; |
| 46 |
wrap.style.paddingTop = (opts.paddingTop || 48) + 'px'; |
| 47 |
wrap.style.paddingBottom = (opts.paddingBottom || 48) + 'px'; |
| 48 |
|
| 49 |
typoCssVarsForEl(opts.typoValue, '--bkbg-cu-val-', wrap); |
| 50 |
typoCssVarsForEl(opts.typoLabel, '--bkbg-cu-lbl-', wrap); |
| 51 |
|
| 52 |
var inner = document.createElement('div'); |
| 53 |
inner.className = 'bkbg-cu-inner'; |
| 54 |
inner.style.maxWidth = (opts.maxWidth || 360) + 'px'; |
| 55 |
inner.style.textAlign = opts.textAlign || 'center'; |
| 56 |
|
| 57 |
var iconEl, valueEl, labelEl; |
| 58 |
|
| 59 |
if (opts.showIcon && opts.icon) { |
| 60 |
iconEl = document.createElement('div'); |
| 61 |
iconEl.className = 'bkbg-cu-icon'; |
| 62 |
iconEl.style.fontSize = (opts.iconSize || 40) + 'px'; |
| 63 |
iconEl.style.color = opts.iconColor || '#7c3aed'; |
| 64 |
iconEl.textContent = opts.icon; |
| 65 |
inner.appendChild(iconEl); |
| 66 |
} |
| 67 |
|
| 68 |
valueEl = document.createElement('div'); |
| 69 |
valueEl.className = 'bkbg-cu-value'; |
| 70 |
valueEl.style.color = opts.valueColor || '#7c3aed'; |
| 71 |
valueEl.textContent = prefix + formatNumber(0, decimals, separator) + suffix; |
| 72 |
inner.appendChild(valueEl); |
| 73 |
|
| 74 |
if (opts.showLabel && opts.label) { |
| 75 |
labelEl = document.createElement('div'); |
| 76 |
labelEl.className = 'bkbg-cu-label'; |
| 77 |
labelEl.style.color = opts.labelColor || '#374151'; |
| 78 |
labelEl.textContent = opts.label; |
| 79 |
inner.appendChild(labelEl); |
| 80 |
} |
| 81 |
|
| 82 |
wrap.appendChild(inner); |
| 83 |
app.parentNode.replaceChild(wrap, app); |
| 84 |
|
| 85 |
// Animate on scroll into view |
| 86 |
var started = false; |
| 87 |
|
| 88 |
function formatNumber(num, dec, sep) { |
| 89 |
var fixed = num.toFixed(dec); |
| 90 |
if (!sep) return fixed; |
| 91 |
var parts = fixed.split('.'); |
| 92 |
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, sep); |
| 93 |
return parts.join('.'); |
| 94 |
} |
| 95 |
|
| 96 |
function animate(startTime) { |
| 97 |
var elapsed = performance.now() - startTime; |
| 98 |
var progress = Math.min(elapsed / duration, 1); |
| 99 |
// easeOutCubic |
| 100 |
var ease = 1 - Math.pow(1 - progress, 3); |
| 101 |
var current = ease * target; |
| 102 |
valueEl.textContent = prefix + formatNumber(current, decimals, separator) + suffix; |
| 103 |
if (progress < 1) { |
| 104 |
requestAnimationFrame(function () { animate(startTime); }); |
| 105 |
} |
| 106 |
} |
| 107 |
|
| 108 |
function formatNumber(num, dec, sep) { |
| 109 |
var fixed = num.toFixed(dec); |
| 110 |
if (!sep) return fixed; |
| 111 |
var parts = fixed.split('.'); |
| 112 |
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, sep); |
| 113 |
return parts.join('.'); |
| 114 |
} |
| 115 |
|
| 116 |
var io = new IntersectionObserver(function (entries) { |
| 117 |
entries.forEach(function (entry) { |
| 118 |
if (entry.isIntersecting && !started) { |
| 119 |
started = true; |
| 120 |
requestAnimationFrame(function (ts) { animate(ts); }); |
| 121 |
io.disconnect(); |
| 122 |
} |
| 123 |
}); |
| 124 |
}, { threshold: 0.3 }); |
| 125 |
|
| 126 |
io.observe(wrap); |
| 127 |
}); |
| 128 |
}); |
| 129 |
|