簡體   English   中英

如何在 Chart.js 的圓環圖上使用徑向 CanvasGradients?

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

我正在嘗試創建兩個徑向漸變以與Charts.js 圓環圖一起使用。

漸變應該看起來有點像下圖,但是是紅色的。
在此處輸入圖片說明

使用 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>

但是當我對Charts.js應用相同的 2 個漸變時,我得到一個灰色的甜甜圈。 使用單個漸變不會改變結果。 但是,使用兩種 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 的漸變教程,它是應用於折線圖的線性漸變 ( createLinearGradient ),但我看不出我在做什么不同。 也許我的漸變區域是錯誤的,但據我所知,這不應該導致灰色圖表。

該Charts.js文檔有一個關於色彩節,他們描述了如何使用CanvasGradient但只有createLinearGradient ,這讓我覺得,這可能是在Charts.js的錯誤...

問題是漸變填充樣式實際上應用於甜甜圈的方式。 您和我最初的假設是 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 事件以獲取畫布的實際大小
  • 根據畫布的大小計算漸變的尺寸並將其繪制在中心
  • 最后用漸變填充甜甜圈的背景顏色

這是一個示例(請以“完整頁面”運行,因為我們在 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