Scrollable chart with fixed Y axis by Chart.js

Sorry, comments are described in Japanese. This page owes much to the following page: https://jsfiddle.net/EmmaLouise/eb1aqpx8/3/

by oktopus1959

HTML

<!-- スクロール可能グラフ領域のラッパーブロック -->
<div class="scrollableChartWrapper">
  <!-- スクロールされる canvas を持つ div:
       - style.width を省略すると div の幅がページ幅に合わせられる;
       - style.width を指定すると div の幅(≒スクロールバーの長さ)が固定される -->
  <div>
    <!-- グラフ描画用 canvas:
         - style.height は必須;
         - style.width は JS によって設定する;
         - width,height は Chart により設定される -->
    <canvas id="chart" style="height: 250px"></canvas>
  </div>
  <!-- Y軸イメージコピー用 canvas: {style.,}{height,width} は JS によって設定する -->
  <canvas id="yAxis" width="0"></canvas>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script>

CSS

/* 配置の基準となるラッパーブロック; 背景色も設定する */
.scrollableChartWrapper {
  position: relative;
  background-color: ghostwhite;
}

/* スクロール可能グラフを囲む div */
.scrollableChartWrapper > div {
  position: relative;
  overflow-x: scroll;
}

/* Y軸イメージコピー用 canvas */
.scrollableChartWrapper > canvas {
  position: absolute;   /* これにより、上の div と重なる位置に canvas が配置される */
  left: 0;
  top: 0;
  background-color: ghostwhite;  /* ここをコメントアウトすると、背景色が透明になるので、Y軸を透かしてグラフ部分が見えるようになる */
}

JavaScript

// Y軸コピー用 canvas
var cvsYAxis = document.getElementById('yAxis');
var ctxYAxis = cvsYAxis.getContext('2d');

// テスト用データ
var data = [];
var labels = [];
var colors = [];

for (i = 0; i < 15; i += 1) {
  [12, 19, 3, 5, 2, 3].forEach(x => { data.push(x); });
  ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'].forEach(x => { labels.push(x); });
  [ 'rgb(255, 99, 132)',
    'rgb(54, 162, 235)',
    'rgb(255, 206, 86)',
    'rgb(75, 192, 192)',
    'rgb(153, 102, 255)',
    'rgb(255, 159, 64)'].forEach(x => { colors.push(x); });
}

// X軸の1データ当たりの幅
var xAxisStepSize = 30;
// グラフ全体の幅を計算
var chartWidth = data.length * xAxisStepSize;

// グラフ描画用 canvas
var cvsChart = document.getElementById('chart');
var ctxChart = cvsChart.getContext('2d');

// Chart用canvasのstyle.width(すなわち実際に描画されるべきサイズ)に上記の幅を設定
cvsChart.style.width = chartWidth + "px";

// canvas.width(height)はグラフ描画時に Chart により変更される(らしい)ので、下記はやる必要なし
//cvsChart.width = chartWidth;
//cvsChart.height = chartHeight;

// 二重実行防止用フラグ
var copyYAxisCalled = false;

// Y軸イメージのコピー関数
function copyYAxisImage(chart) {
  //console.log("copyYAxisCalled="+copyYAxisCalled);

  if (copyYAxisCalled) return;

  copyYAxisCalled = true;

  // グラフ描画後は、canvas.width(height):canvas.style.width(height) 比は、下記 scale の値になっている
  var scale = window.devicePixelRatio;

  // Y軸のスケール情報
  var yAxScale = chart.scales['y-axis-0'];

  // Y軸部分としてグラフからコピーすべき幅 (TODO: 良く分かっていない)
  var yAxisStyleWidth0 = yAxScale.width - 10;

  // canvas におけるコピー幅(yAxisStyleWidth0を直接使うと微妙にずれるので、整数値に切り上げる)
  var copyWidth = Math.ceil(yAxisStyleWidth0 * scale);
  // Y軸canvas の幅(右側に少し空白部を残す)
  var yAxisCvsWidth = copyWidth + 4;
  // 実際の描画幅(styleに設定する)
  var yAxisStyleWidth = yAxisCvsWidth / scale;

  // Y軸部分としてグラフからコピーすべき高さ (TODO: 良く分かっていない) ⇒これを実際の描画高とする(styleに設定)
  var yAxisStyleHeight = yAxScale.height + yAxScale.top + 10;
  // canvas におけるコピー高
  var copyHeight = yAxisStyleHeight * scale;
  // Y軸canvas の高さ
  var yAxisCvsHeight = copyHeight;

  console.log("After chart canvas...