简体   繁体   English

如何在 Chart.js 的圆环图上使用径向 CanvasGradients?

[英]How to use radial CanvasGradients with Chart.js's doughnut chart?

I'm trying to create two radial gradients to use with a Charts.js doughnut chart .我正在尝试创建两个径向渐变以与Charts.js 圆环图一起使用。

The gradients should look kinda like the image below but in red.渐变应该看起来有点像下图,但是是红色的。
在此处输入图片说明

Creating the gradients ( createRadialGradient ) was pretty simple using vanilla javascript and the DOM, as displayed in the code snippet below:使用 vanilla javascript 和 DOM 创建渐变( createRadialGradient )非常简单,如下面的代码片段所示:

 '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>

But when I apply the same 2 gradients to Charts.js , I get a grey doughnut.但是当我对Charts.js应用相同的 2 个渐变时,我得到一个灰色的甜甜圈。 Using a single gradient does not change the result.使用单个渐变不会改变结果。 However, using two Hsla colors work as expected ( red & "white" ).但是,使用两种 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>

What am I doing wrong?我究竟做错了什么?

I read Jelena Jovanovic's gradient tutorial , which is a linear gradient ( createLinearGradient ) applied to a line chart and I do not see what I am doing differently.我阅读了Jelena Jovanovic 的渐变教程,它是应用于折线图的线性渐变 ( createLinearGradient ),但我看不出我在做什么不同。 Perhaps my gradient area is wrong but that should not result in a grey chart, as far as I can tell.也许我的渐变区域是错误的,但据我所知,这不应该导致灰色图表。

The Charts.js documentation has a section about colors where they describe how to use CanvasGradient but only for createLinearGradient , which makes me think that it might be a bug in Charts.js...该Charts.js文档有一个关于色彩节,他们描述了如何使用CanvasGradient但只有createLinearGradient ,这让我觉得,这可能是在Charts.js的错误...

The problem is the way the gradient fill styles get applied to your doughnut actually.问题是渐变填充样式实际上应用于甜甜圈的方式。 Your - and my initial - assumption is that chart.js will take care of positioning and scaling the gradient to the appropriate size to fill the doughnut.您和我最初的假设是 chart.js 将负责定位和缩放渐变到适当的大小以填充甜甜圈。 Well, that's not the case.好吧,事实并非如此。 Instead it uses the size & position of the gradient on the canvas.相反,它使用画布上渐变的大小和位置。

To get a better understanding, let's take a look at your code for one of the gradients:为了更好地理解,让我们看一下其中一个梯度的代码:

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);

This will produce a gradient with a diameter of 140 pixels at x=100 and y=100 like:这将在 x=100 和 y=100 处产生一个直径为 140 像素的渐变,例如:

Now if we dig a bit further and assume the size of the actual canvas you draw onto is 797 x 419 pixels, we can see the problem:现在,如果我们进一步挖掘并假设您绘制的实际画布的大小是 797 x 419 像素,我们可以看到问题:

The gradient is completely out of the doughnuts shape!渐变完全脱离了甜甜圈的形状!

To fix it, the gradient would need to be in the center of the doughnut and have the appropriate size to fill it entirely.要修复它,渐变需要位于甜甜圈的中心并具有适当的大小来完全填充它。 A little something like this:有点像这样:

Well this is easier said then done because initially we don't know the exact size of the canvas as chart.js automatically stretches it to fill the browser window.嗯,说起来容易做起来难,因为最初我们不知道画布的确切大小,因为 chart.js 会自动拉伸它以填充浏览器窗口。

So what we can do to workaround goes a little something like this:所以我们可以做的解决方法是这样的:

  • create the doughnut using chart.js but don't give it a fill yet使用 chart.js 创建甜甜圈,但不要填充它
  • wait until chart.js fires a resize event to get the actual size of the canvas等到 chart.js 触发resize 事件以获取画布的实际大小
  • calculate the dimensions of the gradients according the size of the canvas and draw it at the center根据画布的大小计算渐变的尺寸并将其绘制在中心
  • finally populate the backgroundColor of the doughnut with the gradients最后用渐变填充甜甜圈的背景颜色

Here's an example (PLease run as 'Full page' as we don't get the correct window size in stackoverflow's mini preview frame):这是一个示例(请以“完整页面”运行,因为我们在 stackoverflow 的迷你预览框架中没有获得正确的窗口大小):

 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM