likes
comments
collection
share

如何用css3实现一个动态渐变圆环图

作者站长头像
站长
· 阅读数 13

前言

大家好,我是辉夜真是太可爱啦,最近有一个需求,是实现动态渐变圆环饼图,效果如图(至于为什么没用echarts,主要是项目老旧庞大,echarts版本不方便进行升级,故打算自己手搓)

如何用css3实现一个动态渐变圆环图

conic-gradient

首先,从渐变分析,这里需要使用角向渐变。

<div class="circle-chart-component-box">
  <div
    class="pie-item"
    :style="{
      background: `conic-gradient(var(--one-color) 0%,transparent 20%)`,
    }"
  ></div>
</div>

.circle-chart-component-box {
  position: relative;
  --one-color: #ff67a8;
  --two-color: #6eec9b;
  --three-color: #f97b7b;
  --four-color: #4ae4f0;
  --five-color: #f4aa6a;

  .pie-item {
    position: absolute;
    width: 144px;
    height: 144px;
}

可以看到目前的效果如图

如何用css3实现一个动态渐变圆环图

那我们再给他一个 border-radius: 50%;

如何用css3实现一个动态渐变圆环图

接下来,我们使用 mask ,将中间的部分隐藏,这样子就能实现一个圆环啦。

mask: radial-gradient(
  transparent,
  transparent 47px,
  #000 48px,
  #000 48px,
  #000 100%
);

如何用css3实现一个动态渐变圆环图

接下来,使用 before 伪类,在 pie-item 头部加上一个圆圈。

&::before {
  content: '';
  position: absolute;
  inset: 0;
  width: 24px;
  height: 24px;
  top: 0;
  left: 60px;
  border-radius: 50%;
}

当然,别忘记设置小圆点的颜色。

&:nth-child(1)::before {
  background: linear-gradient(
    90deg,
    var(--one-color) 50%,
    transparent 51%,
    transparent 100%
  );
}

也别忘记剩余的颜色

&:nth-child(2)::before {
  background: linear-gradient(
    90deg,
    var(--two-color) 50%,
    transparent 51%,
    transparent 100%
  );
}
&:nth-child(3)::before {
  background: linear-gradient(
    90deg,
    var(--three-color) 50%,
    transparent 51%,
    transparent 100%
  );
}
&:nth-child(4)::before {
  background: linear-gradient(
    90deg,
    var(--four-color) 50%,
    transparent 51%,
    transparent 100%
  );
}
&:nth-child(5)::before {
  background: linear-gradient(
    90deg,
    var(--five-color) 50%,
    transparent 51%,
    transparent 100%
  );
}

如何用css3实现一个动态渐变圆环图

接下来,我们就完成了第一个圆环,接下来就是让其余的元素算旋转的角度即可。

数据对接

假设我们当前的值为 data: [8, 7, 6, 5, 4] ,我们先计算出总数。

computed: {
  total() {
    return this.data?.reduce((prev = 0, curr) => prev + curr) || 0
  },
},

然后我们计算出每项在圆环中的百分比,值为 circleData

this.circleData = [
  Math.ceil((this.data[0] / this.total) * 100) || 0,
  Math.ceil((this.data[1] / this.total) * 100) || 0,
  Math.ceil((this.data[2] / this.total) * 100) || 0,
  Math.ceil((this.data[3] / this.total) * 100) || 0,
  Math.ceil((this.data[4] / this.total) * 100) || 0,
]

然后计算出每个圆环的旋转角度,值为 rotateData

const num1 = Math.ceil((this.data[0] / this.total) * 100) || 0
const num2 =
  num1 + (Math.ceil((this.data[1] / this.total) * 100) || 0)
const num3 = num2 + Math.ceil((this.data[2] / this.total) * 100) || 0
const num4 = num3 + Math.ceil((this.data[3] / this.total) * 100) || 0
this.rotateData = [num1, num2, num3, num4]

最后将 rotateData 以及 circleData 应用在模板中。

<div
  class="pie-item"
  v-if="circleData[0]"
  :style="{
    background: `conic-gradient(var(--one-color) 0%,transparent ${circleData[0]}%)`,
  }"
></div>
<div
  class="pie-item"
  v-if="circleData[1]"
  :style="{
    background: `conic-gradient(var(--two-color) 0%,transparent ${circleData[1]}%)`,
    transform: `rotate(${(rotateData[0] / 100) * 360}deg)`,
  }"
></div>
<div
  class="pie-item"
  v-if="circleData[2]"
  :style="{
    background: `conic-gradient(var(--three-color) 0,transparent ${circleData[2]}%)`,
    transform: `rotate(${(rotateData[1] / 100) * 360}deg)`,
  }"
></div>
<div
  class="pie-item"
  v-if="circleData[3]"
  :style="{
    background: `conic-gradient(var(--four-color) 0%,transparent ${circleData[3]}%)`,
    transform: `rotate(${(rotateData[2] / 100) * 360}deg)`,
  }"
></div>
<div
  class="pie-item"
  v-if="circleData[4]"
  :style="{
    background: `conic-gradient(var(--five-color) 0%,transparent ${circleData[4]}%)`,
    transform: `rotate(${(rotateData[3] / 100) * 360}deg)`,
  }"
></div>

如何用css3实现一个动态渐变圆环图

然后就完成了该图表,可能这个数据不太明显,我们将它的数据进行修改为 [20, 7, 6, 5, 4]

如何用css3实现一个动态渐变圆环图

组件完整代码

最终,我们将其封装为一个组件,仅需传入 data 即可,格式参考为 [20, 7, 6, 5, 4]

<template>
  <div class="circle-chart-component-box">
    <div
      class="pie-item"
      v-if="circleData[0]"
      :style="{
        background: `conic-gradient(var(--one-color) 0%,transparent ${circleData[0]}%)`,
      }"
    ></div>
    <div
      class="pie-item"
      v-if="circleData[1]"
      :style="{
        background: `conic-gradient(var(--two-color) 0%,transparent ${circleData[1]}%)`,
        transform: `rotate(${(rotateData[0] / 100) * 360}deg)`,
      }"
    ></div>
    <div
      class="pie-item"
      v-if="circleData[2]"
      :style="{
        background: `conic-gradient(var(--three-color) 0,transparent ${circleData[2]}%)`,
        transform: `rotate(${(rotateData[1] / 100) * 360}deg)`,
      }"
    ></div>
    <div
      class="pie-item"
      v-if="circleData[3]"
      :style="{
        background: `conic-gradient(var(--four-color) 0%,transparent ${circleData[3]}%)`,
        transform: `rotate(${(rotateData[2] / 100) * 360}deg)`,
      }"
    ></div>
    <div
      class="pie-item"
      v-if="circleData[4]"
      :style="{
        background: `conic-gradient(var(--five-color) 0%,transparent ${circleData[4]}%)`,
        transform: `rotate(${(rotateData[3] / 100) * 360}deg)`,
      }"
    ></div>
  </div>
</template>

<script>
export default {
  name: 'circleChart',

  props: {
    data: {
      type: Array,
      default: () => [],
    },
  },

  data() {
    return {
      circleData: [0, 0, 0, 0, 0],
      rotateData: [0, 0, 0, 0],
    }
  },

  computed: {
    total() {
      return this.data?.reduce((prev = 0, curr) => prev + curr) || 0
    },
  },

  watch: {
    data: {
      handler(newValue) {
        if (newValue.length > 0) {
          this.circleData = [
            Math.ceil((newValue[0] / this.total) * 100) || 0,
            Math.ceil((newValue[1] / this.total) * 100) || 0,
            Math.ceil((newValue[2] / this.total) * 100) || 0,
            Math.ceil((newValue[3] / this.total) * 100) || 0,
            Math.ceil((newValue[4] / this.total) * 100) || 0,
          ]

          const num1 = Math.ceil((newValue[0] / this.total) * 100) || 0
          const num2 = num1 + (Math.ceil((newValue[1] / this.total) * 100) || 0)
          const num3 = num2 + (Math.ceil((newValue[2] / this.total) * 100) || 0)
          const num4 = num3 + (Math.ceil((newValue[3] / this.total) * 100) || 0)
          this.rotateData = [num1, num2, num3, num4]
        }
      },
      deep: true,
      immediate: true,
    },
  },
}
</script>

<style lang="scss" scoped>
.circle-chart-component-box {
  position: relative;
  --one-color: #ff67a8;
  --two-color: #6eec9b;
  --three-color: #f97b7b;
  --four-color: #4ae4f0;
  --five-color: #f4aa6a;

  .pie-item {
    position: absolute;
    width: 144px;
    height: 144px;
    border-radius: 50%;
    mask: radial-gradient(
      transparent,
      transparent 47px,
      #000 48px,
      #000 48px,
      #000 100%
    );

    &:nth-child(1)::before {
      background: linear-gradient(
        90deg,
        var(--one-color) 50%,
        transparent 51%,
        transparent 100%
      );
    }
    &:nth-child(2)::before {
      background: linear-gradient(
        90deg,
        var(--two-color) 50%,
        transparent 51%,
        transparent 100%
      );
    }
    &:nth-child(3)::before {
      background: linear-gradient(
        90deg,
        var(--three-color) 50%,
        transparent 51%,
        transparent 100%
      );
    }
    &:nth-child(4)::before {
      background: linear-gradient(
        90deg,
        var(--four-color) 50%,
        transparent 51%,
        transparent 100%
      );
    }
    &:nth-child(5)::before {
      background: linear-gradient(
        90deg,
        var(--five-color) 50%,
        transparent 51%,
        transparent 100%
      );
    }

    &::before {
      content: '';
      position: absolute;
      inset: 0;
      width: 24px;
      height: 24px;
      top: 0;
      left: 60px;
      border-radius: 50%;
    }
  }
}
</style>
转载自:https://juejin.cn/post/7313979390839685157
评论
请登录