Chart.js不要!SVGでドーナツチャートを実装してみた
バヅクリの伊藤です。
今回は Chart.js を使用せずに SVG でドーナツチャートを実装してみました。
背景
今回実装する、中央に数値が表示されるドーナツチャートを Chart.js を使用して実装していたところ、Chart.jsではデフォルトで真ん中に数値を表示する様なサポートはない為、
Chart.jsで作成したに対してラッパー要素を追加し、数値部分はposition
を使用して位置調整していました。
その際の、レスポンシブ対応・レイアウトの調整に苦戦しました...😇
この記事では、同じ様な実装に苦しんでいる方に SVG でも再現できることを伝えたいのと、実装するにあって調べことをアウトプットする目的で書きました ✏️
実装するもの
レッツ、トライ
今回は <svg>
と<path>
を使用して実装してみました!
const r = 100 / (2 * Math.PI);
export const Circle = ({ score }) => {
return (
<svg width="200" height="200" viewBox="0 0 40 40">
<path
d={`M20 ${(40 - (r + r)) / 2}
a ${r} ${r} 0 0 1 0 ${r + r}
a ${r} ${r} 0 0 1 0 -${r + r}`}
fill="none"
stroke="#F2F2F2"
strokeWidth="6"
strokeDasharray="100"
/>
<path
d={`M20 ${(40 - (r + r)) / 2}
a ${r} ${r} 0 0 1 0 ${r + r}
a ${r} ${r} 0 0 1 0 -${r + r}`}
fill="none"
stroke="#2fb6f0"
strokeWidth="6"
strokeDasharray={`${score} 100`}
/>
<text x="12" y="23" fontSize=".5em" textAnchor="center">
{score}%
</text>
</svg>
);
};
<svg>
で描画エリアを指定する
1. <svg width="200" height="200" viewBox="0 0 40 40">
width
, height
で viewport を指定し、viewBox
で表示する寸法や位置を指定します
viewBox について
- svg 要素に属性として追加でき、下記のようにスペースで区切って値を指定することができる
<svg viewBox="x y width height"></svg>
x
左上からの X 座標
y
左上からの Y 座標
width
ビューボックスの寸法
height
ビューボックスの寸法
下記の記事がわかりやすかったです 👇
ビューボックスの寸法、つまり最後の 2 つのパラメータを、ビューポートの寸法より大きくして「ズームアウト」し、小さくして「ズームイン」します。
https://webdesign.tutsplus.com/tutorials/svg-viewport-and-viewbox-for-beginners--cms-30844
<path>
で円を作る
2. グレーの円と、ブルーのパーセントに応じて変動する 2 つ円を実装しています。
まずはパーセントに応じて変動する円を作ります!
<path
d={`M20 ${(40 - (r + r)) / 2}
a ${r} ${r} 0 0 1 0 ${r + r}
a ${r} ${r} 0 0 1 0 -${r + r}`}
// 塗りつぶしの色
fill="none"
// アウトライン(輪郭)の色
stroke="#2fb6f0"
// 線幅
strokeWidth="6"
// 線の間隔
strokeDasharray={`${score} 100`}
/>
0〜100 の間で変動するグラフを作りたいので円周を 100 とした円の半径を求めます
const r = 100 / (2 * Math.PI);
-
d 属性に描画する直線や曲線を指定する
- M に続くのは始点の座標。
X 軸は、(width 40px / 2)
、Y 軸は、(height 40px - 直径(r + r)) / 2
- a に続くのは
半径、半径、回転角、0:短孤 or 1:長孤、終点
- M に続くのは始点の座標。
参考記事
<path>
で円を作る
3. もう一つベースとなるグレーの円を作成していきます。
<path
d={`M20 ${(40 - (r + r)) / 2}
a ${r} ${r} 0 0 1 0 ${r + r}
a ${r} ${r} 0 0 1 0 -${r + r}`}
fill="none"
stroke="#F2F2F2"
strokeWidth="6"
strokeDasharray="100"
/>
4. 中央に数値を表示する
<text>
を使用して、数値を表示しています
<text x="12" y="23" fontSize=".5em" textAnchor="center">
{score}%
</text>
グラデーションをつけてみる
<linearGradient>
を使用して線形グラデーションをつけました!
const r = 100 / (2 * Math.PI);
export const Circle = ({ score }) => {
return (
<svg width="200" height="200" viewBox="0 0 40 40">
+ <defs>
+ <linearGradient id="circle-gradient" x1="0" x2="0" y1="0" y2="1">
+ <stop offset="30%" stopColor="#2fb6f0" />
+ <stop offset="60%" stopColor="#396de7" />
+ <stop offset="100%" stopColor="#3a39e7" />
+ </linearGradient>
+ </defs>
<path
d={`M20 ${(40 - (r + r)) / 2}
a ${r} ${r} 0 0 1 0 ${r + r}
a ${r} ${r} 0 0 1 0 -${r + r}`}
fill="none"
stroke="#F2F2F2"
strokeWidth="6"
strokeDasharray="100"
/>
<path
d={`M20 ${(40 - (r + r)) / 2}
a ${r} ${r} 0 0 1 0 ${r + r}
a ${r} ${r} 0 0 1 0 -${r + r}`}
fill="none"
+ stroke="url('#circle-gradient')"
strokeWidth="6"
strokeDasharray={`${score} 100`}
/>
<text x="12" y="23" fontSize=".5em" textAnchor="center">
{score}%
</text>
</svg>
);
};
アニメーションをつけてみる
対象の<path>
にクラスを付与し、CSS でアニメーションをつけました 🪩
.circle {
animation: circleAnimation 1s;
}
@keyframes circleAnimation {
0% {
stroke-dasharray: 0 100;
}
}
<circle>
で作ってみる
<circle>
を使用して実装することも可能です 💃🏻
// 円周(C = 2πr)
const circumference = 2 * Math.PI * 75;
export const Circle = ({ score }) => {
return (
<svg width="500" height="500" viewBox="0 0 500 500">
<circle
r="75"
cx="90"
cy="90"
stroke="#F2F2F2"
fill="transparent"
strokeWidth="30"
strokeDasharray={circumference}
transform="rotate(-90 90 90)"
></circle>
<circle
r="75"
cx="90"
cy="90"
stroke="#2fb6f0"
fill="transparent"
strokeWidth="30"
strokeDasharray={`${circumference * (score / 100)} ${circumference}`}
transform="rotate(-90 90 90)"
></circle>
<text x="56" y="100" fontSize="2em" textAnchor="center">
{score}%
</text>
</svg>
);
};
参考記事
最後に
バヅクリでは Rails エンジニアを募集しています 😍
カジュアル面談等も受け付けておりますので、ご興味ある方は下記リンクから是非お問い合わせください 📮
Discussion