SVG加载进度边框描边粗细随视口尺寸变化不一致的解决方法咨询
SVG加载进度边框描边粗细随视口尺寸变化不一致的解决方法咨询
我正在做一个用SVG路径实现的加载屏,用灰色路径表示未加载的边框,白色路径表示加载进度。加载边框的逻辑是从右上角(0%)开始,到左下角(25%)、右下角(50%)、右上角(75%),最后回到左上角(100%)完成加载。
目前所有功能都正常,唯独遇到一个关键问题:当视口高度大于宽度(或者反之)时,边框的上下边和左右边描边粗细不一样——要么上下边更粗,要么左右边更粗。我想要不管视口尺寸怎么变,描边粗细都保持均匀,而且还能调整这个粗细来适配移动端。
我已经尝试过这些方法,但都没解决问题:
- 设置
stroke-width="1" - 设置
stroke-width="1px" - 设置
stroke-width="1%" - 设置
stroke-width="1vmin" - 使用
vector-effect:non-scaling-stroke;但会破坏动画 - 同时使用
vector-effect:non-scaling-stroke;和vector-effect:non-rotation; - 用JavaScript动态根据视口设置宽度(代码没保留)
当前代码
HTML
<!-- Loading Screen --> <div id="loading-screen"> <div id="loading-content"> <img id="ws-logo" src="ws-logo.png" alt="WS Logo"> <div id="loading-percentage">0%</div> </div> <svg id="loading-bar" width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="none"> <!-- Grey background path --> <path d="M5 5 L5 95 L95 95 L95 5 Z" stroke="#666" stroke-width="2vmin" stroke-linecap="square" fill="none" /> <!-- White progress path --> <path id="progress-path" d="M5 5 L5 95 L95 95 L95 5 Z" stroke="#fff" stroke-width="2vmin" stroke-linecap="square" fill="none" stroke-dasharray="0" stroke-dashoffset="0" /> </svg> </div>
CSS
#loading-screen { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: #222; /* Dark background */ display: flex; justify-content: center; align-items: center; z-index: 9999; /* Overlay all content */ overflow: hidden; transition: opacity 0.5s ease; /* Fade-out effect */ } /* Logo and percentage container */ #loading-content { position: absolute; display: flex; flex-direction: column; align-items: center; z-index: 1; /* Above the loading bar */ } /* Logo styling */ #ws-logo { width: 15%; max-width: 100px; height: auto; margin-bottom: 10px; } /* Percentage text styling */ #loading-percentage { font-size: 1.5em; color: #fff; font-family: Arial, sans-serif; } /* SVG loading bar */ #loading-bar { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 0; /* Behind content */ padding: 0; /* Ensure no internal spacing */ margin: 0; /* Remove any external margin */ box-sizing: border-box; /* Include border in dimensions */ } #loading-bar path { vector-effect: non-scaling-stroke; } #progress-path { transition: stroke-dashoffset 0.2s ease-out; /* Smooth animation for the progress bar */ }
JavaScript
document.addEventListener("DOMContentLoaded", () => { const loadingScreen = document.getElementById("loading-screen"); const loadingPercentage = document.getElementById("loading-percentage"); const progressPath = document.getElementById("progress-path"); let progress = 0; // Current progress const minDisplayTime = 1750; // Minimum display time in milliseconds const fadeDelay = 500; // Duration of fade-out animation (in ms) const delayAfterComplete = 750; // Delay after hitting 100% (in ms) const startTime = Date.now(); // Record when loading starts // 动态获取路径实际总长度,替代硬编码的360 const totalLength = progressPath.getTotalLength(); // Set the initial stroke-dasharray and stroke-dashoffset for the white bar progressPath.style.strokeDasharray = totalLength; progressPath.style.strokeDashoffset = totalLength; // Function to update progress function updateProgress() { const elapsedTime = Date.now() - startTime; // Calculate the progress as a percentage based on time if (elapsedTime < minDisplayTime) { progress = Math.min((elapsedTime / minDisplayTime) * 100, 100); } else { progress = 100; // Ensure progress reaches 100% after minimum display time } // Update percentage text loadingPercentage.textContent = `${Math.floor(progress)}%`; // Update progress bar position const offset = totalLength * (1 - progress / 100); progressPath.style.strokeDashoffset = offset; console.log(`Loading progress: ${progress}%`); // Trigger fade-out when progress hits 100% if (progress === 100) { const remainingTime = minDisplayTime - elapsedTime; // Ensure minimum display time setTimeout(() => { // Add a 0.75-second delay after hitting 100% before starting fade-out setTimeout(() => { loadingScreen.style.opacity = "0"; // Trigger fade-out animation // Completely hide the loading screen after the fade-out setTimeout(() => { loadingScreen.style.display = "none"; }, fadeDelay); // Match the CSS transition duration }, delayAfterComplete); // 0.75-second delay after hitting 100% }, Math.max(remainingTime, 0)); // Wait for any remaining time if necessary } else { // Continue updating progress until it reaches 100% requestAnimationFrame(updateProgress); } } // Start the progress update loop updateProgress(); });
解决思路说明
- 启用
non-scaling-stroke:这个属性会让SVG描边不随SVG的缩放而变化,不管视口是宽屏还是竖屏,描边粗细都保持一致。之前你用这个属性时动画出错,是因为硬编码了路径总长度,导致计算的stroke-dashoffset和实际路径不匹配。 - 动态获取路径总长度:用
progressPath.getTotalLength()替代硬编码的360,这样不管视口怎么缩放,都能拿到当前路径的真实长度,保证动画计算准确。 - 用
vmin设置描边粗细:vmin是视口最小边的百分比单位,比如2vmin就是视口宽高中较小值的2%,这样在移动端和桌面端都能保持合适的粗细,而且不会随视口拉伸变形。
这样修改后,你的加载边框描边就能在任何视口尺寸下保持均匀粗细,同时动画也能正常运行,还能通过调整vmin的数值来适配不同设备的需求。
备注:内容来源于stack exchange,提问作者Antonio Higdon
相关产品推荐
相关产品推荐

