CSS 3D Line Chart のD3 ver.4版
Built with blockbuilder.org
forked from shimizu's block: D3 v4 - CSS 3D Line Chart
license: mit |
CSS 3D Line Chart のD3 ver.4版
Built with blockbuilder.org
forked from shimizu's block: D3 v4 - CSS 3D Line Chart
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"/> | |
<title>CSS 3D Line Chart</title> | |
<style> | |
html, body { | |
width: 100%; | |
height:100%; | |
} | |
#chart { | |
width: 980px; | |
height:500px; | |
transform: perspective( 600px ) rotateY( 45deg ); | |
} | |
</style> | |
</head> | |
<body> | |
<svg id="chart"></svg> | |
<script src="//unpkg.com/[email protected]/babel.min.js"></script> | |
<script src="//unpkg.com/[email protected]/build/d3.min.js"></script> | |
<script type="text/babel"> | |
const width = 980; | |
const height = 400; | |
const margin = {top:50, left:100, bottom:20, right:0 }; | |
const pw = width - (margin.left + margin.right); | |
const ph = height - (margin.top + margin.bottom); | |
//各種レイヤー要素を追加 | |
const svg = d3.select('#chart') | |
const stage = svg.append("g") | |
const gridLayer = stage.append("g").classed("gridLayer", true) | |
.attr("transform", `translate(${margin.left}, ${margin.top})`) | |
const axisLayer = stage.append("g").classed("axisLayer", true) | |
.attr("transform", `translate(${margin.left}, ${margin.top})`) | |
const lineLayer = stage.append("g").classed("lineLayer", true) | |
.attr("transform", `translate(${margin.left}, ${margin.top})`) | |
//ランダムにデータセットを作る | |
const indexSeries = d3.range(30).map(d => { | |
return {x:d, y:Math.floor(Math.random()*5200)+1} | |
}) | |
//スケール設定 | |
const xScale = d3.scaleLinear().domain([0, d3.max(indexSeries, d => d.x )]).range([0, pw]) | |
const yScale = d3.scaleLinear().domain([0, d3.max(indexSeries, d => d.y )]).range([ph, 0]) | |
//ライン生成関数を用意 | |
const lineFunction = d3.line() | |
.x(d => xScale(d.x) ) | |
.y(d => yScale(d.y) ) | |
//ステージを奥に向かって進むようにアニメーション | |
stage.attr("transform", "translate(100, 50)") | |
.transition() | |
.duration(6000) | |
.attr("transform", "translate(-400, 0)") | |
// svg filter要素を追加 | |
const filter = stage.append("defs").append('filter') | |
.attr("id", "drop-shadow") | |
.attr("width", "150%") | |
.attr("height", "150%") | |
filter.append('feGaussianBlur') | |
.attr("in", "SourceAlpha") | |
.attr("result", "blur") | |
.attr("stdDeviation", 2) | |
filter.append('feOffset') | |
.attr("result", "offsetBlur") | |
.attr("dx", 4) | |
.attr("dy", 4) | |
filter.append('feBlend') | |
.attr("in", "SourceGraphic") | |
.attr("in2", "offsetBlur") | |
.attr("mode", "normal") | |
// フィルター追加 end | |
//線グラフ追加 | |
const line = lineLayer.append('path') | |
.attr("filter", "url(#drop-shadow)"); | |
//線グラフのアニメーション | |
line.transition() | |
.duration(6000) | |
.attr('fill', 'none') | |
.attr('stroke', 'black') | |
.attrTween('d', getSmoothInterpolation); | |
//グリッド追加 | |
const xGrid = gridLayer.append("g") | |
.attr("transform", `translate(0, ${ph})`) | |
.call(d3.axisBottom() | |
.scale(xScale) | |
.ticks(40) | |
.tickSize(-ph, 0, 0) | |
.tickFormat("") | |
) | |
xGrid.select(".domain").remove(); | |
xGrid.selectAll(".tick line") | |
.attr("stroke", "#ccc") | |
const yGrid = gridLayer.append("g") | |
.call( d3.axisRight() | |
.scale(yScale) | |
.ticks(20) | |
.tickSize(width-margin.left, 0, 0) | |
.tickFormat("") | |
) | |
yGrid.select(".domain").remove(); | |
yGrid.selectAll(".tick line") | |
.attr("stroke", "#ccc") | |
//軸追加 | |
const xAxis = d3.axisBottom().scale(xScale) | |
const yAxis = d3.axisLeft().scale(yScale) | |
axisLayer.append("g") // Add the X Axis | |
.attr("transform", `translate(0, ${ph})`) | |
.call(xAxis); | |
axisLayer.append("g") // Add the Y Axis | |
.call(yAxis); | |
//パス用のカスタムトランジション | |
function getSmoothInterpolation() { | |
const interpolate = d3.scaleLinear() | |
.domain([0,1]) | |
.range([1, indexSeries.length + 1]); | |
return function(t) { | |
const flooredX = Math.floor(interpolate(t)); | |
const interpolatedLine = indexSeries.slice(0, flooredX); | |
if(flooredX > 0 && flooredX < indexSeries.length) { | |
const weight = interpolate(t) - flooredX; | |
const weightedLineAverage = indexSeries[flooredX].y * weight + indexSeries[flooredX-1].y * (1-weight); | |
interpolatedLine.push({"x":interpolate(t)-1, "y":weightedLineAverage}); | |
} | |
return lineFunction(interpolatedLine); | |
} | |
} | |
</script> | |
</body> | |
</html> |