| 1 |
(function ($) { |
| 2 |
"use strict"; |
| 3 |
|
| 4 |
const SEPARATORS = { comma: ",", dot: ".", space: " ", underline: "_" }; |
| 5 |
|
| 6 |
function formatWithSeparator(num, separator) { |
| 7 |
const str = num.toString(); |
| 8 |
if (!separator) return str; |
| 9 |
const negative = str.charAt(0) === "-"; |
| 10 |
const body = negative ? str.slice(1) : str; |
| 11 |
if (body.length < 4) return str; |
| 12 |
return (negative ? "-" : "") + body.replace(/\B(?=(\d{3})+(?!\d))/g, separator); |
| 13 |
} |
| 14 |
|
| 15 |
function runCounter(counter, target, start, duration, separator) { |
| 16 |
const startTime = performance.now(); |
| 17 |
function tick(time) { |
| 18 |
const progress = Math.min((time - startTime) / duration, 1); |
| 19 |
const current = Math.floor(start + (target - start) * progress); |
| 20 |
counter.textContent = formatWithSeparator(current, separator); |
| 21 |
if (progress < 1) requestAnimationFrame(tick); |
| 22 |
} |
| 23 |
requestAnimationFrame(tick); |
| 24 |
} |
| 25 |
|
| 26 |
function runOdometer(counter, target, duration, separator) { |
| 27 |
const targetInt = Math.floor(Math.abs(target)); |
| 28 |
const targetStr = targetInt.toString(); |
| 29 |
const negative = target < 0; |
| 30 |
|
| 31 |
counter.innerHTML = ""; |
| 32 |
counter.classList.add("eel-cnt-odometer-wrap"); |
| 33 |
|
| 34 |
if (negative) { |
| 35 |
const s = document.createElement("span"); |
| 36 |
s.className = "eel-cnt-odometer-sep"; |
| 37 |
s.textContent = "-"; |
| 38 |
counter.appendChild(s); |
| 39 |
} |
| 40 |
|
| 41 |
const animEls = []; |
| 42 |
let digitIndex = 0; |
| 43 |
|
| 44 |
for (let i = 0; i < targetStr.length; i++) { |
| 45 |
const posFromRight = targetStr.length - i; |
| 46 |
if (i > 0 && separator && posFromRight % 3 === 0) { |
| 47 |
const sep = document.createElement("span"); |
| 48 |
sep.className = "eel-cnt-odometer-sep"; |
| 49 |
sep.textContent = separator; |
| 50 |
counter.appendChild(sep); |
| 51 |
} |
| 52 |
|
| 53 |
const col = document.createElement("span"); |
| 54 |
col.className = "eel-cnt-odometer-digit"; |
| 55 |
|
| 56 |
const roll = document.createElement("span"); |
| 57 |
roll.className = "eel-cnt-odometer-roll"; |
| 58 |
|
| 59 |
const spins = 2 + digitIndex; |
| 60 |
let html = ""; |
| 61 |
for (let s = 0; s < spins; s++) { |
| 62 |
for (let n = 0; n <= 9; n++) { |
| 63 |
html += '<span class="eel-cnt-odometer-num">' + n + '</span>'; |
| 64 |
} |
| 65 |
} |
| 66 |
html += '<span class="eel-cnt-odometer-num">' + targetStr.charAt(i) + '</span>'; |
| 67 |
roll.innerHTML = html; |
| 68 |
roll.style.transform = "translateY(0)"; |
| 69 |
|
| 70 |
col.appendChild(roll); |
| 71 |
counter.appendChild(col); |
| 72 |
animEls.push({ roll: roll, spins: spins }); |
| 73 |
digitIndex++; |
| 74 |
} |
| 75 |
|
| 76 |
requestAnimationFrame(function () { |
| 77 |
requestAnimationFrame(function () { |
| 78 |
animEls.forEach(function (item) { |
| 79 |
item.roll.style.transition = "transform " + duration + "ms cubic-bezier(.22,.85,.34,1)"; |
| 80 |
item.roll.style.transform = "translateY(-" + (item.spins * 10) + "em)"; |
| 81 |
}); |
| 82 |
}); |
| 83 |
}); |
| 84 |
} |
| 85 |
|
| 86 |
function eelRunCounter($scope) { |
| 87 |
const counters = $scope.find(".eel-counter"); |
| 88 |
if (!counters.length) return; |
| 89 |
|
| 90 |
const observer = new IntersectionObserver(function (entries) { |
| 91 |
entries.forEach(function (entry) { |
| 92 |
if (!entry.isIntersecting) return; |
| 93 |
|
| 94 |
const counter = entry.target; |
| 95 |
observer.unobserve(counter); |
| 96 |
|
| 97 |
const target = parseInt(counter.dataset.count, 10) || 0; |
| 98 |
const start = parseInt(counter.dataset.start, 10) || 0; |
| 99 |
const duration = parseInt(counter.dataset.duration, 10) || 1000; |
| 100 |
const format = counter.dataset.format || "default"; |
| 101 |
const animation = counter.dataset.animation || "counter"; |
| 102 |
const separator = SEPARATORS[format] || ""; |
| 103 |
|
| 104 |
if (animation === "odometer") { |
| 105 |
runOdometer(counter, target, duration, separator); |
| 106 |
} else { |
| 107 |
runCounter(counter, target, start, duration, separator); |
| 108 |
} |
| 109 |
}); |
| 110 |
}, { threshold: 0.2 }); |
| 111 |
|
| 112 |
counters.each(function () { |
| 113 |
observer.observe(this); |
| 114 |
}); |
| 115 |
} |
| 116 |
|
| 117 |
$(window).on("elementor/frontend/init", function () { |
| 118 |
elementorFrontend.hooks.addAction("frontend/element_ready/widget", function ($scope) { |
| 119 |
if ($scope.find(".eel-counter").length) { |
| 120 |
eelRunCounter($scope); |
| 121 |
} |
| 122 |
}); |
| 123 |
}); |
| 124 |
|
| 125 |
})(jQuery); |
| 126 |
|