CSS 3D Line Chart のD3 ver.4版
Built with blockbuilder.org
| license: mit |
CSS 3D Line Chart のD3 ver.4版
Built with blockbuilder.org
| <!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/babel-standalone@6.26.0/babel.min.js"></script> | |
| <script src="//unpkg.com/d3@4.12.2/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> |