proxyを使用しているため、対応しているブラウザ以外では動作しません。
○ Charome, FIrefox
× IE, Safari
Built with blockbuilder.org
license: mit |
proxyを使用しているため、対応しているブラウザ以外では動作しません。
○ Charome, FIrefox
× IE, Safari
Built with blockbuilder.org
/** | |
* | |
* @module createVBarChart | |
* @desc セレクター上に棒グラフを描画します。 | |
*/ | |
function createVBarChart(){ | |
"use strict" | |
var _chartWidth,_chartHeight | |
var _margin = {top:0, left:0, bottom:0, right:0}; | |
var _x = function(){ return d }, | |
_y = function(){ return d } | |
var _xScale = d3.scaleBand(), | |
_yScale = d3.scaleLinear() | |
var _xScaleDomain, _yScaleDomain, | |
_xScaleRange, _yScaleRange | |
var _xScalePaddingInner = 0.1, | |
_xScalePaddingOuter = 0.5 | |
var _xAxisLabel,_yAxisLabel | |
var _xAxisGridVisible = false, | |
_yAxisGridVisible = false | |
var _xAxisLabelOption = {x:0, y:0, "text-anchor":"middle", "dominant-baseline":"auto"}, | |
_yAxisLabelOption = {x:0, y:0, "text-anchor":"start", "dominant-baseline":"auto"} | |
var _yTickValues, _xTickValues | |
var _transitionObject = d3.transition().duration(0) | |
var _responsive = true | |
var _dispatch = d3.dispatch("mouseover","mousemove", "mouseout", "click"); | |
function exports(_selection) { | |
_selection.each(function(_data){ | |
var isHash = function(value) { | |
return value.toString() === '[object Object]'; | |
} | |
var isArray = Array.isArray || function(value) { | |
return value.toString() === '[object Array]'; | |
} | |
var parentNode = _selection.node() | |
var selectedSVG = _selection.selectAll("svg") | |
.data(["dummy"]) | |
var newSVG = selectedSVG.enter().append("svg") | |
var svg = selectedSVG.merge(newSVG) | |
svg.attr("class", "vbarChart") | |
var axisLayer = svg.append("g").classed("axisLayer", true) | |
var chartLayer = svg.append("g").classed("chartLayer", true) | |
var parentWidth, parentHeight | |
main(_data) | |
if(_responsive) setReSizeEvent() | |
function main(data) { | |
setSize() | |
if(isHash(data)){ | |
var tmp = [] | |
Object.keys(data).forEach(function(key){ | |
tmp.push(data[key]) | |
}) | |
setScale(Array.prototype.concat.apply([], tmp)) | |
} else if (isArray(data)){ | |
setScale(data) | |
} | |
if(_yAxisGridVisible) renderYAxisGrid() | |
if(_xAxisGridVisible) renderXAxisGrid() | |
renderYAxis() | |
renderXAxis() | |
renderYAxisLabel() | |
renderXAxisLabel() | |
renderBarChart(data) | |
} | |
function setReSizeEvent() { | |
var resizeTimer; | |
var interval = Math.floor(1000 / 60 * 10); | |
window.addEventListener('resize', function (event) { | |
if (resizeTimer !== false) { | |
clearTimeout(resizeTimer); | |
} | |
resizeTimer = setTimeout(function () { | |
main(_data) | |
}, interval); | |
}); | |
} | |
function setSize(args) { | |
parentWidth = parentNode.clientWidth | |
parentHeight = parentNode.clientHeight | |
_chartWidth = parentWidth - (_margin.left + _margin.right) | |
_chartHeight = parentHeight - (_margin.top + _margin.bottom) | |
svg | |
.attr("width", parentWidth) | |
.attr("height", parentHeight) | |
axisLayer | |
.attr("width", parentWidth) | |
.attr("height", parentHeight) | |
chartLayer | |
.attr("width", _chartWidth) | |
.attr("height", _chartHeight) | |
.attr("transform", "translate("+[_margin.left, _margin.top]+")") | |
} | |
function setScale(data){ | |
var xMap = data.map(function(d){ return _x(d) }).sort(function(a, b){ return a -b }) | |
var yMax = d3.max(data, function(d){ return +_y(d) }) | |
var yMin = d3.min(data, function(d){ return +_y(d) }) | |
if (yMin < 0){ | |
var yExtent = [yMin, yMax] | |
}else{ | |
var yExtent = [0, yMax] | |
} | |
_xScaleDomain = xMap | |
_yScaleDomain = yExtent | |
_xScaleRange = [0, _chartWidth] | |
_yScaleRange = [_chartHeight, 0] | |
_xScale.domain(_xScaleDomain).paddingInner(_xScalePaddingInner).paddingOuter(_xScalePaddingOuter) | |
_yScale.domain(_yScaleDomain) | |
_xScale.range(_xScaleRange) | |
_yScale.range(_yScaleRange) | |
} | |
function renderYAxis() { | |
var yAxisCall = d3.axisLeft(_yScale) | |
.tickSizeOuter(0) | |
if (_yTickValues) yAxisCall.tickValues(_yTickValues) | |
var yAxis = axisLayer.selectAll(".axis.y") | |
.data(["dummy"]) | |
var newYAxis = yAxis.enter().append("g") | |
newYAxis.merge(yAxis) | |
.transition(_transitionObject) | |
.attr("transform", "translate("+[_margin.left, _margin.top]+")") | |
.attr("class", "axis y") | |
.call(yAxisCall); | |
} | |
function renderYAxisGrid() { | |
var yAxisCall = d3.axisLeft(_yScale) | |
.tickSizeOuter(0) | |
.tickSizeInner(-_chartWidth) | |
.tickFormat(function(d){ return null }) | |
if (_yTickValues) yAxisCall.tickValues(_yTickValues) | |
var yAxis = axisLayer.selectAll(".grid.y") | |
.data(["dummy"]) | |
var newYAxis = yAxis.enter().append("g") | |
.attr("class", "grid y") | |
newYAxis.merge(yAxis) | |
.transition(_transitionObject) | |
.attr("transform", "translate("+[_margin.left, _margin.top]+")") | |
.call(yAxisCall); | |
} | |
function renderXAxis() { | |
var xAxisCall = d3.axisBottom(_xScale) | |
.tickSizeOuter(0) | |
if (_xTickValues) xAxisCall.tickValues(_xTickValues) | |
var xAxis = axisLayer.selectAll(".axis.x") | |
.data(["dummy"]) | |
var newXAxis = xAxis.enter().append("g") | |
newXAxis.merge(xAxis) | |
.transition(_transitionObject) | |
.attr("transform", "translate("+[_margin.left, _chartHeight+_margin.top]+")") | |
.attr("class", "axis x") | |
.call(xAxisCall) | |
.each(function(){ | |
d3.select(this).select(".domain") | |
.attr("transform", "translate("+[0,-_chartHeight + _yScale(0)]+")") | |
}) | |
} | |
function renderXAxisGrid() { | |
var xAxisCall = d3.axisBottom(_xScale) | |
.tickSizeOuter(0) | |
.tickSizeInner(-_chartHeight) | |
.tickFormat(function(d){ return null }) | |
if (_xTickValues) xAxisCall.tickValues(_xTickValues) | |
var xAxis = axisLayer.selectAll(".grid.x") | |
.data(["dummy"]) | |
var newXAxis = xAxis.enter().append("g") | |
newXAxis.merge(xAxis) | |
.transition(_transitionObject) | |
.attr("transform", "translate("+[_margin.left, _chartHeight+_margin.top]+")") | |
.attr("class", "grid x") | |
.call(xAxisCall); | |
} | |
function renderYAxisLabel() { | |
var yAxisLabel = axisLayer.selectAll(".label.y") | |
.data(["dummy"]) | |
var newYAxisLabel = yAxisLabel.enter().append("text").attr("class", "label y") | |
yAxisLabel.merge(newYAxisLabel) | |
.text(function(d){ return _yAxisLabel }) | |
.attr("x", _yAxisLabelOption.x) | |
.attr("y", _yAxisLabelOption.y) | |
.attr("text-anchor", _yAxisLabelOption["text-anchor"]) | |
.attr("dominant-baseline", _yAxisLabelOption["dominant-baseline"]) | |
.attr("transform", "translate("+[_margin.left, _margin.top]+")") | |
} | |
function renderXAxisLabel() { | |
var xAxisLabel = axisLayer.selectAll(".label.x") | |
.data(["dummy"]) | |
var newXAxisLabel = xAxisLabel.enter().append("text").attr("class", "label x") | |
xAxisLabel.merge(newXAxisLabel) | |
.text(function(d){ return _xAxisLabel }) | |
.attr("x", _xAxisLabelOption.x) | |
.attr("y", _xAxisLabelOption.y) | |
.attr("text-anchor", _xAxisLabelOption["text-anchor"]) | |
.attr("dominant-baseline", _xAxisLabelOption["dominant-baseline"]) | |
.attr("transform", "translate("+[_chartWidth+_margin.left, _chartHeight+_margin.top]+")") | |
} | |
function renderBarChart(data) { | |
var bar = chartLayer.selectAll(".bar").data(data) | |
bar.exit().remove() | |
var newBar = bar.enter().append("rect") | |
.attr("class", function(d){ return "bar " + _x(d) }) | |
.attr("height", 0) | |
.attr("transform", function(d){ return "translate("+[_xScale(_x(d)), _chartHeight]+")"}) | |
bar.merge(newBar) | |
.attr("width", _xScale.bandwidth()) | |
bar.merge(newBar).transition(_transitionObject) | |
.attr("height", function(d){ | |
var height = Math.abs( _yScale(_y(d)) - _yScale(0) ) | |
return height | |
}) | |
.attr("transform", function(d){ | |
var y = _yScale(Math.max(0, _y(d))) | |
return "translate("+[_xScale(_x(d)), y]+")" | |
}) | |
} | |
_selection.update = function(data){ | |
var _data = data | |
main(_data) | |
} | |
_selection._module = exports | |
}) | |
} | |
exports.margin = function(_arg) { | |
if (!arguments.length) return _margin; | |
Object.keys(_arg).forEach(function(key){ | |
_margin[key] = _arg[key] | |
}) | |
return this; | |
} | |
exports.x = function(_arg) { | |
if (!arguments.length) return _x; | |
_x = _arg; | |
return this; | |
} | |
exports.y = function(_arg) { | |
if (!arguments.length) return _y; | |
_y = _arg; | |
return this; | |
} | |
exports.responsive = function(_arg) { | |
if (!arguments.length) return _responsive; | |
_responsive = _arg; | |
return this; | |
} | |
return exports | |
} |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"/> | |
<title>D3 - use Proxy</title> | |
<style> | |
html, body { | |
width: 100%; | |
height: 100%; | |
padding: 0px; | |
margin: 0px; | |
} | |
#chart { | |
width: 900px; | |
height: 450px; | |
} | |
.vbarChart .bar { | |
fill:blue; | |
} | |
/* axis */ | |
.vbarChart .axis { | |
} | |
.vbarChart .axis .domain { | |
stroke: #333333; | |
} | |
.vbarChart .tick line { | |
stroke: #333333; | |
stroke-width: 1px; | |
} | |
.vbarChart .tick text { | |
fill: #333333; | |
font-size: 14px; | |
letter-spacing: .05em; | |
} | |
/* grid */ | |
.vbarChart .grid line { | |
stroke: #cccccc; | |
stroke-dasharray: 3,3; | |
} | |
/* label */ | |
.vbarChart .label { | |
font-size: 12px; | |
font-weight: normal; | |
letter-spacing: .05em; | |
} | |
#form { | |
margin: 10px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="form"> | |
tokyo:<input class="input" type="number" name="tokyo"> | |
gunma:<input class="input" type="number" name="gunma"> | |
saitama:<input class="input" type="number" name="saitama"> | |
</div> | |
<div id="chart"></div> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.1.1/d3.min.js"></script> | |
<script src="createVBarChart.js"></script> | |
<script> | |
!(function(){ | |
"use strict"; | |
const dataSet = [ | |
{name:"tokyo", value:"100"}, | |
{name:"gunma", value:"200"}, | |
{name:"saitama", value:"300"} | |
] | |
//バーチャートモジュールを生成 | |
const BarChart = createVBarChart() | |
.margin({top:40, left:100, bottom:40, right:10}) | |
.x(function(d){ return d["name"] }) | |
.y(function(d){ return d["value"] }) | |
//チャートを描画する | |
const selector = d3.selectAll("#chart").datum(dataSet) | |
.call(BarChart) | |
//データセットの内容が変更されたらチャートをアップデートする | |
const proxyHandler = { | |
get: function(target, name, value){ | |
return target[name] | |
}, | |
set: function(target, name, value){ | |
if (name == "value" && isNaN(+value)) throw new TypeError("型がちがうよ") | |
target[name] = value | |
selector.update(dataSet) //チャートアップデート | |
return target[name] | |
} | |
} | |
//データセットをネストし、proxyで包む | |
const nested = d3.nest() | |
.rollup(function(d){ return new Proxy(d[0], proxyHandler) }) | |
.key(function(d){ return d.name }) | |
.map(dataSet) | |
//テキストボックスの値が変更されたらデータセットをporxy経由で変更する | |
const updateDataset = function() { | |
nested["$"+this.name].value = this.value | |
} | |
const textBox = d3.selectAll(".input") | |
.on("change", updateDataset) | |
//テキストボックスに初期値を渡す。 | |
textBox.each(function(){ | |
this.value = nested["$"+this.name].value | |
}) | |
}()); | |
</script> | |
</body> | |
</html> |