CSSだけでグラフ上に文字を表示する円グラフを作る

こんにちは、ナカムラです。
今回はCSSだけで円グラフを作ってみたいと思います。
文字を配置するにあたり、CSSの三角関数cos()・sin()を使用します。
グラフを作るというよりまだ使ったことがなかったので、三角関数を使うというのが今回のテーマになります。

DEMO

See the Pen CSSだけでグラフ上に文字を表示する円グラフを作る by Nakamura (@takayo-nakamura) on CodePen.

背景色の作り方

.chartを円形にし、背景を円形のグラデーションで設定します。

:root {
 --r: 100px;
}

.chart {
    position: relative;
    width: calc(var(--r) * 4);
    height: calc(var(--r) * 4);
    border-radius: 50%;
    background-image: conic-gradient(
      #BFEA7C 0% 30%,
      #9BCF53 30% 60%,
      #416D19 60% 100%
    );
  }

円形のグラデーションはconic-gradient()関数を使うことで実装できます。
background-image: conic-gradient( 色 位置, 色 位置, 色 位置);

developer.mozilla.org

円の半径は次のグラフ上に文字を配置する際に使用するので、ルート変数として記述しました。

グラフの上に文字を配置する

(1)円の中央に配置する

まずposition: absolute;で円の中央に配置します。
topとleftを50%にし、translateで-50%する方法にしています。
※translateは座標も指定するのでラベルごとに設定します。

  .label {
    position: absolute;
    top: 50%;
    left: 50%;
    font-size: calc(var(--r) * 0.3);
    color:green;
  }

(2)各ラベルの位置を設定する

ラベルの範囲の中央を計算する

label-1は30%なのでその中間である15% = 0.15
label-2は30%からさらに30%なので、30〜60%の中間である45% = 0.45
label-3は60%からさらに100%なので、60〜100%の中間である80% = 0.8
になります。

「(開始+終了)÷ 2」という計算式になるので、それをCSSで記述していきます。
--startと--endに開始と終了の割合を入れます。

    --start: 0;
    --end: 0.3;
    --percentage: calc((var(--start) + var(--end)) / 2);  

これで割合が計算できたので、degに直します。

    --angle: calc(var(--percentage) * 360deg);  // 角度の割合からdegでの数値を計算します

x値とy値を計算する

さて、いよいよ三角関数の出番です。
円形上にレイアウトするので、x値とy値を導き出すために使います。

x = con(角度) × 半径
y = sin(角度) × 半径

になります。

また、90度が起点になるので、-90degにvar(--angle)を足してズレを調整しています。

    --deg: calc(-90deg + var(--angle));

最後にtranslateで座標を設定します。(最後の-50%はtopとleftの調整用です)

    translate: calc(cos(var(--deg)) * var(--r) - 50%) calc(sin(var(--deg)) * var(--r) - 50%);

三角関数はこちらの記事を参考にさせていただきました。 ics.media

最後に

三角関数を習っていた遥か昔の学生時代には自分には縁のないものだと思っていました。
学生時代の勉強って大事ですね。
割合を出す計算式はChatGPTに教えてもらいました・・作業時間が短縮できました。
ありがとうChatGPT。

JavaScriptだとプラグインなどが色々あると思いますが、CSSだけでも結構できるものですね。
ラベルのところはmixinにした方がよりコンパクトになります。
背景の数値も変数にするとか、色々改良の余地があります。
半径も文字を置くところを基準にしているのでグラフのサイズで作った方がわかりやすいですよね。
ちょっと今回は時間がなくなってきたので、そのうち直そうかな。