vite vue3 用SVG实现3D圆柱体进度条

563 阅读1分钟

手写3D圆柱体,实现进度条效果

分三部分,path属性实现上部柱体,下部柱体,顶部椭圆效果;再用linearGradient属性实现背景色的线性渐变

项目用vite vue3搭建实现 组件代码如下,components/cylinder3d.vue

<template>
  <svg
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    :width="`${zT.zTW}`"
    :height="`${zT.zTH + 20}px`"
    fill="transparent"
  >
    <defs>
      <!-- 下部柱体 -->
      <linearGradient
        id="myLinearGradient1"
        x1="0%"
        y1="0%"
        x2="0%"
        y2="100%"
        spreadMethod="pad"
      >
        <stop offset="0%" stop-color="rgb(0, 89, 192)" stop-opacity="1" />
        <stop offset="100%" stop-color="rgb(116, 170, 255)" stop-opacity="1" />
      </linearGradient>
      <!-- 上部柱体 -->
      <linearGradient
        id="myLinearGradient3"
        x1="0%"
        y1="0%"
        x2="60%"
        y2="100%"
        spreadMethod="pad"
      >
        <stop offset="0%" stop-color="rgb(44,44,44)" stop-opacity="0.2" />
        <stop offset="75%" stop-color="rgb(77,77,77)" stop-opacity="0.3" />
        <stop offset="100%" stop-color="rgb(99,99,99)" stop-opacity="0.5" />
      </linearGradient>
      <!-- 顶部椭圆 -->
      <linearGradient
        id="myLinearGradient2"
        x1="0%"
        y1="0%"
        x2="60%"
        y2="100%"
        spreadMethod="pad"
      >
        <stop offset="0%" stop-color="rgb(129,157,190)" stop-opacity="0.2" />
        <stop offset="100%" stop-color="rgb(89,130,196)" stop-opacity="0.5" />
      </linearGradient>
      <linearGradient id="gradient">
        <stop offset="0%" stop-color="#FFCF02" />
        <stop offset="100%" stop-color="#FF7352" />
      </linearGradient>
    </defs>

    <!-- 下部柱体 -->
    <path :d="xbzt" style="fill: url(#myLinearGradient1)" />
    <!-- 上部柱体 -->
    <path :d="sbzt" style="fill: url(#myLinearGradient3)" />
    <!-- 顶部椭圆 -->
    <path :d="dbty" style="fill: url(#myLinearGradient2)" />
    <!-- <text x="10" :y="`${zT.zTH - 10}`" fill="#333">9</text> -->
    <text
      x="50%"
      :y="`${zT.zTH - 10}`"
      alignment-baseline="middle"
      text-anchor="middle"
      fill="#fff"
      font-size="14"
    >
      {{ scale }}%
    </text>
    <text
      x="50%"
      :y="`${zT.zTH + 12}`"
      alignment-baseline="middle"
      text-anchor="middle"
      fill="#333"
      font-size="10"
    >
      {{ label }}
    </text>
  </svg>
</template>

<script setup>
import { reactive } from "vue";
const fData = defineProps({
  scale: {
    type: Number,
    default: 0,
  },
  label: {
    type: String,
    default: "比率",
  },
});
const scale = fData.scale;
const label = fData.label;
// 柱体进度高度 52.6是调试出来的
const progress = (52.6 / 100) * scale;
// 柱体快高比例
const zTWHP = 150 / 230;
// 柱体宽度
const zT = reactive({
  zTW: 50,
  zTH: 50 / zTWHP,
  zTRX: 50 / 2,
  zTRY: 12,
});
// 顶部椭圆
const dbty = `
      M${0} ${zT.zTRY}
      A${zT.zTRX} ${zT.zTRY}, 0, 0, 0, ${zT.zTW} ${zT.zTRY}
      A${zT.zTRX} ${zT.zTRY}, 0, 0, 0, ${0} ${zT.zTRY}`;
// 上部柱体
const sbzt = `
      M${0} ${zT.zTH - zT.zTRY - progress}
      A${zT.zTRX} ${zT.zTRY}, 0, 0, 0, ${zT.zTW} ${zT.zTH - zT.zTRY - progress}
      L${zT.zTW}, ${zT.zTRY}
      A${zT.zTRX} ${zT.zTRY}, 0, 0, 1, ${0} ${zT.zTRY}`;
// 下部柱体
const xbzt = `
      M${0} ${zT.zTH - zT.zTRY}
      A${zT.zTRX} ${zT.zTRY}, 0, 0, 0, ${zT.zTW} ${zT.zTH - zT.zTRY}
      L${zT.zTW}, ${zT.zTH - zT.zTRY - progress}
      A${zT.zTRX} ${zT.zTRY}, 0, 0, 0, ${0} ${zT.zTH - zT.zTRY - progress}`;
</script>
<style scoped lang="scss"></style>

views/home.vue中引用cylinder3d组件

<template>
  <div class="page homePage">
    <div class="title">{{ titleText }}</div>
    <cylinder3d :scale="scale" :label="label" />
  </div>
</template>
<script setup>
import cylinder3d from "@/components/cylinder3d.vue";
const titleText = "首页";
const scale = 45;
const label = "这是比率";
</script>
<style lang="scss" scoped>
.homePage {
  @include flexbox($jc: center, $ai: center, $fd: column, $fw: nowrap);
  background-color: #dbdbdb;
  .title {
    margin-bottom: 10px;
  }
}
</style>

效果图eg:

8.png

9.png