如何将计数动画代码封装为可复用函数并在IntersectionObserver中调用
封装数字计数与SVG描边动画函数并结合IntersectionObserver使用
1. 封装可复用动画函数
把原逻辑封装成独立函数,将依赖的元素集合作为参数传入,避免全局变量污染,同时增加重置逻辑确保动画可重复触发:
function startCountAndSvgAnimation(numbers, svgElements) { // 初始化每个元素的计数器和定时器 const counters = []; const intervals = []; // 先重置所有元素状态(避免重复触发时状态混乱) numbers.forEach((number, index) => { counters[index] = 0; number.textContent = "0%"; // 重置SVG描边初始状态 svgElements[index].style.strokeDashoffset = 472; // 清除可能存在的旧定时器 if (intervals[index]) { clearInterval(intervals[index]); } }); // 启动动画 numbers.forEach((number, index) => { const targetNum = parseInt(number.dataset.num); intervals[index] = setInterval(() => { if (counters[index] === targetNum) { clearInterval(intervals[index]); } else { counters[index] += 1; number.textContent = `${counters[index]}%`; // 计算SVG描边偏移量 svgElements[index].style.strokeDashoffset = Math.floor(472 - 440 * (targetNum / 100)); } }, 20); }); } // 可选:重置函数,用于元素离开视口时恢复初始状态 function resetCountAndSvgAnimation(numbers, svgElements) { numbers.forEach((number, index) => { number.textContent = "0%"; svgElements[index].style.strokeDashoffset = 472; // 清除定时器 if (window.intervals?.[index]) { clearInterval(window.intervals[index]); } }); }
2. 结合IntersectionObserver调用
修改Observer逻辑,在元素进入视口时启动动画,离开时按需重置状态:
// 替换成你的实际元素选择器 const numbers = document.querySelectorAll('.number-element'); const svgEl = document.querySelectorAll('.svg-stroke-element'); const animateObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { // 元素进入视口,启动动画 startCountAndSvgAnimation(numbers, svgEl); // 若只需触发一次,可在此调用unobserve停止观测 // animateObserver.unobserve(entry.target); } else { // 元素离开视口,重置状态(按需启用) resetCountAndSvgAnimation(numbers, svgEl); } }); }); animateObserver.observe(test);
关键优化点
- 消除全局依赖:将计数器、定时器放在函数内部,通过参数传递元素集合
- 支持重复触发:添加重置逻辑,确保元素再次进入视口时动画能正常启动
- 逻辑拆分:将启动和重置拆分为独立函数,提升代码可维护性
内容的提问来源于stack exchange,提问作者newbcods
相关产品推荐
相关产品推荐

