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