SVG数据可视化组件基础教程8:自定义水波球

我是设计师邱兴,一个学习前端的设计师,今天给大家制作一个用SVG实现的自定义水波球,SVG相较于Echart来说制作简单,但是效果可以非常丰富。

一、目标

通过HTML、CSS和JavaScript创建一个带有水波动画效果的SVG水波球,实现以下功能:

  1. 使用CSS动画实现水波效果。
  2. 通过滑块动态调整水波球的进度。
  3. 根据进度值自动调整文字颜色以提高可读性。

二、所需工具与准备

  1. 工具

    • 一个文本编辑器(如Notepad++、VS Code等)。
    • 浏览器(用于预览效果)。
  2. 基础准备

    • 确保你对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动画实现水波效果,通过滑块动态调整水波球的进度,并根据进度值自动调整文字颜色以提高可读性。你可以通过调整代码中的参数来改变水波球的外观和行为。希望这个教程对你有所帮助! 以上制作的是一个最简单的一个带刻度的仪表盘,我还录制了一个更加美观的带刻度的仪表盘的视频教程,有兴趣的小伙伴可以点击查看。

相关推荐
OEC小胖胖4 小时前
去中心化身份:2025年Web3身份验证系统开发实践
前端·web3·去中心化·区块链
vvilkim5 小时前
Electron 进程间通信(IPC)深度优化指南
前端·javascript·electron
ai小鬼头7 小时前
百度秒搭发布:无代码编程如何让普通人轻松打造AI应用?
前端·后端·github
漂流瓶jz7 小时前
清除浮动/避开margin折叠:前端CSS中BFC的特点与限制
前端·css·面试
前端 贾公子7 小时前
在移动端使用 Tailwind CSS (uniapp)
前端·uni-app
散步去海边7 小时前
Cursor 进阶使用教程
前端·ai编程·cursor
清幽竹客7 小时前
vue-30(理解 Nuxt.js 目录结构)
前端·javascript·vue.js
weiweiweb8887 小时前
cesium加载Draco几何压缩数据
前端·javascript·vue.js
幼儿园技术家7 小时前
微信小店与微信小程序简单集成指南
前端
我不吃饼干9 天前
鸽了六年的某大厂面试题:你会手写一个模板引擎吗?
前端·javascript·面试