手写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: