block by shimizu b8ac61e2f8dd42add0f87644c5c0c7c6

D3 - use Proxy

Full Screen

proxyを使用しているため、対応しているブラウザ以外では動作しません。

Proxy - JavaScript | MDN

○ Charome, FIrefox

× IE, Safari

Built with blockbuilder.org

index.html

<!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>

createVBarChart.js

/**
 *
 * @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
}