likes
comments
collection
share

css实现优雅的渐变圆弧

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

简介:通过css圆锥渐变实现一个渐变圆弧效果。 实现效果如下:

css实现优雅的渐变圆弧

使用css来实现的原因

首先说明下为什么要直接通过css来实现该效果。 实现环境是在微信小程序中,小程序是使用 Taro react 来实现。

在通过该方案实现之前也尝试使用canvas进行实现,当时只是在浏览器环境进行尝试写了个demo,具体demo实现效果如下:

css实现优雅的渐变圆弧

具体代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <canvas id="canvas"></canvas>
  <script>
    /** @type {HTMLCanvasElement} */
    const canvas = document.getElementById('canvas');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    const ctx = canvas.getContext('2d');

    ctx.beginPath()
    ctx.moveTo(canvas.width / 2, canvas.height / 2)
    ctx.arc(canvas.width / 2, canvas.height / 2, 100, Math.PI * 1.8, Math.PI * 2)
    ctx.strokeStyle = '#000000'
    ctx.closePath()
    ctx.stroke()

    ctx.beginPath()
    ctx.lineWidth = 1
    // 设置间距(参数为无限数组,虚线的样式会随数组循环)
    ctx.setLineDash([5, 5]);
    ctx.arc(canvas.width / 2, canvas.height / 2, 150, Math.PI * 1.6, Math.PI * 2)
    ctx.strokeStyle = '#000000'
    ctx.stroke()
    ctx.closePath()

    ctx.beginPath()
    console.log('sin', Math.cos(Math.PI / 3) * 150);
    ctx.moveTo(canvas.width / 2 + Math.cos(Math.PI * 0.6) * 150, canvas.height / 2 - 150)
    // ctx.moveTo()
    ctx.closePath()

    ctx.beginPath()
    const conicGradient = ctx.createConicGradient(0, canvas.width / 2, canvas.height / 2);
    conicGradient.addColorStop(0, '#000000');
    conicGradient.addColorStop(0.5, '#FFFFFF');
    conicGradient.addColorStop(1, '#000000');
    ctx.fillStyle = conicGradient
    // ctx.moveTo(canvas.width / 2, canvas.height / 2)
    ctx.arc(canvas.width / 2, canvas.height / 2, 50, 0, Math.PI * 2)
    ctx.closePath()
    ctx.fill()

    ctx.beginPath()
    ctx.lineWidth = 8
    ctx.lineCap = 'round'
    ctx.setLineDash([]);
    ctx.arc(canvas.width / 2, canvas.height / 2, 180, 0, Math.PI * 1)
    ctx.strokeStyle = conicGradient
    ctx.stroke()
    ctx.closePath()

    // ctx.beginPath()
    // ctx.arc(canvas.width - 8, canvas.height / 2, 4, 0, Math.PI * 2)
    // ctx.closePath()
    // ctx.fillStyle = '#000000'
    // ctx.fill()
  </script>
</body>

</html>

通过canvas画出该效果后想着在小程序中应该也差不多,以为这样就结束了。直到真正上手在小程序中实现渐变圆弧时,才发现情况好像不大一样,怎么在文档中找不到创建canvas锥形渐变的方法,有点不知所措了,然后尝试使用提供的线性渐变来实现一下,发现效果不太理想,颜色过渡会很生硬,不够自然,pass(个人感觉线性渐变也是可以实现圆润的颜色过渡,将圆弧分位很多份,给每一份以此加上颜色和透明度,感觉好像可行)。实现了一个半圆的渐变圆弧,具体效果如下:

css实现优雅的渐变圆弧

之后想着🤔️,既然canvas的圆锥渐变不能使用,那么能不能使用background的圆锥渐变来实现呢?感觉上是可行的,只是圆弧是需要元素来遮挡实现,圆弧结束的位置过于生硬,不过这些问题感觉都是可以慢慢解决的。接下来直接开干!

css创建一个扇形

background: `conic-gradient(transparent ${startPercent}turn, ${color} ${startPercent}turn, transparent ${endPercent}turn)`,

具体效果:

css实现优雅的渐变圆弧

然后设置一个较小的白色圆,进行遮挡就能够实现圆弧了,具体效果:

css实现优雅的渐变圆弧

这个圆弧的开始处有些生硬,直接画一个小圆,通过三角函数计算对应位置(没一道数学题是白做的😭),定位到对应的位置上就可以了。

具体效果如下:

css实现优雅的渐变圆弧

抽离出来具体实现代码如下:

const initVariable = {
  width: 200,
  height: 200,
  borderWidth: 10,
}

const PieRoundLine = ({
  startPercent = 0,
  endPercent = 0.5,
  color = 'yellowgreen',
}) => {
  return (
    <View
      style={{
        position: 'absolute',
        width: initVariable.width,
        height: initVariable.height,
        background: `conic-gradient(transparent ${startPercent}turn, ${color} ${startPercent}turn, transparent ${endPercent}turn)`,
        borderRadius: '50%',
        padding: initVariable.borderWidth,
        boxSizing: 'border-box',
      }}
    >
      <View
        style={{
          position: 'absolute',
          top:
            initVariable.height / 2 -
            Math.cos(startPercent * 2 * Math.PI) *
              (initVariable.height / 2 - initVariable.borderWidth / 2),
          left:
            initVariable.width / 2 +
            Math.sin(startPercent * 2 * Math.PI) *
              (initVariable.width / 2 - initVariable.borderWidth / 2),
          width: initVariable.borderWidth,
          height: initVariable.borderWidth,
          borderRadius: '50%',
          background: `${color}`,
          transform: `translate(-50%, -50%)`,
        }}
      ></View>
      <View
        style={{
          width: initVariable.width - 2 * initVariable.borderWidth,
          height: initVariable.width - 2 * initVariable.borderWidth,
          background: '#FFFFFF',
          borderRadius: '50%',
          boxSizing: 'border-box',
        }}
      ></View>
    </View>
  )
}

饼图部分

以上部分完成后,整个效果已经算是差不多了,接下来只是实现饼图,通过对上面👆锥形渐变的应用,只需要注意一点点就能够很快的完成该功能了。

将需要展示的饼图位置设置为对应颜色,不需要的位置设置为透明的,多个饼图叠加在一起就能够实现里面的部分了。具体实现效果如下:

css实现优雅的渐变圆弧

抽离出来实现代码如下:

const initPieRoundVariable = {
  width: 130,
  height: 130,
}

const PieRoundPart = ({
  width = initPieRoundVariable.width,
  height = initPieRoundVariable.height,
  startPercent,
  endPercent,
  color,
}) => {
  return (
    <View
      style={{
        position: 'absolute',
        top: '50%',
        left: '50%',
        width: width,
        height: height,
        background: `conic-gradient(transparent ${startPercent}turn, ${color} ${startPercent}turn, ${color} ${endPercent}turn, transparent ${endPercent}turn)`,
        borderRadius: '50%',
        transform: `translate(-50%, -50%)`,
        zIndex: 1,
      }}
    ></View>
  )
}

结尾

最后将圆弧和饼图拼在一起就能实现开始的效果了

css实现优雅的渐变圆弧

完整代码如下:

import Taro from '@tarojs/taro'
import { useEffect } from 'react'
import { View } from '@tarojs/components'

const initVariable = {
  width: 200,
  height: 200,
  borderWidth: 10,
}

const PieRoundLine = ({
  startPercent = 0,
  endPercent = 0.5,
  color = 'yellowgreen',
}) => {
  return (
    <View
      style={{
        position: 'absolute',
        width: initVariable.width,
        height: initVariable.height,
        background: `conic-gradient(transparent ${startPercent}turn, ${color} ${startPercent}turn, transparent ${endPercent}turn)`,
        borderRadius: '50%',
        padding: initVariable.borderWidth,
        boxSizing: 'border-box',
      }}
    >
      <View
        style={{
          position: 'absolute',
          top:
            initVariable.height / 2 -
            Math.cos(startPercent * 2 * Math.PI) *
              (initVariable.height / 2 - initVariable.borderWidth / 2),
          left:
            initVariable.width / 2 +
            Math.sin(startPercent * 2 * Math.PI) *
              (initVariable.width / 2 - initVariable.borderWidth / 2),
          width: initVariable.borderWidth,
          height: initVariable.borderWidth,
          borderRadius: '50%',
          background: `${color}`,
          transform: `translate(-50%, -50%)`,
        }}
      ></View>
      <View
        style={{
          width: initVariable.width - 2 * initVariable.borderWidth,
          height: initVariable.width - 2 * initVariable.borderWidth,
          background: '#FFFFFF',
          borderRadius: '50%',
          boxSizing: 'border-box',
        }}
      ></View>
    </View>
  )
}

const initPieRoundVariable = {
  width: 130,
  height: 130,
}

const PieRoundPart = ({
  width = initPieRoundVariable.width,
  height = initPieRoundVariable.height,
  startPercent,
  endPercent,
  color,
}) => {
  return (
    <View
      style={{
        position: 'absolute',
        top: '50%',
        left: '50%',
        width: width,
        height: height,
        background: `conic-gradient(transparent ${startPercent}turn, ${color} ${startPercent}turn, ${color} ${endPercent}turn, transparent ${endPercent}turn)`,
        borderRadius: '50%',
        transform: `translate(-50%, -50%)`,
        zIndex: 1,
      }}
    ></View>
  )
}

function PieChart() {
  return (
    <View
      style={{
        position: 'relative',
        width: initVariable.width,
        height: initVariable.height,
      }}
    >
      <PieRoundLine startPercent={0} endPercent={0.6} color='red' />
      <PieRoundLine startPercent={0.6} endPercent={0.7} color='black' />
      <PieRoundLine startPercent={0.7} endPercent={1} color='pink' />
      <PieRoundPart
        startPercent={0}
        endPercent={0.6}
        color='red'
        width={145}
        height={145}
      />
      <PieRoundPart startPercent={0.6} endPercent={0.7} color='black' />
      <PieRoundPart startPercent={0.7} endPercent={1} color='pink' />
    </View>
  )
}

export default PieChart