有趣的css知识又增加了之 conic-gradient + 饼图组件

avatar
@https://www.tuya.com/

本文由团队成员 BingLee1994 撰写,已授权涂鸦大前端独家使用,包括但不限于编辑、标注原创等权益。

你是不是有这种感觉:能用几行css画出来,不用四处找svg或者上传图片,就觉得特别开心呢。那么渐变就可以做很多这样的事情,比如花纹,遮罩,常见图形等。

我们熟知的有线性渐变和锥形渐变,那么今天介绍另一位伙伴 conic-gradient 锥形渐变。

兼容性

主流现代浏览器均兼容。 581db541-7a14-4a20-9e89-f34392436bb9-image.png

所有好玩高效的css特性均不兼容IE11,不要让他成为阻碍进步的绊脚石。

名词解释

如果你初次接触渐变呢,简单了解下:渐变由方向位置,和多个颜色驻点组成。

颜色驻点

一个颜色驻点由颜色和位置组成,用空格隔开,以下图为例: 708ba743-df40-4cf3-a94b-1556739422cd-image.png 可以看到背景由绿→红→蓝,控制颜色的圈圈就是颜色驻点,那么本图中绿和蓝分别在头尾部,红色在25%的位置,那么这个红色驻点表示为:red 25%,处于头尾部可不必写位置。

方向与中心

上图我们明显看到渐变朝右,那么我们可以指定渐变的方向为任意角度,如下面这样 eda8cca0-8201-49ea-840e-9aba2491ef38-image.png 特殊的,锥形渐变和放射渐变还可以指定中心点,放射渐变还可以指定半径,原型椭圆形等。 95979704-00ea-4be5-a34d-424d7de16a2c-image.png

conic-gradient

好,进入正题,什么是conic-gradient(锥形渐变)?顾名思义,看下图 f33c7c4a-ca33-4d95-bed8-8b99f1a7b708-image.png 发现了吗?颜色绕着中心点旋转了一周,就像圆锥一样,这就是锥形渐变,颜色驻点位置如下图红线所示,刚好在半径线上: 4b26b7b2-4877-4d4a-99bd-247277b49b7f-image.png

语法

backgrouns-image: conic-gradient([ 角度 [at 位置] ], 颜色驻点1, 颜色驻点2, 颜色驻点...) 其中中括号表示非必要属性,默认角度为0,位置在元素正中心,大部分场景,默认的就满足需求。
颜色驻点位置除了可以使用百分比,还可以使用角度表示法。

试一试

语法看着有点长,别害怕,让我们写一个css试试效果吧:

div {
  width: 200px;
  height: 200px;
  background-image: conic-gradient(orange, blue 50%, purple)
}

渲染如下:沿着中心转一圈,开始由橙色转到50%变为蓝色,然后由蓝色转到360°变成紫色,就是这么简单!

那么上述50%还可以使用角度表示法: conic-gradient(orange, blue 180deg, purple), 特别地,可以指定两个角度,表示颜色覆盖的角度区域,比如 conic-gradient(orange, blue 180deg 240deg, purple).

c8f11dab-05bf-4165-985e-c86d675664ee-image.png

开始画饼图

光了解而不会实际应用怎么可以呢?本文我们拿常见的饼图为实际应用案例,来更进一步体验锥形渐变。

如下图:橙色区域占30%. 4df6b5d9-e373-49f7-900c-9b1ae310c18a-image.png

差在哪里?

对比下我们上述渐变和这个饼图差在哪里呢?
没错,你发现了吗?饼图背景和橙色扇形区域(下文我们叫进度)之间的衔接处是硬的而不是渐变过渡的。 d6a59dfa-95bf-4942-9589-941a0339fc8d-image.png 怎么做呢?不要着急,让我们先把渐变画出来:

div {
  width: 200px;
  height: 200px;
  background-image: conic-gradient(orange, purple 30%)
}

e58cc51d-5e97-4509-b2bf-8c31e7c34592-image.png

让颜色衔接处变硬

唉~,我们惊喜发现,在起始处0°和360°,是一条橙色直接到紫色的硬线,而不是渐变,因为0° = 360 °,同一个角度位置出现两种颜色,那么两个颜色距离是0,换句话说,留给橙色渐变到紫色的空间长度为0,那自然视觉上就是没有渐变,相连处变硬,颜色直接改变

知道了这个道理,我们就可以轻松画出上述饼图,只需要将上述30%的位置添加两个颜色驻点,这样他们两个就会直接硬性过渡。

让我们修改css代码如下:

div {
  width: 200px;
  height: 200px;
  background-image: conic-gradient(orange 30%, purple 30%);
  /*  30%的位置设置两个驻点橙色和紫色,那么在这个位置直接硬性过渡而无渐变 */
}

d9c15801-eceb-4187-bc8a-84144e83be81-image.png 大功告成,最后不要忘了加个border-radius,so easy!

饼图动起来

这么有趣的东西我们肯定要封装成组件用才能用到真实的场景,上文我们轻松画出了30%的饼图,类似地,通过更改百分比就可以让饼图动起来了,那么我们写个简单地demo控制饼图,我们通过一个滑块控制!

你可以直接拷贝如下html代码做练习

<body>
  <style>
    body {
      height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }

    #pie-chart {
      width: 200px;
      height: 200px;
      border-radius: 100px;
      background-image: conic-gradient(orange 0%, purple 0%);
      margin-bottom: 24px;
    }
  </style>

  <div id="pie-chart"></div>
  <input
    id="slider"
    type="range"
    value="0"
    max="100"
    min="0"
    step="10"
  />

  <script type="text/javascript">
    // js代码写这里
  </script>
</body>

6ceb40ce-5ad5-46cf-ac8b-74f5acbbe2d8-image.png

添加滑块事件

本例中滑块取值为0 ~ 100,对应颜色驻点位置,通过添加滑块的input事件来控制饼图百分比:

<script type="text/javascript">
    const elPieChart = document.getElementById('pie-chart')
    const elSlider = document.getElementById('slider')

    elSlider.oninput = function updateChart() {
      const { value } = elSlider
      //将滑块value转换为驻点位置
      elPieChart.style.backgroundImage = `conic-gradient(orange ${value}%, purple ${value}%)`
    }
  </script>

你看,就是这么短短几句代码,饼图就能动起来了😊! 230c6ade-b5fe-47dd-813d-10ce571a7991-屏幕录制2021-06-05 上午10.43.44.gif

组件可定制

上面紫色饼可能不合你口味,让我们在js代码里换换颜色吧,我们定义两个变量,一个背景色,一个进度颜色,最后修改css背景即可:

<script type="text/javascript">
    // 自定义颜色
    const chartColor = '#C6E9FF'
    const chartProgressColor = '#4589EF'
    
    const elPieChart = document.getElementById('pie-chart')
    const elSlider = document.getElementById('slider')

   // 首次渲染设置颜色
    elPieChart.style.backgroundImage = `conic-gradient(${chartProgressColor} 0%, ${chartColor} 0%)`

    elSlider.oninput = function updateChart() {
      const { value } = elSlider
      //更改饼图进度
      elPieChart.style.backgroundImage = `conic-gradient(${chartProgressColor} ${value}%, ${chartColor} ${value}%)`
    }
  </script>

a1d95f3a-1076-468e-8eb8-f4d18f049dec-image.png

持续优化

你以为写到这里就结束了吗?让我们回过头看看代码,只是增加了个颜色需求,js代码开始变得非常丑陋,主要出现在 elPieChart.style.backgroundImage = `conic-gradient(${chartProgressColor} ${value * 100}%, ${chartColor} ${value * 100}%)

这一坨代码非常刺眼,因为:

  1. 这个css字符串很臭长。
  2. js拼接css字符串因为没有代码提示,所以非常容易写错,中间少个标点符号空格啥的,调试起来太恶心了。
  3. 今天我写的,我能看懂,过几天我再来看,我自己都看不懂了,无可维护性!

如果你在日常组件开发时遇到类似的烦恼,那么好消息是,这是一个典型的使用css变量的应用场景教程传送门

代码可维护性在整个项目中非常重要,不要为了赶进度或者偷懒而忽视。

使用css变量瘦身

现在,让我们把所有样式有关的东西通通留在css代码里,本例中是两个颜色和一个饼图进度,那么相应的,我们要暴露出对应的css变量让js来操作。是的,css代码就应该老老实实呆在css里面,不要出来凑热闹.

修改上面的css代码如下,定义需要暴露给js的变量并赋予初始值:

#pie-chart {
  /* 声明变量并初始化 */
  --chartColor: orange; /* 背景色 */
  --chartProgressColor: purple; /* 进度色 */
  --chartProgress: 0%; /* 进度 */

  width: 200px;
  height: 200px;
  border-radius: 100px;
  /* 使用变量,(有点模板的意思) */
  background-image: conic-gradient(
    var(--chartProgressColor) var(--chartProgress), 
    var(--chartColor) var(--chartProgress)
  );
  margin-bottom: 24px;
}

然后在js里直接操作css变量即可,API传送门

<script type="text/javascript">
    // 自定义颜色
    const chartColor = '#C6E9FF'
    const chartProgressColor = '#4589EF'
    
    const elPieChart = document.getElementById('pie-chart')
    const { style } = elPieChart
    const elSlider = document.getElementById('slider')
    
     // 首次渲染设置颜色
    style.setProperty('--chartColor', chartColor)
    style.setProperty('--chartProgressColor', chartProgressColor)

    elSlider.oninput = function updateChart() {
      const { value } = elSlider
      //更改饼图进度
      style.setProperty('--chartColor', chartColor)
      style.setProperty('--chartProgressColor', chartProgressColor)
      style.setProperty('--chartProgress', `${value}%`)
    }
  </script>

现在我们用更优,可读性更高的变量代替传统css拼接字符串,经过优化后的js代码更加js.

你可以用喜欢的方式继续重构,那么上述操作css变量的部分其实可以再优化,可以写工具类,但是无论如何,终于摆脱了css.

试想,如果我们的组件dom层级很深,还按照老的笨方法,层层查找节点,样式穿透,将会是噩梦,使用css变量,只需专注变量自身,而无需考虑dom层级,并且开发者可以更好维护,也方便了使用者通过css hack的手段去定制样式,希望你能get到这个点。

更多场景

除了饼图,圆环进度条,Loading动画,遮罩等等也是锥形渐变的使用场景,大胆发挥你的想象力吧。

喜欢本文记得三连支持😙