思路:
1.先利用conic-gradient属性画一个圆,然后再叠加
效果图
javascript
<template>
<div class="ring">
<div class="content">
<slot></slot>
</div>
</div>
</template>
<script>
import { defineComponent, reactive, toRefs, computed, onMounted } from "vue";
export default defineComponent({
props: {
width: {
type: Number,
default: 200,
},
height: {
type: Number,
default: 200,
},
color: {
type: String,
default: "#fcb844",
},
angle: {
type: Number,
default: 360,
},
total: {
type: Number,
default: 360,
},
unit: {
type: String,
default: "px",
},
interval: {
type: Number,
default: 20,
},
autoPlay: {
type: Boolean,
default: true,
},
},
setup(props, { emit }) {
const datas = reactive({
timer: null,
sportAge: 0,
});
// 设置渐变
const bgImage = computed(() => {
datas.sportAge = props.autoPlay ? datas.sportAge : currentAngle.value;
return `conic-gradient(${props.color} 0deg, ${props.color} ${datas.sportAge}deg, transparent 0deg)`;
});
// 当前度数(deg)
const currentAngle = computed(() => {
return ((360 / props.total) * props.angle).toFixed(2);
});
// 单位前缀
const unitSuffix = computed(() => {
return (data, interval = 0) => {
return data - interval + props.unit;
};
});
//动画
const animationPlay = () => {
datas.timer = setInterval(() => {
if (currentAngle.value <= datas.sportAge) {
clearInterval(datas.timer);
datas.timer = null;
return;
}
datas.sportAge++;
}, 10);
};
onMounted(() => {
props.autoPlay ? animationPlay() : "";
});
return {
...toRefs(datas),
bgImage,
unitSuffix,
};
},
});
</script>
<style lang="less" scoped>
.ring {
position: relative;
width: v-bind(unitSuffix(width));
height: v-bind(unitSuffix(height));
display: flex;
align-items: center;
justify-content: center;
&::before {
content: "";
position: absolute;
left: 0;
top: 0;
width: v-bind(unitSuffix(width));
height: v-bind(unitSuffix(height));
// background-color: red; /* 对于不支持渐变的浏览器 */
background-image: v-bind(bgImage);
border-radius: 50%;
transform: scale(-1, 1);
}
&::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: v-bind(unitSuffix(width, interval));
height: v-bind(unitSuffix(height, interval));
border-radius: 50%;
background-color: #ffffff;
}
.content {
position: absolute;
z-index: 3;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>
javascript
<template>
<div class="ring_box">
<div class="inner">
<ring
:width="180"
:height="180"
:color="innerColor"
:z-index="2"
:interval="interval"
>
</ring>
<div class="out">
<ring :total="3454" :angle="2234" :interval="interval" :z-index="4">
<ring
:width="175"
:height="175"
:color="innerColor"
:interval="interval / 2"
:z-index="4"
>
<div>内容</div>
</ring>
</ring>
</div>
</div>
</div>
</template>
<script lang="ts">
import ring from "./ring.vue";
import { defineComponent, reactive, toRefs } from "vue";
export default defineComponent({
components: {
ring,
},
setup(props) {
const datas = reactive({
width: 180,
height: 180,
outColor: "",
innerColor: "#feedd1",
interval: 40,
});
return {
...toRefs(datas),
};
},
});
</script>
<style lang="less" scoped>
.ring_box {
width: 300px;
height: 300px;
display: flex;
align-items: center;
justify-items: center;
position: relative;
.out,
.inner {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
</style>