如何将径向 CanvasGradient 与 Chart.js 圆环图一起使用?
Posted
技术标签:
【中文标题】如何将径向 CanvasGradient 与 Chart.js 圆环图一起使用?【英文标题】:How to use radial CanvasGradients with Chart.js's doughnut chart? 【发布时间】:2021-02-23 00:22:21 【问题描述】:我正在尝试创建两个径向渐变以与 Charts.js doughnut chart 一起使用。
渐变应该看起来有点像下图,但是是红色的。
使用 vanilla javascript 和 DOM 创建渐变 (createRadialGradient
) 非常简单,如下面的代码 sn-p 所示:
'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" ></canvas>
<canvas class="vanilla" ></canvas>
<canvas class="vanilla" ></canvas>
<canvas class="vanilla" ></canvas>
但是当我将相同的 2 个渐变应用到 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 中的一个错误......
【问题讨论】:
糟糕!我想我发现了我的错误。createGradient1
和 createGradient2
返回 ctx, gradient
而不是 gradient1
和 gradient2
!我的解构变量应该是const gradient: gradient1 = createGradient1(ctx)
和const gradient: gradient2 = createGradient2(ctx)
那解决了您的问题吗?
只是问题的一部分。我现在得到第一个渐变,但不是第二个。而且看起来不对,这可能是因为渐变区域与圆环图区域不匹配。当我有时间进一步调查时,我会发布一个解决方案。
【参考方案1】:
问题在于渐变填充样式实际应用于您的甜甜圈的方式。您的 - 和我最初的 - 假设是 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 像素的渐变,例如:
现在,如果我们进一步挖掘并假设您绘制的实际画布的大小是 797 x 419 像素,我们可以看到问题:
渐变完全超出了甜甜圈的形状!
要修复它,渐变需要位于甜甜圈的中心并具有适当的大小以完全填充它。有点像这样:
这说起来容易做起来难,因为最初我们不知道画布的确切大小,因为 chart.js 会自动拉伸它以填充浏览器窗口。
所以我们可以做些什么来解决这个问题:
使用 chart.js 创建甜甜圈,但不要填充它 等到 chart.js 触发 resize 事件以获取画布的实际大小 根据画布的大小计算渐变的尺寸并将其绘制在中心 最后用渐变填充甜甜圈的背景颜色这是一个示例(请以“整页”运行,因为我们在 *** 的迷你预览框架中没有获得正确的窗口大小):
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>
【讨论】:
只是 真棒 @obscure ! 很高兴我能帮助@dotnetCarpenter! =)以上是关于如何将径向 CanvasGradient 与 Chart.js 圆环图一起使用?的主要内容,如果未能解决你的问题,请参考以下文章