You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

滚动离开skills区块时环形进度条重置失效问题如何解决

环形进度条滚动触发重置问题修复

原有代码的核心问题

  • 滚动事件高频触发时会重复创建setInterval定时器,切换状态前没有清空已有定时器实例,多个定时器并行运行会导致动画逻辑冲突、数值跳变
  • 区块进入判断逻辑缺失:仅校验滚动位置是否超过skills区块顶部,没有校验滚动位置是否超出区块底部边界,向下滚动离开skills区块到下方第二区块时,依然会命中「进入区块」的判断分支,无法触发重置逻辑
  • 重置分支判断条件错误:仅当计数器数值等于目标最大值时才执行递减操作,若动画未播放完成用户就滚动离开区块,重置逻辑完全不会执行
  • 环形描边重置没有和百分比数值变化同步计算,直接硬编码设置为空白状态,和数字动画节奏不匹配

修复方案

  • 新增状态标记位,记录当前进度条是否处于激活播放状态,避免重复触发动画
  • 完善区块可见性判断规则:只有当滚动位置处于「区块顶部偏移-触发阈值」到「区块底部位置」区间内时,才判定为进入skills区块,其余滚动位置均判定为离开区块
  • 每次切换进入/离开状态前,先清空所有正在运行的定时器,避免定时器叠加导致的动画异常
  • 统一百分比数值和环形描边偏移量的计算逻辑,递增、递减过程中同步更新两个状态,保证动画节奏完全一致

修复后可直接运行的JS代码

原有CSS和HTML结构无需调整,将原有JS逻辑替换为以下代码即可:

const numbers = document.querySelectorAll(".number");
const svgEl = document.querySelectorAll('svg circle');
const section = document.querySelector('#skills');
const counters = Array(numbers.length).fill(0);
const intervals = Array(numbers.length);
// 状态标记:记录进度条是否处于激活播放状态
let isActive = false;

// 重置进度条到0%状态
function resetProgress() {
    // 先清空所有运行中的定时器
    intervals.forEach(timer => clearInterval(timer));
    numbers.forEach((number, index) => {
        intervals[index] = setInterval(() => {
            if (counters[index] <= 0) {
                clearInterval(intervals[index]);
                counters[index] = 0;
                number.innerHTML = "0%";
                svgEl[index].style.strokeDashoffset = 472;
            } else {
                counters[index] -= 1;
                number.innerHTML = counters[index] + "%";
                // 同步计算描边偏移量,和数字变化保持一致
                svgEl[index].style.strokeDashoffset = Math.floor(472 - 440 * (counters[index] / 100));
            }
        }, 20);
    });
    isActive = false;
}

// 播放进度条从0到目标值的动画
function playProgress() {
    // 先清空所有运行中的定时器
    intervals.forEach(timer => clearInterval(timer));
    counters.fill(0);
    numbers.forEach((number, index) => {
        const target = parseInt(number.dataset.num);
        intervals[index] = setInterval(() => {
            if (counters[index] >= target) {
                clearInterval(intervals[index]);
                counters[index] = target;
                number.innerHTML = `${target}%`;
                svgEl[index].style.strokeDashoffset = Math.floor(472 - 440 * (target / 100));
            } else {
                counters[index] += 1;
                number.innerHTML = `${counters[index]}%`;
                svgEl[index].style.strokeDashoffset = Math.floor(472 - 440 * (counters[index] / 100));
            }
        }, 20);
    });
    isActive = true;
}

window.onscroll = function () {
    const sectionTop = section.offsetTop - 50;
    const sectionBottom = section.offsetTop + section.offsetHeight;
    const currentScroll = window.scrollY;
    const isInSection = currentScroll >= sectionTop && currentScroll < sectionBottom;

    if (isInSection && !isActive) {
        playProgress();
    } else if (!isInSection && isActive) {
        resetProgress();
    }
}

补充说明:需要确保每个.skill-level容器内包含对应的SVG环形DOM结构,否则svg circle选择器无法获取到描边元素,会导致环形填充动画不生效。

内容的提问来源于stack exchange,提问作者Mohamad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 00:24:24