大家好,我是设计师邱兴,一个学习前端的设计师,今天给大家制作一个用SVG实现的带刻度的仪表盘,SVG相较于Echart来说制作简单,但是效果可以非常丰富。
一、目标
通过SVG和JavaScript实现一个精美的圆形仪表盘进度条,具有以下特点:
- 使用渐变色背景和进度块,提升视觉效果。
- 动态更新进度,展示从0到80的下载速度值。
- 包含刻度数字,显示不同的速度值。
- 使用滑块控制进度值。
二、所需工具与准备
-
工具:
- 一个文本编辑器(如Notepad++、VS Code等)。
- 浏览器(用于预览效果)。
-
基础准备:
- 确保你对HTML、CSS和JavaScript有一定的了解。
- 确保你对SVG的基本语法有一定了解。
三、代码分析与操作步骤
1. 创建HTML结构
创建一个HTML文件(如Lesson3.html
)并设置基本的HTML结构:
xml
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>SVG 仪表盘进度条</title>
<style>
/* 样式部分 */
</style>
</head>
<body>
<div class="gauge-container">
<svg id="gauge" width="350" height="300" viewBox="0 0 350 300">
<!-- SVG内容 -->
</svg>
</div>
<div class="slider-container">
<input type="range" id="progressSlider" min="0" max="80" value="0" step="1" style="width:300px;" />
<div>进度:<span id="progressNum">0</span> / 80</div>
</div>
<script>
// JavaScript部分
</script>
</body>
</html>
2. 添加CSS样式
在<style>
标签中,添加以下CSS样式:
css
body {
background: #fff;
display: flex;
flex-direction: column;
align-items: center;
font-family: Arial, sans-serif;
}
.gauge-container {
margin-top: 40px;
margin-bottom: 20px;
}
.slider-container {
margin-top: 30px;
text-align: center;
}
- 设置页面背景颜色为白色。
- 使用
flexbox
布局将SVG仪表盘和滑块居中显示。 - 设置滑块容器的样式。
3. 定义SVG渐变
在<svg>
标签中,定义背景渐变和活动渐变:
ini
<defs>
<linearGradient id="bgGradient" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#3b82f6" stop-opacity="0.2"/>
<stop offset="100%" stop-color="#3b82f6" stop-opacity="0.05"/>
</linearGradient>
<linearGradient id="activeGradient" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#2563eb"/>
<stop offset="100%" stop-color="#60a5fa"/>
</linearGradient>
</defs>
bgGradient
:背景渐变,用于显示未填充区域。activeGradient
:活动渐变,用于显示已填充区域。
4. 绘制背景和进度圆环
在<svg>
标签中,添加背景和进度圆环的组元素:
bash
<g id="gauge-bg"></g>
<g id="gauge-active"></g>
5. 添加刻度数字和中心元素
在<svg>
标签中,添加刻度数字和中心元素:
arduino
<g id="gauge-labels"></g>
<circle cx="175" cy="175" r="90" fill="white" opacity="0.95"/>
<text x="175" y="130" text-anchor="middle" font-size="18" fill="#888" font-weight="bold">下载速度</text>
<text id="gauge-value" x="175" y="170" text-anchor="middle" font-size="44" fill="#222" font-weight="bold">0</text>
<text x="175" y="200" text-anchor="middle" font-size="18" fill="#888" font-weight="bold">Mbps</text>
gauge-labels
:用于显示刻度数字。- 中心白色圆和文字:用于显示进度值。
6. 编写JavaScript代码
在<script>
标签中,添加以下JavaScript代码来实现动态效果:
ini
const minValue = 0;
const maxValue = 80;
const numBlocks = 8;
const startAngle = 225;
const endAngle = 45 + 360;
const radiusOuter = 130;
const radiusInner = 110;
const centerX = 175;
const centerY = 175;
// 辅助函数:极坐标转换为笛卡尔坐标
function polarToCartesian(cx, cy, r, angle) {
const rad = (angle-90) * Math.PI / 180.0;
return {
x: cx + (r * Math.cos(rad)),
y: cy + (r * Math.sin(rad))
};
}
// 辅助函数:生成圆弧路径
function describeArc(cx, cy, r1, r2, startAngle, endAngle) {
const p1 = polarToCartesian(cx, cy, r2, endAngle);
const p2 = polarToCartesian(cx, cy, r2, startAngle);
const p3 = polarToCartesian(cx, cy, r1, startAngle);
const p4 = polarToCartesian(cx, cy, r1, endAngle);
const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
return [
"M", p1.x, p1.y,
"A", r2, r2, 0, largeArcFlag, 0, p2.x, p2.y,
"L", p3.x, p3.y,
"A", r1, r1, 0, largeArcFlag, 1, p4.x, p4.y,
"Z"
].join(" ");
}
// 绘制背景和进度块
function drawGaugeBlocks(value) {
const bgGroup = document.getElementById('gauge-bg');
const activeGroup = document.getElementById('gauge-active');
bgGroup.innerHTML = '';
activeGroup.innerHTML = '';
const angleStep = (270) / numBlocks;
const valuePerBlock = (maxValue - minValue) / numBlocks;
const activeBlocks = Math.round((value - minValue) / valuePerBlock);
for (let i = 0; i < numBlocks; i++) {
const sa = startAngle + i * angleStep;
const ea = sa + angleStep * 0.92;
// 背景块
const bgPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
bgPath.setAttribute("d", describeArc(centerX, centerY, radiusInner, radiusOuter, sa, ea));
bgPath.setAttribute("fill", "url(#bgGradient)");
bgGroup.appendChild(bgPath);
// 进度块
if (i < activeBlocks) {
const activePath = document.createElementNS("http://www.w3.org/2000/svg", "path");
activePath.setAttribute("d", describeArc(centerX, centerY, radiusInner, radiusOuter, sa, ea));
activePath.setAttribute("fill", "url(#activeGradient)");
activeGroup.appendChild(activePath);
}
}
}
// 绘制刻度数字
function drawGaugeLabels() {
const labelsGroup = document.getElementById('gauge-labels');
labelsGroup.innerHTML = '';
const angleStep = (270) / numBlocks;
const valuePerBlock = (maxValue - minValue) / numBlocks;
for (let i = 0; i <= numBlocks; i++) {
const angle = startAngle + i * angleStep;
const pos = polarToCartesian(centerX, centerY, radiusOuter + 18, angle);
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
label.setAttribute("x", pos.x);
label.setAttribute("y", pos.y + 6);
label.setAttribute("text-anchor", "middle");
label.setAttribute("font-size", "16");
label.setAttribute("fill", "#7ca0d9");
label.setAttribute("font-weight", "bold");
label.textContent = Math.round(minValue + i * valuePerBlock);
labelsGroup.appendChild(label);
}
}
// 控制进度
function setProgress(val) {
drawGaugeBlocks(val);
document.getElementById('gauge-value').textContent = val;
document.getElementById('progressNum').textContent = val;
}
// 初始化
drawGaugeLabels();
setProgress(0);
// 监听滑块
document.getElementById('progressSlider').addEventListener('input', function() {
setProgress(Number(this.value));
});
7. 完整代码
将上述代码整合到一个HTML文件中,完整的代码如下:
xml
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>SVG 仪表盘进度条</title>
<style>
body {
background: #fff;
display: flex;
flex-direction: column;
align-items: center;
font-family: Arial, sans-serif;
}
.gauge-container {
margin-top: 40px;
margin-bottom: 20px;
}
.slider-container {
margin-top: 30px;
text-align: center;
}
</style>
</head>
<body>
<div class="gauge-container">
<svg id="gauge" width="350" height="300" viewBox="0 0 350 300">
<!-- 背景渐变 -->
<defs>
<linearGradient id="bgGradient" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#3b82f6" stop-opacity="0.2"/>
<stop offset="100%" stop-color="#3b82f6" stop-opacity="0.05"/>
</linearGradient>
<linearGradient id="activeGradient" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#2563eb"/>
<stop offset="100%" stop-color="#60a5fa"/>
</linearGradient>
</defs>
<!-- 背景圆环 -->
<g id="gauge-bg"></g>
<!-- 进度圆环 -->
<g id="gauge-active"></g>
<!-- 刻度数字 -->
<g id="gauge-labels"></g>
<!-- 中心白色圆 -->
<circle cx="175" cy="175" r="90" fill="white" opacity="0.95"/>
<!-- 中心文字 -->
<text x="175" y="130" text-anchor="middle" font-size="18" fill="#888" font-weight="bold">下载速度</text>
<text id="gauge-value" x="175" y="170" text-anchor="middle" font-size="44" fill="#222" font-weight="bold">0</text>
<text x="175" y="200" text-anchor="middle" font-size="18" fill="#888" font-weight="bold">Mbps</text>
</svg>
</div>
<div class="slider-container">
<input type="range" id="progressSlider" min="0" max="80" value="0" step="1" style="width:300px;" />
<div>进度:<span id="progressNum">0</span> / 80</div>
</div>
<script>
const minValue = 0;
const maxValue = 80;
const numBlocks = 8;
const startAngle = 225;
const endAngle = 45 + 360;
const radiusOuter = 130;
const radiusInner = 110;
const centerX = 175;
const centerY = 175;
// 辅助函数:极坐标转换为笛卡尔坐标
function polarToCartesian(cx, cy, r, angle) {
const rad = (angle-90) * Math.PI / 180.0;
return {
x: cx + (r * Math.cos(rad)),
y: cy + (r * Math.sin(rad))
};
}
// 辅助函数:生成圆弧路径
function describeArc(cx, cy, r1, r2, startAngle, endAngle) {
const p1 = polarToCartesian(cx, cy, r2, endAngle);
const p2 = polarToCartesian(cx, cy, r2, startAngle);
const p3 = polarToCartesian(cx, cy, r1, startAngle);
const p4 = polarToCartesian(cx, cy, r1, endAngle);
const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
return [
"M", p1.x, p1.y,
"A", r2, r2, 0, largeArcFlag, 0, p2.x, p2.y,
"L", p3.x, p3.y,
"A", r1, r1, 0, largeArcFlag, 1, p4.x, p4.y,
"Z"
].join(" ");
}
// 绘制背景和进度块
function drawGaugeBlocks(value) {
const bgGroup = document.getElementById('gauge-bg');
const activeGroup = document.getElementById('gauge-active');
bgGroup.innerHTML = '';
activeGroup.innerHTML = '';
const angleStep = (270) / numBlocks;
const valuePerBlock = (maxValue - minValue) / numBlocks;
const activeBlocks = Math.round((value - minValue) / valuePerBlock);
for (let i = 0; i < numBlocks; i++) {
const sa = startAngle + i * angleStep;
const ea = sa + angleStep * 0.92;
// 背景块
const bgPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
bgPath.setAttribute("d", describeArc(centerX, centerY, radiusInner, radiusOuter, sa, ea));
bgPath.setAttribute("fill", "url(#bgGradient)");
bgGroup.appendChild(bgPath);
// 进度块
if (i < activeBlocks) {
const activePath = document.createElementNS("http://www.w3.org/2000/svg", "path");
activePath.setAttribute("d", describeArc(centerX, centerY, radiusInner, radiusOuter, sa, ea));
activePath.setAttribute("fill", "url(#activeGradient)");
activeGroup.appendChild(activePath);
}
}
}
// 绘制刻度数字
function drawGaugeLabels() {
const labelsGroup = document.getElementById('gauge-labels');
labelsGroup.innerHTML = '';
const angleStep = (270) / numBlocks;
const valuePerBlock = (maxValue - minValue) / numBlocks;
for (let i = 0; i <= numBlocks; i++) {
const angle = startAngle + i * angleStep;
const pos = polarToCartesian(centerX, centerY, radiusOuter + 18, angle);
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
label.setAttribute("x", pos.x);
label.setAttribute("y", pos.y + 6);
label.setAttribute("text-anchor", "middle");
label.setAttribute("font-size", "16");
label.setAttribute("fill", "#7ca0d9");
label.setAttribute("font-weight", "bold");
label.textContent = Math.round(minValue + i * valuePerBlock);
labelsGroup.appendChild(label);
}
}
// 控制进度
function setProgress(val) {
drawGaugeBlocks(val);
document.getElementById('gauge-value').textContent = val;
document.getElementById('progressNum').textContent = val;
}
// 初始化
drawGaugeLabels();
setProgress(0);
// 监听滑块
document.getElementById('progressSlider').addEventListener('input', function() {
setProgress(Number(this.value));
});
</script>
</body>
</html>
四、总结
通过以上步骤,你可以创建一个精美的SVG圆形仪表盘进度条。这个仪表盘具有渐变色背景、动态进度块、刻度数字和滑块控制功能。你可以通过调整代码中的参数来改变仪表盘的外观和行为。希望这个教程对你有所帮助! 以上制作的是一个最简单的一个带刻度的仪表盘,我还录制了一个更加美观的带刻度的仪表盘的视频教程,有兴趣的小伙伴可以点击查看。
