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

前言

大家好,我是辉夜真是太可爱啦,最近有一个需求,是实现渐变圆环饼图,效果如图

conic-gradient

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

html 复制代码
<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;
}

可以看到目前的效果如图

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

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

css 复制代码
mask: radial-gradient(
  transparent,
  transparent 47px,
  #000 48px,
  #000 48px,
  #000 100%
);

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

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

当然,别忘记设置 pie-item 的颜色。

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

也别忘记剩余的颜色

css 复制代码
&: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%
  );
}

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

数据对接

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

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

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

javascript 复制代码
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

javascript 复制代码
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 应用在模板中。

html 复制代码
<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>

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

组件完整代码

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

html 复制代码
<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>
相关推荐
gqkmiss10 分钟前
Chrome 浏览器 131 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器·chrome devtools
Summer不秃16 分钟前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰20 分钟前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
Viktor_Ye26 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm28 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
乐闻x1 小时前
Vue.js 性能优化指南:掌握 keep-alive 的使用技巧
前端·vue.js·性能优化
一条晒干的咸魚1 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
Amd7941 小时前
Nuxt.js 应用中的 webpack:compiled 事件钩子
前端·webpack·开发·编译·nuxt.js·事件·钩子
生椰拿铁You1 小时前
09 —— Webpack搭建开发环境
前端·webpack·node.js
狸克先生2 小时前
如何用AI写小说(二):Gradio 超简单的网页前端交互
前端·人工智能·chatgpt·交互