/** * @module nChart/createHBarChart * @desc 棒グラフ(横)を生成します。 * @example *var instans = nChart.createHBarChart() * * *var selector = d3.select(query) * .datum(data) * .call(instans) */ !(function() { 'use strict'; if (typeof module !== 'undefined' && module.exports) { module.exports = createHBarChart; } else { window.createHBarChart = createHBarChart; } function createHBarChart() { var _addSvgElement = true; var _plotWidth, _plotHeight; var _plotMargin = { top: 0, left: 0, bottom: 0, right: 0 }; var _x = function(d) { return d; }, _y = function(d) { return d; }; var _xScale = d3.scaleLinear(), _yScale = d3.scaleBand(); var _xScaleDomain, _yScaleDomain, _xScaleRange, _yScaleRange; var _forcedXScaleDomain, _forcedYScaleDomain; var _scalePaddingInner = 0, _scalePaddingOuter = 0; var _valueLabelVisible = false; var _responsive = true; var _parentNode; var _svg, _backgroundLayer, _chartLayer, _plotLayer, _axisLayer; function instance(_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]'; }; setLayers(_selection); main(_data); if (_responsive) setReSizeEvent(_selection); updateBindSelector(_selection); 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); } renderBarChart(data); if (_valueLabelVisible) renderValueLabels(data); } function setReSizeEvent(_selection) { var resizeTimer; var interval = Math.floor(1000 / 60 * 10); window.addEventListener('resize', function(event) { if (resizeTimer !== false) { clearTimeout(resizeTimer); } resizeTimer = setTimeout(function() { main(_data); _selection.dispatch('resize'); }, interval); }); } function setLayers(_selection) { //parent element _parentNode = _selection.node(); //parent -> svg element var target = _addSvgElement ? 'svg' : 'g'; var selectedSVG = _selection.selectAll(target).data([ null ]); var newSVG = selectedSVG.enter().append(target).attr('class', 'createHBarChart'); _svg = selectedSVG.merge(newSVG); //svg -> background layer var newBackgroundLayer = _svg.selectAll('.backgroundLayer').data([ null ]); _backgroundLayer = newBackgroundLayer .enter() .append('g') .classed('backgroundLayer', true) .merge(newBackgroundLayer); newBackgroundLayer.append('rect').classed('bg', true); //svg -> chart layer var newChartLayer = _svg.selectAll('.chartLayer').data([ null ]); _chartLayer = newChartLayer.enter().append('g').classed('chartLayer', true).merge(newChartLayer); //chart layer -> plot layer var newPlotLayer = _chartLayer.selectAll('.plotLayer').data([ null ]); _plotLayer = newChartLayer.enter().append('g').classed('plotLayer', true).merge(newPlotLayer); //chart layer -> axis layer var newAxisLayer = _chartLayer.selectAll('.axisLayer').data([ null ]); _axisLayer = newChartLayer.enter().append('g').classed('axisLayer', true).merge(newAxisLayer); } function setSize() { var parentWidth = _parentNode.clientWidth || _parentNode.parentNode.clientWidth; var parentHeight = _parentNode.clientHeight || _parentNode.parentNode.clientHeight; _plotWidth = parentWidth - (_plotMargin.left + _plotMargin.right); _plotHeight = parentHeight - (_plotMargin.top + _plotMargin.bottom); _svg.attr('width', parentWidth).attr('height', parentHeight); _backgroundLayer.select('.bg').attr('width', parentWidth).attr('height', parentHeight); _plotLayer .attr('width', _plotWidth) .attr('height', _plotHeight) .attr('transform', 'translate(' + [ _plotMargin.left, _plotMargin.top ] + ')'); } function setScale(data) { var yMap = data.map(function(d) { return _y(d).toString(); }); var xMax = d3.max(data, function(d) { return _x(d); }); var xMin = d3.min(data, function(d) { return _x(d); }); var xExtent = [ xMin, xMax ]; if (xMin > 0) xExtent = [ 0, xMax ]; _xScaleDomain = _forcedXScaleDomain ? _forcedXScaleDomain : xExtent; _yScaleDomain = _forcedYScaleDomain ? _forcedYScaleDomain : yMap; _xScaleRange = [ 0, _plotWidth ]; _yScaleRange = [ _plotHeight, 0 ]; _xScale.domain(_xScaleDomain); _yScale.domain(_yScaleDomain); _xScale.range(_xScaleRange); _yScale.rangeRound(_yScaleRange); scaleBindSelector(_selection); _yScale.paddingInner(_scalePaddingInner).paddingOuter(_scalePaddingOuter); } function renderBarChart(data) { var bar = _plotLayer.selectAll('.bar').data(data, function(d) { return _y(d); }); bar.exit().remove(); var newBar = bar .enter() .append('rect') .attr('class', function(d) { return 'bar ' + _y(d); }) .attr('transform', function(d) { var x = _xScale(Math.min(0, _x(d))); return 'translate(' + [ x, _yScale(_y(d)) ] + ')'; }); bar .merge(newBar) //選択済みセレクションをenterで追加されるセレクションにマージする .attr('height', _yScale.bandwidth()) .transition() .attr('width', function(d) { var width = Math.abs(_xScale(_x(d)) - _xScale(0)); return width; }) .attr('transform', function(d) { var x = _xScale(Math.min(0, _x(d))); return 'translate(' + [ x, _yScale(_y(d)) ] + ')'; }); } function renderValueLabels(data) { var label = _plotLayer.selectAll('.label').data(data, function(d) { return _y(d); }); label.exit().remove(); var newLabel = label.enter().append('text').attr('class', function(d) { return 'label ' + _y(d); }); var formart = d3.format('.2f'); label .merge(newLabel) //選択済みセレクションをenterで追加されるセレクションにマージする .attr('text-anchor', function(d) { if (d.Bop > 0) return 'start'; if (d.Bop < 0) return 'end'; }) .attr('dominant-baseline', 'middle') .attr('x', function(d) { if (_x(d) <= 0) return '-0.5em'; return '0.5em'; }) .attr('y', _yScale.bandwidth() / 2) .html(function(d) { var value = _x(d) * 1000; var abs = Math.abs(value); var str = null; if (abs > 900000000000) { str = formart(value / 1000000000000); str = '' + str + ''; str += ''; } else { str = formart(value / 100000000000); str = '' + str + ''; str += ' 千億'; } return str; }) .transition() .attr('transform', function(d) { var x = _xScale(_x(d)); return 'translate(' + [ x, _yScale(_y(d)) ] + ')'; }); } function scaleBindSelector(_selection) { //セレクターにaxisがバインドできるか判別するためのフラグ _selection._enableBindAxis = true; //セレクターにスケールをバインドする _selection._xScale = _xScale; _selection._yScale = _yScale; //セレクターにマージンをバインドする _selection._plotMargin = _plotMargin; //セレクターにアクセサをバインドする _selection._x = _x; _selection._y = _y; _selection._addSvgElement = _addSvgElement; } function updateBindSelector(_selection) { if (!_selection.updateFanctions) _selection.updateFanctions = []; _selection.updateFanctions.push(main); _selection.update = function(data) { _selection.updateFanctions.forEach(function(f) { f(data); }); }; } }); } //getter instance.getChartWidth = function(_arg) { if (!arguments.length) return _plotWidth; throw new Error('このメソッドは取得専用です'); }; instance.getChartHeight = function(_arg) { if (!arguments.length) return _plotHeight; throw new Error('このメソッドは取得専用です'); }; /** * チャート描画エリアのマージンを設定する * @alias module:nChart/createHBarChart.plotMargin * @param {Object} options 四方のマージンサイズを渡す * @param {Number} options.top 上辺のマージン * @param {Number} options.left 左辺のマージン * @param {Number} options.bottom 下辺のマージン * @param {Number} options.right 右辺のマージン */ instance.plotMargin = function(_arg) { if (!arguments.length) return _plotMargin; Object.keys(_arg).forEach(function(key) { _plotMargin[key] = _arg[key]; }); return this; }; /** * データセットからx軸に適応するデータ項目を設定する * @alias module:nChart/createHBarChart.x * @param {Function} accessor アクセサ関数を渡す */ instance.x = function(_arg) { if (!arguments.length) return _x; _x = _arg; return this; }; /** * データセットからy軸に適応するデータ項目を設定する * @alias module:nChart/createHBarChart.y * @param {Function} accessor アクセサ関数を渡す */ instance.y = function(_arg) { if (!arguments.length) return _y; _y = _arg; return this; }; /** * x軸のスケールを設定する * @alias module:nChart/createHBarChart.xScale * @param {Object} scale d3スケールオブジェクトを渡す */ instance.xScale = function(_arg) { if (!arguments.length) return _xScale; _xScale = _arg; return this; }; /** * y軸のスケールを設定する * @alias module:nChart/createHBarChart.yScale * @param {Object} scale d3スケールオブジェクトを渡 */ instance.yScale = function(_arg) { if (!arguments.length) return _yScale; _yScale = _arg; return this; }; /** * x軸のドメイン範囲を設定する * @alias module:nChart/createHBarChart.xScaleDomain * @param {Number[]} domain 描画する最小値と最大値を渡す */ instance.xScaleDomain = function(_arg) { if (!arguments.length) return _xScaleDomain; _forcedXScaleDomain = _arg; return this; }; /** * y軸のドメイン範囲を設定する * @alias module:nChart/createHBarChart.yScaleDomain * @param {Number[]} domain 描画する最小値と最大値を渡す */ instance.yScaleDomain = function(_arg) { if (!arguments.length) return _yScaleDomain; _forcedYScaleDomain = _arg; return this; }; /** * x軸のバーチャート間のパディング(内側)を指定する * @alias module:nChart/createHBarChart.scalePaddingInner * @param {Number[]} padding 0〜1の間で、バーチャートのサイズに対する割合を渡す */ instance.scalePaddingInner = function(_arg) { if (!arguments.length) return _scalePaddingInner; _scalePaddingInner = _arg; return this; }; /** * x軸のバーチャートのパディング(外側)を指定する * @alias module:nChart/createHBarChart.scalePaddingOuter * @param {Number[]} padding 0〜1の間で、バーチャートのサイズに対する割合を渡す */ instance.scalePaddingOuter = function(_arg) { if (!arguments.length) return _scalePaddingOuter; _scalePaddingOuter = _arg; return this; }; /** * バーの先端に値ラベルを表示する * @alias module:nChart/createAxis.xAxisDomainLineVisible * @param {Boolean} flag 表示(true),非表示(false)を渡す */ instance.valueLabelVisible = function(_arg) { if (!arguments.length) return _valueLabelVisible; _valueLabelVisible = _arg; return this; }; /** * レスポンシブ機能の有効・無効を設定する * @alias module:nChart/createHBarChart.responsive * @param {Boolean} flag 有効, 無効を渡す */ instance.responsive = function(_arg) { if (!arguments.length) return _responsive; _responsive = _arg; return this; }; /** * svgエレメントを追加する・しないを設定する * @alias module:nChart/createHBarChart.addSvgElement * @param {Boolean} flag 有効, 無効を渡す */ instance.addSvgElement = function(_arg) { if (!arguments.length) return _addSvgElement; _addSvgElement = _arg; return this; }; return instance; } })();