-
Notifications
You must be signed in to change notification settings - Fork 0
add basic implementation of Heatmap chart #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import React, { useState } from 'react'; | ||
| import Heatmap from './charts/Heatmap'; | ||
| import {range, randomInt} from '../util'; | ||
|
|
||
| const margin = {top: 2, left: 2}; | ||
| const heatmapSize = {width: 10, height: 10}; | ||
| const showcaseSize = {width: 50, height: 50}; | ||
|
|
||
| const generateHeatmapData = (max: number): number[][] => { | ||
| const xNum = 1 + randomInt(9) | ||
| const yNum = 1 + randomInt(9) | ||
| return range(0, yNum).map( | ||
| (y) => range(0, xNum).map( | ||
| (x)=> randomInt(max) | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| const ChartShowcase = () => { | ||
| const [heatmapData, setHeatmapData] = useState<number[][]>([[10]]); | ||
| const handleButton = () => { | ||
| setHeatmapData(generateHeatmapData(50)) | ||
| } | ||
|
|
||
| return ( | ||
| <article className="chart-showcase"> | ||
| <section className="chart-showwcase__title"> | ||
| <h1>SVG Chart Showcase</h1> | ||
| </section> | ||
| <section> | ||
| <h1 className="chart-showwcase__element-title">Heatmap SVG</h1> | ||
| <div> | ||
| <button onClick={handleButton}>Generate Heatmap data</button> | ||
| <div> | ||
| { heatmapData.map((e,i) => (<div key={i}>{ e.toString() }</div>)) } | ||
| </div> | ||
| <svg | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SVGには、 viewBoxを指定することで、横幅や縦幅を SVGの親となる要素の横幅や縦幅に合わせることが出来ます。 |
||
| viewBox={`0 0 ${showcaseSize.width} ${showcaseSize.height}`} | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| version="1.1" | ||
| > | ||
| <Heatmap | ||
| margin={margin} | ||
| size={heatmapSize} | ||
| data={heatmapData} | ||
| max={50} | ||
| /> | ||
| </svg> | ||
| </div> | ||
| </section> | ||
| </article> | ||
| ); | ||
| } | ||
|
|
||
| export default ChartShowcase; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import React from 'react'; | ||
|
|
||
| type Margin = { | ||
| top: number; | ||
| left: number; | ||
| } | ||
| type Size = { | ||
| width: number; | ||
| height: number; | ||
| } | ||
|
|
||
| type Props = { | ||
| margin: Margin; | ||
| size: Size; | ||
| data: number[][]; | ||
| max: number; | ||
| }; | ||
|
|
||
| const calcElementSize = (data: number[][], size: Size): Size => { | ||
| if (data.length > 0 && data[0].length > 0 ) { | ||
| const width = size.width/data[0].length; | ||
| const height = size.height/data.length; | ||
| return {width, height}; | ||
| } | ||
| throw new Error("data is invalid"); | ||
| } | ||
|
|
||
| const generateColor = (n: number, max: number): [number, number, number] => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. プロットする値が 0 に近いほど青色に近くなり、max に近いほど 赤色に近くなります。 |
||
| const blue = [0, 103, 192]; | ||
| const red = [255, 0, 0]; | ||
| const diff = [red[0]-blue[0], red[1]-blue[1], red[2]-blue[2]]; | ||
| const percentage = n/max | ||
| return [ | ||
| diff[0]*percentage+blue[0], | ||
| diff[1]*percentage+blue[1], | ||
| diff[2]*percentage+blue[2] | ||
| ] | ||
| } | ||
|
|
||
| const drawElementRect = (data: number[][], size: Size, max: number) => { | ||
| const elementSize = calcElementSize(data, size); | ||
| return data.map( | ||
| (nl,y) => nl.map((n,x)=> ( | ||
| <rect key={`${x}-${y}`} className="charts__heatmap__rect-element" | ||
| x={x*elementSize.width} | ||
| y={y*elementSize.height} | ||
| style={ | ||
| { | ||
| fill: `rgb(${generateColor(n, max).join(',')})` | ||
| } | ||
| } | ||
| width={elementSize.width} | ||
| height={elementSize.height} | ||
| >{n}</rect> | ||
| )) | ||
| ); | ||
| }; | ||
|
|
||
| const Heatmap = ({margin, size, data, max}: Props) => { | ||
| return ( | ||
| <g transform={`translate(${margin.left},${margin.top})`}> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://developer.mozilla.org/ja/docs/Web/SVG/Element/g
g要素でグループ化し、そのグループに対して、marginを設定しています。 |
||
| <rect className="charts__heatmap__rect-element" | ||
| x={0} | ||
| y={0} | ||
| width={size.width} | ||
| height={size.height} | ||
| /> | ||
| { drawElementRect(data, size, max) } | ||
| </g> | ||
| ); | ||
| } | ||
|
|
||
| export default Heatmap; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| const range = | ||
| (start: number, end: number) => Array.from({length: (end - start + 1)}, (v, k) => k + start); | ||
|
|
||
| const randomInt = (max: number): number => Math.floor(Math.random() * Math.floor(max)); | ||
|
|
||
| export { | ||
| range, | ||
| randomInt | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
行数: 1-10
列数: 1-10
の範囲でランダムに配列の配列を生成。