我是设计师邱兴,一个学习前端的设计师,今天给大家制作一个用SVG实现的带刻度的仪表盘,SVG相较于Echart来说制作简单,但是效果可以非常丰富。
一、目标
通过SVG和JavaScript实现一个带有指针的渐变色圆形仪表盘,具有以下特点:
- 使用线性渐变色来显示进度。
- 动态更新进度,展示从0到80的下载速度值。
- 包含刻度数字,显示不同的速度值。
- 使用滑块控制进度值。
- 添加指针,直观显示当前进度。
二、所需工具与准备
-
工具:
- 一个文本编辑器(如Notepad++、VS Code等)。
- 浏览器(用于预览效果)。
-
基础准备:
- 确保你对HTML、CSS和JavaScript有一定的了解。
- 确保你对SVG的基本语法有一定了解。
三、代码分析与操作步骤
1. 创建HTML结构
创建一个HTML文件(如Lesson5.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="gaugeGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#2563eb"/>
<stop offset="100%" stop-color="#60a5fa"/>
</linearGradient>
</defs>
gaugeGradient
:定义了一个从#2563eb
到#60a5fa
的线性渐变。
4. 绘制背景和进度圆弧
在<svg>
标签中,添加背景和进度圆弧的路径元素:
ini
<path
id="gauge-bg"
d=""
stroke="#e5e7eb"
stroke-width="18"
fill="none"
stroke-linecap="round"
/>
<path
id="gauge-progress"
d=""
stroke="url(#gaugeGradient)"
stroke-width="18"
fill="none"
stroke-linecap="round"
/>
gauge-bg
:背景圆弧,颜色为浅灰色。gauge-progress
:进度圆弧,使用定义的渐变色。
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">Download</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. 添加指针
在<svg>
标签中,添加指针元素:
arduino
<g id="gauge-pointer" transform="translate(175, 175)">
<line x1="0" y1="0" x2="0" y2="-100" stroke="#2563eb" stroke-width="4" stroke-linecap="round"/>
<circle cx="0" cy="0" r="8" fill="#2563eb"/>
</g>
gauge-pointer
:指针组,初始位置在中心。line
:指针线,从中心向上延伸。circle
:指针基座,位于中心。
7. 编写JavaScript代码
在<script>
标签中,添加以下JavaScript代码来实现动态效果:
ini
const minValue = 0;
const maxValue = 80;
const startAngle = 225;
const endAngle = 495;
const radius = 120;
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, r, startAngle, endAngle) {
const start = polarToCartesian(cx, cy, r, endAngle);
const end = polarToCartesian(cx, cy, r, startAngle);
const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
return [
"M", start.x, start.y,
"A", r, r, 0, largeArcFlag, 0, end.x, end.y
].join(" ");
}
// 绘制背景圆弧
function drawGaugeBg() {
const bgPath = document.getElementById('gauge-bg');
bgPath.setAttribute("d", describeArc(centerX, centerY, radius, startAngle, endAngle));
}
// 绘制进度圆弧
function drawGaugeProgress(value) {
const progressPath = document.getElementById('gauge-progress');
const percent = (value - minValue) / (maxValue - minValue);
const angle = startAngle + percent * (endAngle - startAngle);
if (value === 0) {
progressPath.setAttribute("d", "");
} else {
progressPath.setAttribute("d", describeArc(centerX, centerY, radius, startAngle, angle));
}
}
// 绘制刻度数字
function drawGaugeLabels() {
const labelsGroup = document.getElementById('gauge-labels');
labelsGroup.innerHTML = '';
const numLabels = 8;
for (let i = 0; i <= numLabels; i++) {
const percent = i / numLabels;
const angle = startAngle + percent * (endAngle - startAngle);
const pos = polarToCartesian(centerX, centerY, radius + 28, 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 + percent * (maxValue - minValue));
labelsGroup.appendChild(label);
}
}
// 控制进度
function setProgress(val) {
drawGaugeProgress(val);
document.getElementById('gauge-value').textContent = val;
document.getElementById('progressNum').textContent = val;
// 更新指针角度
const percent = (val - minValue) / (maxValue - minValue);
const angle = startAngle + percent * (endAngle - startAngle);
document.getElementById('gauge-pointer').setAttribute("transform", `translate(175, 175) rotate(${angle})`);
}
// 初始化
drawGaugeBg();
drawGaugeLabels();
setProgress(0);
// 监听滑块
document.getElementById('progressSlider').addEventListener('input', function() {
setProgress(Number(this.value));
});
8. 完整代码
将上述代码整合到一个HTML文件中,完整的代码如下:
ini
<!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="gaugeGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#2563eb"/>
<stop offset="100%" stop-color="#60a5fa"/>
</linearGradient>
</defs>
<path
id="gauge-bg"
d=""
stroke="#e5e7eb"
stroke-width="18"
fill="none"
stroke-linecap="round"
/>
<path
id="gauge-progress"
d=""
stroke="url(#gaugeGradient)"
stroke-width="18"
fill="none"
stroke-linecap="round"
/>
<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">Download</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>
<g id="gauge-pointer" transform="translate(175, 175)">
<line x1="0" y1="0" x2="0" y2="-100" stroke="#2563eb" stroke-width="4" stroke-linecap="round"/>
<circle cx="0" cy="0" r="8" fill="#2563eb"/>
</g>
</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 startAngle = 225;
const endAngle = 495;
const radius = 120;
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, r, startAngle, endAngle) {
const start = polarToCartesian(cx, cy, r, endAngle);
const end = polarToCartesian(cx, cy, r, startAngle);
const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
return [
"M", start.x, start.y,
"A", r, r, 0, largeArcFlag, 0, end.x, end.y
].join(" ");
}
// 绘制背景圆弧
function drawGaugeBg() {
const bgPath = document.getElementById('gauge-bg');
bgPath.setAttribute("d", describeArc(centerX, centerY, radius, startAngle, endAngle));
}
// 绘制进度圆弧
function drawGaugeProgress(value) {
const progressPath = document.getElementById('gauge-progress');
const percent = (value - minValue) / (maxValue - minValue);
const angle = startAngle + percent * (endAngle - startAngle);
if (value === 0) {
progressPath.setAttribute("d", "");
} else {
progressPath.setAttribute("d", describeArc(centerX, centerY, radius, startAngle, angle));
}
}
// 绘制刻度数字
function drawGaugeLabels() {
const labelsGroup = document.getElementById('gauge-labels');
labelsGroup.innerHTML = '';
const numLabels = 8;
for (let i = 0; i <= numLabels; i++) {
const percent = i / numLabels;
const angle = startAngle + percent * (endAngle - startAngle);
const pos = polarToCartesian(centerX, centerY, radius + 28, 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 + percent * (maxValue - minValue));
labelsGroup.appendChild(label);
}
}
// 控制进度
function setProgress(val) {
drawGaugeProgress(val);
document.getElementById('gauge-value').textContent = val;
document.getElementById('progressNum').textContent = val;
// 更新指针角度
const percent = (val - minValue) / (maxValue - minValue);
const angle = startAngle + percent * (endAngle - startAngle);
document.getElementById('gauge-pointer').setAttribute("transform", `translate(175, 175) rotate(${angle})`);
}
// 初始化
drawGaugeBg();
drawGaugeLabels();
setProgress(0);
// 监听滑块
document.getElementById('progressSlider').addEventListener('input', function() {
setProgress(Number(this.value));
});
</script>
</body>
</html>
四、总结
通过以上步骤,你可以创建一个精美的SVG渐变仪表盘,并添加指针来直观显示当前进度。这个仪表盘具有渐变色背景、动态进度圆弧、刻度数字、滑块控制功能和指针。你可以通过调整代码中的参数来改变仪表盘的外观和行为。希望这个教程对你有所帮助!以上制作的是一个最简单的一个带刻度的仪表盘,我还录制了一个更加美观的带刻度的仪表盘的视频教程,有兴趣的小伙伴可以点击查看。
