追記
そういえば tootlitp のデザインは下記URL から拝借しています。書き忘れていたので追記します。ちなみにこの CSS にドロップシャドウを追加したらシャレオツな感じになった気がします。
【5パターン】画像を使わず CSS3 のみで作れる吹き出しを作ってみた – Pure CSS3 Balloons | Stronghold Archive
あらすじ
Chart.js の tooltip をカスタマイズする必要があったので、その勉強した結果をメモしておきます。
どんなの?
こんなの
準備
react-storybook 上で演習します
create-react-app practice-react-chartjs && cd $_ getstorybook npm run storybook
open http://localhost:6006
して以下の画面が表示されれば準備完了です
演習開始
chart.js をinstlall
npm install --save chart.js
シンプルな Bar Chart を作成します。
create src/components/bar-chart.js
import React, { Component } from 'react' import ReactDOM from 'react-dom' import { Chart } from 'chart.js' class BarChart extends Component { componentDidMount() { const ctx = this.node.getContext('2d') const chart = new Chart(ctx, { type: "bar", data: { labels: ["好き", "嫌い", "どちらでもない"], datasets: [{ label: "りんご", data: [ 5, 1, 4] }, { label: "なし", data: [ 8, 1, 1] }, { label: "みかん", data: [ 2, 1, 7] }], }, options: { scales: { yAxes: [{ display: true, ticks: { beginAtZero: true, max: 10, }, }], } } }) } render() { return ( <div> <canvas ref={(node) => this.node = node}></canvas> </div> ) } } export default BarChart
edit stories/index.js
import React from 'react'; import { storiesOf, action, linkTo } from '@kadira/storybook'; import BarChart from '../src/components/bar-chart' storiesOf('Welcome', module) .add('BarChart', () => ( <BarChart /> ));
tooltip component を作成する
css in js にします。content に空文字を設定する時は "' '"
のようにしましょう。
npm install glamor --save
create src/components/tooltip.js
import React, { Component } from 'react' import { css } from 'glamor' let rule = css({ position: "relative", display: "inline-block", padding: "0 15px", width: "auto", minWidth: "115px", height: "40px", lineHeight: "34px", color: "#19283C", textAlign: "center", background: "#F6F6F6", border: "3px solid #19283C", boxShadow: "2px 1px 8px 0 rgba(0,0,0,0.3)", zIndex: 0, ":before": { content: "' '", position: "absolute", bottom: "-8px", left: "50%", marginLeft: "-9px", width: "0px", height: "0px", borderStyle: "solid", borderWidth: "9px 9px 0 9px", borderColor: "#F6F6F6 transparent transparent transparent", zIndex: 0 }, ":after": { content: "' '", position: "absolute", bottom: "-12px", left: "50%", marginLeft: "-10px", width: "0px", height: "0px", borderStyle: "solid", borderWidth: "10px 10px 0 10px", borderColor: "#19283C transparent transparent transparent", zIndex: -1 } }) class Tooltip extends Component { componentDidMount() { } render() { return ( <div className={`${rule}`}>{this.props.children}</div> ) } } export default Tooltip
storybook に追加する edit: story/index.js
import React from 'react'; import { storiesOf, action, linkTo } from '@kadira/storybook'; import BarChart from '../src/components/bar-chart' import Tooltip from '../src/components/tooltip' storiesOf('Welcome', module) .add('BarChart', () => ( <BarChart /> )) .add('Tooltip', () => ( <Tooltip>test</Tooltip> ));
Chart.js の tooltip option で挙動を確認
試しに Chart.js に以下のオプションを追加してマウスをグラフに hover させると tooltip のイベントが発生します。ここで取得した情報を先ほどの Tooltip に渡します。
+++ b/src/components/bar-chart.js @@ -32,7 +32,17 @@ class BarChart extends Component { max: 10, }, }], - } + }, + tooltips: { + enabled: false, + mode: 'nearest', + position: 'nearest', + intersect: false, + custom: function(tooltip) { + console.log(this._chart, tooltip) + } + }, } })
tooltip の表示、非表示については tooltip.opacity
で判定できます。
tooltip の位置については tooltip.caretX
と this.tooltip.caretY
で相対的な位置がわかります。 これらと this._chart.canvas.getBoundingClientRect()
を組み合わせることで計算することができます。
tooltip の表示位置を position: absolute
にして以下の style を操作することで実現させます。
- el.styleopacity
- el.style.left
- el.style.top
tooltip に表示する要素は tooltip.body[0].lines
を参照すれば取得できます。
tooltip component をカスタマイズ
というわけで tooltip component に対して以下の props を渡せるようにします。
el.style.opacity el.style.left el.style.top text
動作確認をする際、 react-storybook
には Knobs
という便利な Addon があるのでこれを使います。が、使い方は割愛します。
edit src/components/tooltip.js
render() { + const { opacity, top, left, children } = this.props; return ( - <div className={`${rule}`}>{this.props.children}</div> + <div style={{position: "absolute", opacity, top, left}}> + <div className={`${tooltip}`}>{children}</div> + </div> )
位置情報を計算する
公式サイトに計算方法が載ってあるのでこの計算式を流用します。
http://www.chartjs.org/docs/latest/configuration/tooltip.html
例えば以下のようにします。chart.canvas.getBoundingClientRect() を実行すると canvas の位置がとれるのでそれを使用します。ただし、scroll するとずれるので注意。
正直ここの計算式はどれが正しいのかわかってないのですがたぶんこの計算式でまあまあ良い感じになると思います。
function customTooltip(canvas, tooltipModel) { if (tooltipModel.opacity === 0) { return {opacity: 0, left: 0, top: 0, text: ""} } // const position = chart.canvas.getBoundingClientRect() // console.log(position.left, position.right) return { opacity: 1, // left: caret - tooltip の幅 / 2 + potion.left // top: caret - tooltip の高さ + potion.top left: tooltipModel.caretX - 151 / 2 + 8, top: tooltipModel.caretY - tooltipModel.height - 8, text: tooltipModel.body[0].lines.join("") } }
あとは BarChart Compnent に onTooltipFire
というイベントハンドラを仕込んで受け取った情報を Tooltip に渡せば良いです。
疲れてきたので後は github に上げておきます。興味がある方はご覧頂き、宜しければ⭐️でも付けてやってください
ソースコード
https://github.com/okamuuu/react-chart.js-example