代码之家  ›  专栏  ›  技术社区  ›  dotnetCarpenter

如何在Chart.js的甜甜圈图中使用径向画布渐变?

  •  1
  • dotnetCarpenter  · 技术社区  · 4 年前

    我正在尝试创建两个径向渐变,用于 Charts.js doughnut chart .


    enter image description here

    createRadialGradient )使用vanilla javascript和DOM非常简单,如下面的代码段所示:

    'use strict'
    
    const red = "hsla(1, 73.7%, 38.8%, 1)"
    const redDark = "hsla(1, 60%, 30%, 1)"
    const redDarker = "hsla(1, 20%, 20%, 1)"
    const redLight = "hsla(1, 73.7%, 48%, 1)"
    
    const canvasList = document.querySelectorAll('canvas.vanilla')
    
    var {ctx, gradient} = createGradient1(canvasList[0].getContext('2d'))
    ctx.fillStyle = gradient
    drawRect(ctx)
    
    var {ctx, gradient} = createGradient1(canvasList[1].getContext('2d'))
    ctx.strokeStyle = gradient
    ctx.lineWidth = 42
    drawArc(ctx)
    
    var {ctx, gradient} = createGradient2(canvasList[2].getContext('2d'))
    ctx.fillStyle = gradient
    drawRect(ctx)
    
    var {ctx, gradient} = createGradient2(canvasList[3].getContext('2d'))
    ctx.strokeStyle = gradient
    ctx.lineWidth = 42
    drawArc(ctx)
    
    function createGradient1 (ctx) {
        // The inner circle is at x=110, y=90, with radius=30
        // The outer circle is at x=100, y=100, with radius=70
        // ctx.createRadialGradient(x0, y0, r0, x1, y1, r1)
        const gradient = ctx.createRadialGradient(100,100,31, 100,100,70);
    
        // Add three color stops
        const innerColor = redDark
        const mainColor = red
        const outerColor = redLight
        gradient.addColorStop(0, innerColor);
        gradient.addColorStop(.04, innerColor);
        gradient.addColorStop(.05, mainColor);
        gradient.addColorStop(1, outerColor);
    
        return { ctx, gradient }
    }
    function createGradient2 (ctx) {
        // The inner circle is at x=110, y=90, with radius=30
        // The outer circle is at x=100, y=100, with radius=70
        // ctx.createRadialGradient(x0, y0, r0, x1, y1, r1)
        const gradient = ctx.createRadialGradient(100,100,31, 100,100,70);
    
        // Add three color stops
        const innerColor = "hsla(1, 90%, 10%, 1)"
        const mainColor = "hsla(1, 73.7%, 20%, 1)"
        const outerColor = "transparent"
        gradient.addColorStop(0, innerColor);
        gradient.addColorStop(.04, innerColor);
        gradient.addColorStop(.05, mainColor);
        gradient.addColorStop(.7, mainColor);
        gradient.addColorStop(.73, outerColor);
    
        return { ctx, gradient }
    }
    
    function drawRect (ctx) {
        // ctx.fillRect(x, y, width, height)
        ctx.fillRect(20, 20, 160, 160);
    }
    
    function drawArc (ctx) {
        ctx.beginPath();
        // ctx.arc(x, y, radius, startAngle, endAngle [, anticlockwise])
        ctx.arc(100, 100, 50, 0, 2 * Math.PI);
        ctx.stroke()
    }
    .vanilla {
        display: inline-block;
    }
    <canvas class="vanilla" width="180" height="180"></canvas>
    <canvas class="vanilla" width="180" height="180"></canvas>
    <canvas class="vanilla" width="180" height="180"></canvas>
    <canvas class="vanilla" width="180" height="180"></canvas>

    Charts.js ,我得到一个灰色的甜甜圈。使用单个渐变不会更改结果。但是,使用两种Hsla颜色的工作方式与预期相同( red & "white" ).

    /** @type {CanvasRenderingContext2D} */
    const ctx = document.querySelector('.d-goal--canvas').getContext('2d')
    const red = "hsla(1, 73.7%, 38.8%, 1)"
    const { gradient1 } = createGradient1(ctx)
    const { gradient2 } = createGradient2(ctx)
    const donut = new Chart(ctx, {
        type: 'doughnut',
        data: {
            labels: [
                "Pledged",
                "Missing"
            ],
            datasets: [{
                label: "Donations",
                data: [420, 80],
                cubicInterpolationMode: "monotone",
                // borderColor: [red, "white"],
                // backgroundColor: [red, "white"],
                borderColor: [gradient1, gradient2],
                backgroundColor: [gradient1, gradient2],
            }]
        },
        options: {
            legend: {
                display: false
            }
        }
    })
    
    function createGradient1 (ctx) {
        // The inner circle is at x=110, y=90, with radius=30
        // The outer circle is at x=100, y=100, with radius=70
        // ctx.createRadialGradient(x0, y0, r0, x1, y1, r1)
        const gradient = ctx.createRadialGradient(100,100,31, 100,100,70);
    
        // Add three color stops
        const innerColor = "hsla(1, 60%, 30%, 1)"
        const mainColor = red
        const outerColor = "hsla(1, 73.7%, 48%, 1)"
        gradient.addColorStop(0, innerColor);
        gradient.addColorStop(.04, innerColor);
        gradient.addColorStop(.05, mainColor);
        gradient.addColorStop(1, outerColor);
    
        return { ctx, gradient }
    }
    function createGradient2 (ctx) {
        // The inner circle is at x=110, y=90, with radius=30
        // The outer circle is at x=100, y=100, with radius=70
        // ctx.createRadialGradient(x0, y0, r0, x1, y1, r1)
        const gradient = ctx.createRadialGradient(100,100,31, 100,100,70);
    
        // Add three color stops
        const innerColor = "hsla(1, 90%, 10%, 1)"
        const mainColor = "hsla(1, 73.7%, 20%, 1)"
        const outerColor = "transparent"
        gradient.addColorStop(0, innerColor);
        gradient.addColorStop(.04, innerColor);
        gradient.addColorStop(.05, mainColor);
        gradient.addColorStop(.7, mainColor);
        gradient.addColorStop(.73, outerColor);
    
        return { ctx, gradient }
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
    <canvas class="d-goal--canvas"></canvas>

    我做错什么了?

    Jelena Jovanovic's gradient tutorial ,这是一个线性梯度( createLinearGradient )应用于折线图,我看不出我在做什么不同。也许我的梯度区域是错误的,但据我所知,这不应该导致灰色图表。

    The Charts.js documentation has a section about colors CanvasGradient 但只为 createLinearGradient创建 ,这让我觉得它可能是Charts.js中的一个bug。。。

    0 回复  |  直到 4 年前
        1
  •  2
  •   obscure    4 年前

    问题是渐变填充样式如何应用到你的甜甜圈上。我最初的假设是chart.js将负责定位并将渐变缩放到合适的大小以填充甜甜圈。好吧,那是 这个案子。相反,它使用大小;画布上渐变的位置。

    const red = "hsla(1, 73.7%, 38.8%, 1)"
    const gradient = ctx.createRadialGradient(100,100,31, 100,100,70);
    const innerColor = "hsla(1, 60%, 30%, 1)"
    const mainColor = red
    const outerColor = "hsla(1, 73.7%, 48%, 1)"
    gradient.addColorStop(0, innerColor);
    gradient.addColorStop(.04, innerColor);
    gradient.addColorStop(.05, mainColor);
    gradient.addColorStop(1, outerColor);
    

    这将在x=100和y=100处产生直径为140像素的渐变,如:

    坡度为 不是甜甜圈的形状!

    说起来容易做起来难,因为最初我们不知道画布的确切大小,因为chart.js会自动拉伸画布以填充浏览器窗口。

    • 使用chart.js创建甜甜圈,但是 加油了吗
    • 等到chart.js触发 调整事件大小
    • 根据画布的大小计算渐变的尺寸,并在中心绘制
    • 最后用渐变填充甜甜圈的背景色

    const canvas = document.querySelector('.d-goal--canvas');
    const ctx = canvas.getContext('2d')
    const red = "hsla(1, 73.7%, 38.8%, 1)"
    let gradient1;
    let gradient2;
    
    function createGradient1(ctx) {
      const gradient = ctx.createRadialGradient(canvas.width / 2, canvas.height / 2, canvas.height / 4, canvas.width / 2, canvas.height / 2, canvas.height / 2);
    
      const innerColor = "hsla(1, 60%, 30%, 1)"
      const mainColor = red
      const outerColor = "hsla(1, 73.7%, 48%, 1)"
      gradient.addColorStop(0, innerColor);
      gradient.addColorStop(.12, innerColor);
      gradient.addColorStop(.121, mainColor);
      gradient.addColorStop(1, outerColor);
    
      return gradient;
    }
    
    function createGradient2(ctx) {
      const gradient = ctx.createRadialGradient(canvas.width / 2, canvas.height / 2, canvas.height / 4, canvas.width / 2, canvas.height / 2, canvas.height / 2);
    
      const innerColor = "hsla(1, 90%, 10%, 1)"
      const mainColor = "hsla(1, 73.7%, 20%, 1)"
      const outerColor = "transparent"
      gradient.addColorStop(0, innerColor);
      gradient.addColorStop(.12, innerColor);
      gradient.addColorStop(.121, mainColor);
      gradient.addColorStop(.99, mainColor);
      gradient.addColorStop(1, outerColor);
    
      return gradient;
    }
    
    function resized() {
      gradient1 = createGradient1(ctx);
      gradient2 = createGradient2(ctx);
      config.data.datasets[0].backgroundColor = [gradient1, gradient2];
      donut.update();
    }
    var config = {
      type: 'doughnut',
      data: {
        labels: [
          "Pledged",
          "Missing"
        ],
        datasets: [{
          label: "Donations",
          data: [420, 80],
          cubicInterpolationMode: "monotone"
        }]
      },
      options: {
        onResize: resized,
        legend: {
          display: false
        }
      }
    };
    const donut = new Chart(ctx, config);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
    <canvas class="d-goal--canvas"></canvas>