Canvas 射线渐变问题求助 (有偿)

2022-11-24 22:59:28 +08:00
 Seaony

如题,需求如下图,实现这样一个图表,目前实现了环形图,但是渐变卡住了。

设计稿:

目前实现:

迫于不精通 Canvas ,想请教 v 友如何实现这样的环形渐变。

代码如下

    async drawCanvas () {
      await Taro._helper.waiting(200)
      var exer_data = (this.archive.score_exercise - 60) * 12;
      var skin_data = (this.archive.score_skin - 60) * 12;
      var climate_data = (this.archive.score_climate - 60) * 12;
      var AQI_data = (this.archive.score_air_quality - 60) * 12;
      var uv_data = (this.archive.score_uv - 60) * 12;
      var Mind_data = (this.archive.score_mind - 60) * 12;
      var Nutrition_data = (this.archive.score_nutrition - 60) * 12;

      wx.createSelectorQuery()
      .select('#myCanvas') // 在 W
      .fields({ node: true, size: true })// XML 中填入的 id
      .exec((res) => {

        const blob = (mask) => {
          ctx.beginPath()
          mask(data)
          ctx.clip()
        }

        const drawBars = (opacity, thickness, radius, shadow = false)  => {
          ctx.rotate(3.1)
          let colors = ["#F84F45", "#FE5C1E", "#FFE145", "#2EDC90", "#1CEEFC", "#3033FC", "#F117DB"];
          let t = -1
          // const gnt = ctx.createLinearGradient(0, 134, 20, 134)
          // gnt.addColorStop(0/6, colors[0])
          // gnt.addColorStop(1/6, colors[1])
          // gnt.addColorStop(2/6, colors[2])
          // gnt.addColorStop(3/6, colors[3])
          // gnt.addColorStop(4/6, colors[4])
          // gnt.addColorStop(5/6, colors[5])
          // gnt.addColorStop(1, colors[6])

          d3.range(totalBars).forEach(i => {
            ctx.rotate(barThickness)
            if (i % 2 === 0) {
              // if (i % bars === 0) {
              //   ctx.fillStyle = 'rgba(255,255,255,0)'
              // } else {
              //
              // }
              if (parseInt(i % 20) == 0) {
                t++
              }
              if (shadow) {
                ctx.fillStyle = d3.rgb('#C2EFFF')
              } else {
                // ctx.fillStyle = gnt
                ctx.fillStyle = d3.rgb(colors[t])
              }
              ctx.fillRect(0, 0, thickness, radius)
            }
          })
        }

        const drawCircle = (radius)  => {
          ctx.beginPath()
          ctx.shadowOffsetX = 0;
          ctx.shadowOffsetY = 0;
          ctx.shadowBlur = 8;
          ctx.shadowColor = '#DDF4FF';
          ctx.fillStyle = 'white'
          ctx.arc(0, 0, radius, 0, 2 * Math.PI)
          ctx.fill()
        }

        var c = res[0].node
        var ctx = c.getContext('2d')

        const dpr = wx.getSystemInfoSync().pixelRatio
        c.width = res[0].width * dpr
        c.height = res[0].height * dpr
        ctx.scale(dpr, dpr)

        var data = [exer_data, skin_data, climate_data, AQI_data, uv_data, Mind_data, Nutrition_data];
        var width = res[0].width

        var category = 7
        var bars = 20
        var totalBars = category * bars

        var innerCircleRadius = 20
        var barHeight = (width - 60)/2
        var shadowInnerCircle = innerCircleRadius - 20
        var shadowBarHeight = barHeight + 10
        var extent = d3.extent([0, 500])
        var barThickness = 2 * Math.PI / totalBars
        var barWidth = 40 * barThickness
        var shadowExtent = d3.extent([0, 500 + 30])
        var scales = {}

        scales.bar = d3.scaleLinear().domain(extent).range([innerCircleRadius + 5, innerCircleRadius + barHeight - 15])
        scales.shadow = d3.scaleLinear().domain(shadowExtent).range([shadowInnerCircle, shadowBarHeight])

        scales.bar.range([0, innerCircleRadius + barHeight - 20])

        var masks = {}
        var categoryArcThickness = 2 * Math.PI / 7
        var categoryMaxOffset = categoryArcThickness * 0.5

        masks.bar = d3.radialLine()
        .radius(d => {
          return scales.bar(d)
        })
        .angle((d, i) => {
          return (i * categoryArcThickness) - categoryMaxOffset
        })
        .curve(d3.curveCardinalClosed)
        .context(ctx)

        masks.shadow = d3.radialLine()
        .radius(d => {
          return scales.shadow(d) + 25
        })
        .angle((d, i) => {
          return (i * categoryArcThickness) - categoryMaxOffset
        })
        .curve(d3.curveCardinalClosed)
        .context(ctx)

        ctx.translate(width / 2, width / 2)

        ctx.save()
        ctx.setTransform(1, 0, 0, 1, 0, 0)
        ctx.clearRect(0, 0, 3 * width, 3 * width)

        ctx.restore()
        ctx.save()

        ctx.beginPath()
        ctx.rotate(3.1)
        d3.range(totalBars).forEach(i => {
          ctx.rotate(barThickness)
          if (i % 2 === 0) {
            if (i % bars === 0) {
              const gnt = ctx.createLinearGradient(0, 0, 0, width/2)
              gnt.addColorStop(0, '#C7C3D2')
              gnt.addColorStop(1, '#FFFFFF')
              ctx.fillStyle = gnt
              // ctx.fillStyle = 'rgba(230,230,230, .5)'
              ctx.fillRect(0, 0, 1.5, width/2)
            } else {
            }
          }
        })

        ctx.restore()
        ctx.save()
        blob(masks.shadow)
        drawBars(1, 1.5, 200, true)
        ctx.restore()
        ctx.save()
        ctx.beginPath()
        blob(masks.bar)
        drawBars(1, 1.5, 200)
        ctx.closePath()
        drawCircle(innerCircleRadius - 5)
        ctx.restore()
        ctx.save()
      })
    }

如果可以直接有偿帮我完成这个需求就更好了 XD

详聊绿色:Y2FsaWJ1b3Jz

1683 次点击
所在节点    程序员
9 条回复
codehz
2022-11-24 23:29:36 +08:00
RGB 不好渐变,用 HSL 不就好了,直接把角度填上去)
Aloento
2022-11-24 23:30:48 +08:00
《神力》
mayliya
2022-11-25 00:35:10 +08:00
蹲一个解决方案,学习学习🙃
Imindzzz
2022-11-25 00:52:20 +08:00
代码没注释没看。我的思路是渐变画底图,用上面这些长条去剪辑,可以参考刮刮乐的实现方式,主要 api 是 globalCompositeOperation

https://juejin.cn/post/6844903926899163150

不过你这个渐变有点特别,又是线性的又是经向的,我不知道用代码怎么画,那底图可以直接用图片,

我都不知道设计师是画出来的,可以问问他思路,最好能把底图也用代码画出来。
tool2d
2022-11-25 01:36:55 +08:00
SVG 很容易实现 path 渐变色。
chaoschick
2022-11-25 07:56:15 +08:00
我觉得如果单个线条上也要有渐变色的话,可以先渐变的环,然后画白条。如果单个线条是单一颜色,可以画一个隐藏的 canvas 线性渐变条,然后用长条所处的度数除以 360 ,然后用这个数从线性渐变条获取对应的颜色值(大概就是这个意思吧)
Mutoo
2022-11-25 08:06:30 +08:00
圆锥渐变跟射线同心的话,射线上的颜色是不会变的。两个颜色之间的插值用 hue interpolation 比 rgb interpolation 要好。

https://www.alanzucconi.com/2016/01/06/colour-interpolation/
Imindzzz
2022-11-25 08:59:52 +08:00
Seaony
2022-11-25 16:26:15 +08:00
@chaoschick 感谢,研究了一天,最后的实现方案是线条本身颜色不变,单独写了个 canvas 渐变长条提取了所有线条的颜色,然后写死在代码里了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/897725

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX