LoginSignup
0
0

vue-chartjs(chart.js)で縦軸のタイトルを縦書きにする

Last updated at Posted at 2023-11-22

やりたいこと

スクリーンショット 2023-11-22 11.19.18.png

動作環境

package.json
    "vue": "^3.3.8",
    "vue-chartjs": "^4.0.4",
    "chart.js": "^3.7.1",

ソースコード

正規分布図を描画するコンポーネント。

NormalDistributionChart.vue
<template>
  <Line
    :chart-data="chartData"
    :chart-options="chartOptions"
    :plugins="plugins"
  />
</template>

<script setup lang="ts">
import { defineProps, withDefaults, computed } from "vue";
import { Line } from "vue-chartjs";
import {
  Chart,
  ChartData,
  ChartOptions,
  Plugin,
  registerables,
} from "chart.js";

Chart.register(...registerables);

export interface LineData {
  fx: number[];
  label: string;
  color: string;
}

export interface Props {
  x: number[];
  lines: LineData[];
}

const props = withDefaults(defineProps<Props>(), {
  x: () => [],
  lines: () => [],
});

const chartData = computed<ChartData<"line">>(() => ({
  labels: props.x,
  datasets: props.lines.map((line) => {
    return {
      label: line.label,
      data: line.fx,
      backgroundColor: line.color,
      borderColor: line.color,
      borderWidth: 3,
    };
  }),
}));

const chartOptions = computed<ChartOptions<"line">>(() => ({
  responsive: true,
  maintainAspectRatio: false,
  scales: {
    x: {
      title: {
        display: true,
        text: "横軸ラベル",
        font: {
          size: 14,
          weight: "bold",
        },
      },
    },
    y: {
      ticks: {
        callback: (value) => `${Math.floor(Number(value) * 100)}%`,
      },
    },
  },
  plugins: {
    legend: {
      align: "end",
      labels: {
        usePointStyle: true,
        font: {
          size: 12,
        },
      },
    },
    verticalTitle: {
      display: true,
      text: "縦軸ラベル",
      font: {
        size: 14,
        weight: "bold",
      },
    },
  },
}));

const plugins: Plugin<"line">[] = [
  {
    id: "verticalTitle",
    beforeLayout: (chart: any, args: any, opts: any) => {
      const { display, font } = opts;
      if (!display) return;

      const { ctx } = chart;
      ctx.font = `${font?.weight || "normal"} ${font?.size || 12}px ${
        font?.family || "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"
      }`;
      const { width } = ctx.measureText(opts.text[0]);
      chart.options.layout.padding.left = width * 1.5;
    },
    afterDraw: (chart: any, args: any, opts: any) => {
      const { display, font, text, color } = opts;
      const {
        ctx,
        chartArea: { top, bottom, left, right },
      } = chart;

      if (!display) return;

      ctx.fillStyle = color || Chart.defaults.color;
      ctx.font = `${font?.weight || "normal"} ${font?.size || 12}px ${
        font?.family || "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"
      }`;

      const fontSize = font?.size || 12;
      const y = (top + bottom) / 2 - (text.length / 2) * fontSize;
      const lineHeight = 1.1618; // 行の高さ (フォントサイズに対する倍率)
      for (var lines = text.split(""), i = 0, l = lines.length; l > i; i++) {
        var line = lines[i];
        var addY = fontSize;

        // 2行目以降の水平位置は行数とlineHeightを考慮する
        if (i) addY += fontSize * lineHeight * i;

        ctx.fillText(line, 0, y + addY);
      }
    },
  },
];
</script>

ポイント

通常機能として提供されていないので plugins で縦書きの縦軸ラベル機能を追加する。

"verticalTitle"という名前で追加するコード。

NormalDistributionChart.vue
<script setup lang="ts">
const plugins: Plugin<"line">[] = [
  {
    id: "verticalTitle",
    beforeLayout: (chart: any, args: any, opts: any) => {
      const { display, font } = opts;
      if (!display) return;

      const { ctx } = chart;
      ctx.font = `${font?.weight || "normal"} ${font?.size || 12}px ${
        font?.family || "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"
      }`;
      const { width } = ctx.measureText(opts.text[0]);
      chart.options.layout.padding.left = width * 1.5;
    },
    afterDraw: (chart: any, args: any, opts: any) => {
      const { display, font, text, color } = opts;
      const {
        ctx,
        chartArea: { top, bottom, left, right },
      } = chart;

      if (!display) return;

      ctx.fillStyle = color || Chart.defaults.color;
      ctx.font = `${font?.weight || "normal"} ${font?.size || 12}px ${
        font?.family || "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"
      }`;

      const fontSize = font?.size || 12;
      const y = (top + bottom) / 2 - (text.length / 2) * fontSize;
      const lineHeight = 1.1618; // 行の高さ (フォントサイズに対する倍率)
      for (var lines = text.split(""), i = 0, l = lines.length; l > i; i++) {
        var line = lines[i];
        var addY = fontSize;

        // 2行目以降の水平位置は行数とlineHeightを考慮する
        if (i) addY += fontSize * lineHeight * i;

        ctx.fillText(line, 0, y + addY);
      }
    },
  },
];
</script>

"verticalTitle"を使うコード。

NormalDistributionChart.vue
<script setup lang="ts">
const chartOptions = computed<ChartOptions<"line">>(() => ({
  plugins: {
    verticalTitle: {
      display: true,
      text: "縦軸ラベル",
      font: {
        size: 14,
        weight: "bold",
      },
    },
  },
}));
</script>
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0