我是设计师邱兴,一个学习前端的设计师,今天给大家制作一个用SVG实现的自定义水波球,SVG相较于Echart来说制作简单,但是效果可以非常丰富。
一、目标
通过HTML、CSS和JavaScript创建一个带有水波动画效果的SVG水波球,实现以下功能:
- 使用CSS动画实现水波效果。
- 通过滑块动态调整水波球的进度。
- 根据进度值自动调整文字颜色以提高可读性。
二、所需工具与准备
-
工具:
- 一个文本编辑器(如Notepad++、VS Code等)。
- 浏览器(用于预览效果)。
-
基础准备:
- 确保你对HTML、CSS和JavaScript有一定的了解。
- 确保你对SVG的基本语法有一定了解。
三、代码分析与操作步骤
1. 创建HTML结构
创建一个HTML文件(如Lesson8.html
)并设置基本的HTML结构:
xml
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>SVG 水波球 (CSS 动画版)</title>
<style>
/* 样式部分 */
</style>
</head>
<body>
<div class="container">
<h1>SVG 水波球 (CSS 动画版)</h1>
<div class="wave-ball-container">
<svg id="wave-ball" width="200" height="200" viewBox="0 0 200 200">
<defs>
<clipPath id="ball-clip">
<circle cx="100" cy="100" r="90" />
</clipPath>
</defs>
<circle cx="100" cy="100" r="95" fill="none" stroke="#00bcd4" stroke-width="5" />
<g clip-path="url(#ball-clip)">
<rect x="0" y="0" width="200" height="200" fill="#fff" />
<g id="wave-group">
<path id="wave-path-1" class="wave"
d="M -400,20 c 100,-30, 300,30, 400,0 c 100,-30, 300,30, 400,0 c 100,-30, 300,30, 400,0 L 800 200 L -400 200 Z"
fill="#4dd0e1" opacity="0.6" />
<path id="wave-path-2" class="wave"
d="M -400,25 c 100,-40, 300,40, 400,0 c 100,-40, 300,40, 400,0 c 100,-40, 300,40, 400,0 L 800 200 L -400 200 Z"
fill="#00bcd4" opacity="0.8" />
</g>
</g>
<text id="percentage-text" x="100" y="110" text-anchor="middle" font-size="40" fill="#00796b" font-weight="bold">0%</text>
</svg>
</div>
<div class="slider-container">
<input type="range" id="progress-slider" min="0" max="100" value="50" />
<div id="percentage-label">当前进度: 50%</div>
</div>
</div>
<script>
// JavaScript部分
</script>
</body>
</html>
2. 添加CSS样式
在<style>
标签中,添加以下CSS样式:
css
body {
background: #e0f7fa; /* 淡蓝色背景 */
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0;
font-family: Arial, sans-serif;
}
.container {
background: white;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 40px;
text-align: center;
}
h1 {
color: #00796b;
margin-bottom: 30px;
}
.wave-ball-container {
margin-bottom: 30px;
}
.slider-container {
width: 300px;
}
input[type="range"] {
width: 100%;
cursor: pointer;
}
#percentage-label {
font-size: 20px;
color: #004d40;
font-weight: bold;
margin-top: 10px;
}
/* --- 波浪的 CSS 动画 --- */
#wave-group {
transition: transform 0.5s ease-in-out;
}
.wave {
animation: wave-move linear infinite;
}
#wave-path-1 {
animation-duration: 10s;
}
#wave-path-2 {
animation-duration: 15s;
animation-direction: reverse;
}
@keyframes wave-move {
from {
transform: translateX(0);
}
to {
transform: translateX(-400px);
}
}
- 设置页面背景为淡蓝色。
- 使用
flexbox
布局将水波球和滑块居中显示。 - 设置水波球的样式,包括外圈边框。
- 定义水波动画的CSS关键帧。
3. 编写JavaScript代码
在<script>
标签中,添加以下JavaScript代码来实现动态调整水波球的进度:
ini
const slider = document.getElementById('progress-slider');
const percentageText = document.getElementById('percentage-text');
const percentageLabel = document.getElementById('percentage-label');
const waveGroup = document.getElementById('wave-group');
const svgHeight = 200;
const ballRadius = 90;
const ballTopY = 100 - ballRadius;
const ballBottomY = 100 + ballRadius;
function updateProgress(value) {
const percentage = Number(value);
percentageLabel.textContent = `当前进度: ${percentage}%`;
percentageText.textContent = `${percentage}%`;
// 计算波浪组的垂直位置。
const travelDistance = ballBottomY - ballTopY;
const yPosition = ballBottomY - (travelDistance * percentage / 100);
// 使用 CSS transform 来应用垂直平移。
waveGroup.style.transform = `translateY(${yPosition - 20}px)`;
// 调整文字颜色以获得更好的对比度
if (percentage > 55) {
percentageText.style.fill = '#fff';
} else {
percentageText.style.fill = '#00796b';
}
}
// 监听滑块事件
slider.addEventListener('input', (e) => {
updateProgress(e.target.value);
});
// 使用滑块的默认值进行初始化
updateProgress(slider.value);
4. 完整代码
将上述代码整合到一个HTML文件中,完整的代码如下:
xml
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>SVG 水波球 (CSS 动画版)</title>
<style>
body {
background: #e0f7fa; /* 淡蓝色背景 */
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0;
font-family: Arial, sans-serif;
}
.container {
background: white;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 40px;
text-align: center;
}
h1 {
color: #00796b;
margin-bottom: 30px;
}
.wave-ball-container {
margin-bottom: 30px;
}
.slider-container {
width: 300px;
}
input[type="range"] {
width: 100%;
cursor: pointer;
}
#percentage-label {
font-size: 20px;
color: #004d40;
font-weight: bold;
margin-top: 10px;
}
/* --- 波浪的 CSS 动画 --- */
#wave-group {
transition: transform 0.5s ease-in-out;
}
.wave {
animation: wave-move linear infinite;
}
#wave-path-1 {
animation-duration: 10s;
}
#wave-path-2 {
animation-duration: 15s;
animation-direction: reverse;
}
@keyframes wave-move {
from {
transform: translateX(0);
}
to {
transform: translateX(-400px);
}
}
</style>
</head>
<body>
<div class="container">
<h1>SVG 水波球 (CSS 动画版)</h1>
<div class="wave-ball-container">
<svg id="wave-ball" width="200" height="200" viewBox="0 0 200 200">
<defs>
<clipPath id="ball-clip">
<circle cx="100" cy="100" r="90" />
</clipPath>
</defs>
<circle cx="100" cy="100" r="95" fill="none" stroke="#00bcd4" stroke-width="5" />
<g clip-path="url(#ball-clip)">
<rect x="0" y="0" width="200" height="200" fill="#fff" />
<g id="wave-group">
<path id="wave-path-1" class="wave"
d="M -400,20 c 100,-30, 300,30, 400,0 c 100,-30, 300,30, 400,0 c 100,-30, 300,30, 400,0 L 800 200 L -400 200 Z"
fill="#4dd0e1" opacity="0.6" />
<path id="wave-path-2" class="wave"
d="M -400,25 c 100,-40, 300,40, 400,0 c 100,-40, 300,40, 400,0 c 100,-40, 300,40, 400,0 L 800 200 L -400 200 Z"
fill="#00bcd4" opacity="0.8" />
</g>
</g>
<text id="percentage-text" x="100" y="110" text-anchor="middle" font-size="40" fill="#00796b" font-weight="bold">0%</text>
</svg>
</div>
<div class="slider-container">
<input type="range" id="progress-slider" min="0" max="100" value="50" />
<div id="percentage-label">当前进度: 50%</div>
</div>
</div>
<script>
const slider = document.getElementById('progress-slider');
const percentageText = document.getElementById('percentage-text');
const percentageLabel = document.getElementById('percentage-label');
const waveGroup = document.getElementById('wave-group');
const svgHeight = 200;
const ballRadius = 90;
const ballTopY = 100 - ballRadius;
const ballBottomY = 100 + ballRadius;
function updateProgress(value) {
const percentage = Number(value);
percentageLabel.textContent = `当前进度: ${percentage}%`;
percentageText.textContent = `${percentage}%`;
// 计算波浪组的垂直位置。
const travelDistance = ballBottomY - ballTopY;
const yPosition = ballBottomY - (travelDistance * percentage / 100);
// 使用 CSS transform 来应用垂直平移。
waveGroup.style.transform = `translateY(${yPosition - 20}px)`;
// 调整文字颜色以获得更好的对比度
if (percentage > 55) {
percentageText.style.fill = '#fff';
} else {
percentageText.style.fill = '#00796b';
}
}
// 监听滑块事件
slider.addEventListener('input', (e) => {
updateProgress(e.target.value);
});
// 使用滑块的默认值进行初始化
updateProgress(slider.value);
</script>
</body>
</html>
四、总结
通过以上步骤,你可以创建一个带有水波动画效果的SVG水波球。这个水波球使用CSS动画实现水波效果,通过滑块动态调整水波球的进度,并根据进度值自动调整文字颜色以提高可读性。你可以通过调整代码中的参数来改变水波球的外观和行为。希望这个教程对你有所帮助! 以上制作的是一个最简单的一个带刻度的仪表盘,我还录制了一个更加美观的带刻度的仪表盘的视频教程,有兴趣的小伙伴可以点击查看。
