将图例添加到自制条形图
Adding legends to selfmade barchart
我知道您可能想知道 self-made 条形图?为什么不是现有的图书馆?好吧,我讨厌使用包含 20000 行代码的文件,而只需要 500 行代码。
哦,这很有趣 :) 主要 objective 是我将把这个脚本用于我将使用 Phonegap 制作的应用程序。所以尺寸越小越好。
所以思路是实现如下:
我已经能够绘制条形图,确保它们的宽度相等,并且它们的高度取决于 parent 容器的高度。正如您将在下面的代码中看到的,我还在选项中添加了 font-size。因为有些图表会扩展大约 300 像素的高度(例如,这将使用默认的 16 像素)。有些只有 50px,font-size 为 12 或更小。因此,我将 ChartContainer 栏减少了字体大小的 3 倍(+ 其余部分),以确保顶部(数量)和底部(图例 + 标题)
有足够的 space
现在我不完全确定如何添加和居中金额。我尝试搜索现有的图表库来检查它是如何呈现的,不幸的是他们都使用画布或 SVG 容器。有什么建议吗?
/* dataset
------------------
add legends
add result / amount
add bottom-border: 8px extra to both sides?
add chart name
*/
(function ($) {
var methods = {
init : function(options) {
return this.each(function() {
var $this = $(this),
dataset = options.dataset,
fontSize = options.fontSize,
widthOfContainer = $this.width(),
heightOfContainer = $this.height() - (3 * (fontSize + 4)), // make room for title (bottom), legend (bottom), amounts (top)
widthOfBar = parseInt(widthOfContainer / options.dataset.length) - 2,
bar;
$this.bind('draw', function(e) {
$this.empty();
var maxValueInDataset = Math.max.apply(Math, dataset.map(function(o){return o.a;})),
heightPerUnit = parseInt(heightOfContainer / maxValueInDataset);
for (var i = 0; i < dataset.length; i++) {
bar = $(document.createElement('div'));
bar.addClass('bar');
bar.css({
'height': parseInt(dataset[i].a * heightPerUnit) + 'px',
'width': parseInt(widthOfBar) + 'px',
'margin-left': parseInt(i * 2 + i * widthOfBar) + 'px',
'bottom': 2 * (fontSize + 4)
});
$this.append(bar);
}
});
$this.trigger('draw');
});
},
draw : function(n) {
$(this).trigger('draw');
}
};
$.fn.chart = function(methodOrOptions) {
if ( methods[methodOrOptions] ) {
return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
// Default to "init"
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.tooltip' );
}
};
$(document).ready(function(){
$('div.barchart').chart({
// Add font-size?
fontSize: 14,
name: 'mana cost',
dataset: [
{a: 2, label: '0'},
{a: 8, label: '1'},
{a: 9, label: '2'},
{a: 4, label: '3'},
{a: 7, label: '4'},
{a: 3, label: '5'},
{a: 1, label: '6'},
{a: 1, label: '7'},
{a: 2, label: '8'},
{a: 5, label: '9'}
]
});
});
}( jQuery ));
/* Barchart
========================================================================== */
.barchart {
color: black;
}
/* Bar
========================================================================== */
.bar {
position: absolute;
height: 0px;
width: 0px;
margin-left: 0px;
bottom: 0px;
background: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="padding: 20px;">
<div class="barchart" style="height: 100px; position: relative"></div>
</div>
图表不错!看起来很干净。我知道你的意思。我花了几个月的时间尝试操纵 bxSlider 的布局,然后我意识到自己编写的代码更少。这是回答您的查询的尝试。我通过使用百分比使其宽度响应(不用担心;很容易改回来),为图例、值和计数添加了额外的 css class,并将您的插件修改为包括您的姓名选项(称为图例)。这些位然后只是格式化和附加。希望这可以帮助。
/* dataset
------------------
add legends
add result / amount
add bottom-border: 8px extra to both sides?
add chart name
*/
(function ($) {
var methods = {
init : function(options) {
return this.each(function() {
var $this = $(this),
dataset = options.dataset,
fontSize = options.fontSize,
legend = options.name,
widthOfContainer = $this.width(),
heightOfContainer = $this.height() - (3 * (fontSize + 4)), // make room for title (bottom), legend (bottom), amounts (top)
widthOfBar = parseInt(widthOfContainer / options.dataset.length) - 2,
widthOfBarPer = (widthOfBar / widthOfContainer) *100,
bar;
$this.bind('draw', function(e) {
$this.empty();
var maxValueInDataset = Math.max.apply(Math, dataset.map(function(o){return o.a;})),
heightPerUnit = parseInt(heightOfContainer / maxValueInDataset);
for (var i = 0; i < dataset.length; i++) {
var dataVal = dataset[i].a;
bar = $(document.createElement('div'));
bar.addClass('bar');
bar.css({
'height': parseInt( dataVal * heightPerUnit) + 'px',
'width': widthOfBarPer + '%', // percentages to make more responsive?
'margin-left': (i + i * widthOfBarPer ) + '%', // no need to parseInt as you have already on widthOfBar. now your chart stretches with the width .
'bottom': 2 * (fontSize + 4)
});
bar.append('<p class="count">'+ i +'</p>'); // defines the bar count, this could be dataset[i].label but if you just use i then you don't need to type it out for each bar?
bar.append('<p class="value">'+ dataVal +'</p>'); // defines the bar value
$this.append(bar); // adds the bar count
}
var chartHeight = $this.height();
$('.bar .count').css({ bottom: fontSize - chartHeight * 0.5 });
$('.bar .value').css({ bottom: chartHeight * 0.5 - fontSize });
if(legend){
legend = '<p class="legend">'+legend+'</p>';
$this.append(legend);
// $this.css({ border: '1px solid #f90'}); // temp to see the current chart size
$this.find('.legend').css({ top: chartHeight - fontSize });
}
});
$this.trigger('draw');
});
},
draw : function(n) {
$(this).trigger('draw');
}
};
$.fn.chart = function(methodOrOptions) {
if ( methods[methodOrOptions] ) {
return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
// Default to "init"
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.tooltip' );
}
};
$(document).ready(function(){
$('div.barchart').chart({
// Add font-size?
fontSize: 14,
name: 'mana cost',
dataset: [
{a: 2, label: '0'},
{a: 8, label: '1'},
{a: 9, label: '2'},
{a: 4, label: '3'},
{a: 7, label: '4'},
{a: 3, label: '5'},
{a: 1, label: '6'},
{a: 1, label: '7'},
{a: 2, label: '8'},
{a: 5, label: '9'}
]
});
});
}( jQuery ));
/* Barchart
========================================================================== */
.barchart {
color: black;
}
/* Bar
========================================================================== */
.bar {
position: absolute;
height: 0px;
width: 0px;
margin-left: 0px;
bottom: 0px;
background: black;
}
.count, .value{
z-index: 7;
position: absolute;
text-align: center;
width: 100%;
}
.legend{
position: relative;
text-align: center;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="padding: 20px;">
<div class="barchart" style="height: 100px; position: relative"></div>
</div>
为避免不必要的代码量点赞 - 干净的条形图是不需要大量库的完美示例。
如果您使用 <table>
作为创建条形图的基本结构,您可以轻松访问所需的格式设置选项,从而减少代码:
- 一行用于值,一行用于标签
- 可以通过为每个值单元格
border-bottom
设置样式来制作条形图
- 文本可以在 table 个单元格中居中
- 为图例添加一个
<caption>
,默认居中,并且可以很容易地定位在table下方,并带有caption-side
属性
- 设置值单元格
vertical-align
属性 的样式允许值直接位于条形上方(如下面的演示所示)或排在顶部(如您的插图所示)-this控制在下面js演示代码中的第20行。
毕竟,条形图只是表格数据的可视化,因此使用 table 很有意义。
一个工作演示(大约 30 行 vanilla js 和几行 css,如果需要,您可以轻松地适应您的 jquery 方法):
function barchart(containerId, options) {
var i,
html,
valueRow = '',
labelRow = '',
data = options.dataset,
maxBarHeight = 60, /* in px, could be set from options */
barWidth = 20, /* in px, could be set from options */
maxValue = Math.max.apply(
Math,
data.map(function(o) {
return o.a;
})
);
for(i = 0; i < data.length; i++){
labelRow += '<td>' + data[i].label + '</td>';
valueRow += '<td style="border-bottom:' +
(data[i].a * maxBarHeight / maxValue) +
'px solid black;vertical-align:' +
'bottom' + /* set to 'top' to get value labels lined up */
';width: ' +
barWidth + 'px">' +
data[i].a + '</td>';
}
html = '<table class="barchart" ' +
'style="font-size:' + options.fontSize + 'px">' +
'<caption>' + options.name + '</caption>' +
'<tr>' + valueRow + '</tr>' +
'<tr>' + labelRow + '</tr>' +
'</table>';
document.getElementById(containerId)
.innerHTML = html;
}
/* create a barchart */
barchart('testdiv', {
fontSize: 14,
name: 'mana cost',
dataset: [
{a: 2, label: '0'},
{a: 8, label: '1'},
{a: 9, label: '2'},
{a: 4, label: '3'},
{a: 7, label: '4'},
{a: 3, label: '5'},
{a: 1, label: '6'},
{a: 1, label: '7'},
{a: 2, label: '8'},
{a: 5, label: '9'}
]
});
.barchart td{
text-align: center;
}
.barchart{
font-family: 'arial narrow', sans-serif;
caption-side: bottom;
}
<div id="testdiv"></div>
我知道您可能想知道 self-made 条形图?为什么不是现有的图书馆?好吧,我讨厌使用包含 20000 行代码的文件,而只需要 500 行代码。
哦,这很有趣 :) 主要 objective 是我将把这个脚本用于我将使用 Phonegap 制作的应用程序。所以尺寸越小越好。
所以思路是实现如下:
我已经能够绘制条形图,确保它们的宽度相等,并且它们的高度取决于 parent 容器的高度。正如您将在下面的代码中看到的,我还在选项中添加了 font-size。因为有些图表会扩展大约 300 像素的高度(例如,这将使用默认的 16 像素)。有些只有 50px,font-size 为 12 或更小。因此,我将 ChartContainer 栏减少了字体大小的 3 倍(+ 其余部分),以确保顶部(数量)和底部(图例 + 标题)
有足够的 space现在我不完全确定如何添加和居中金额。我尝试搜索现有的图表库来检查它是如何呈现的,不幸的是他们都使用画布或 SVG 容器。有什么建议吗?
/* dataset
------------------
add legends
add result / amount
add bottom-border: 8px extra to both sides?
add chart name
*/
(function ($) {
var methods = {
init : function(options) {
return this.each(function() {
var $this = $(this),
dataset = options.dataset,
fontSize = options.fontSize,
widthOfContainer = $this.width(),
heightOfContainer = $this.height() - (3 * (fontSize + 4)), // make room for title (bottom), legend (bottom), amounts (top)
widthOfBar = parseInt(widthOfContainer / options.dataset.length) - 2,
bar;
$this.bind('draw', function(e) {
$this.empty();
var maxValueInDataset = Math.max.apply(Math, dataset.map(function(o){return o.a;})),
heightPerUnit = parseInt(heightOfContainer / maxValueInDataset);
for (var i = 0; i < dataset.length; i++) {
bar = $(document.createElement('div'));
bar.addClass('bar');
bar.css({
'height': parseInt(dataset[i].a * heightPerUnit) + 'px',
'width': parseInt(widthOfBar) + 'px',
'margin-left': parseInt(i * 2 + i * widthOfBar) + 'px',
'bottom': 2 * (fontSize + 4)
});
$this.append(bar);
}
});
$this.trigger('draw');
});
},
draw : function(n) {
$(this).trigger('draw');
}
};
$.fn.chart = function(methodOrOptions) {
if ( methods[methodOrOptions] ) {
return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
// Default to "init"
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.tooltip' );
}
};
$(document).ready(function(){
$('div.barchart').chart({
// Add font-size?
fontSize: 14,
name: 'mana cost',
dataset: [
{a: 2, label: '0'},
{a: 8, label: '1'},
{a: 9, label: '2'},
{a: 4, label: '3'},
{a: 7, label: '4'},
{a: 3, label: '5'},
{a: 1, label: '6'},
{a: 1, label: '7'},
{a: 2, label: '8'},
{a: 5, label: '9'}
]
});
});
}( jQuery ));
/* Barchart
========================================================================== */
.barchart {
color: black;
}
/* Bar
========================================================================== */
.bar {
position: absolute;
height: 0px;
width: 0px;
margin-left: 0px;
bottom: 0px;
background: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="padding: 20px;">
<div class="barchart" style="height: 100px; position: relative"></div>
</div>
图表不错!看起来很干净。我知道你的意思。我花了几个月的时间尝试操纵 bxSlider 的布局,然后我意识到自己编写的代码更少。这是回答您的查询的尝试。我通过使用百分比使其宽度响应(不用担心;很容易改回来),为图例、值和计数添加了额外的 css class,并将您的插件修改为包括您的姓名选项(称为图例)。这些位然后只是格式化和附加。希望这可以帮助。
/* dataset
------------------
add legends
add result / amount
add bottom-border: 8px extra to both sides?
add chart name
*/
(function ($) {
var methods = {
init : function(options) {
return this.each(function() {
var $this = $(this),
dataset = options.dataset,
fontSize = options.fontSize,
legend = options.name,
widthOfContainer = $this.width(),
heightOfContainer = $this.height() - (3 * (fontSize + 4)), // make room for title (bottom), legend (bottom), amounts (top)
widthOfBar = parseInt(widthOfContainer / options.dataset.length) - 2,
widthOfBarPer = (widthOfBar / widthOfContainer) *100,
bar;
$this.bind('draw', function(e) {
$this.empty();
var maxValueInDataset = Math.max.apply(Math, dataset.map(function(o){return o.a;})),
heightPerUnit = parseInt(heightOfContainer / maxValueInDataset);
for (var i = 0; i < dataset.length; i++) {
var dataVal = dataset[i].a;
bar = $(document.createElement('div'));
bar.addClass('bar');
bar.css({
'height': parseInt( dataVal * heightPerUnit) + 'px',
'width': widthOfBarPer + '%', // percentages to make more responsive?
'margin-left': (i + i * widthOfBarPer ) + '%', // no need to parseInt as you have already on widthOfBar. now your chart stretches with the width .
'bottom': 2 * (fontSize + 4)
});
bar.append('<p class="count">'+ i +'</p>'); // defines the bar count, this could be dataset[i].label but if you just use i then you don't need to type it out for each bar?
bar.append('<p class="value">'+ dataVal +'</p>'); // defines the bar value
$this.append(bar); // adds the bar count
}
var chartHeight = $this.height();
$('.bar .count').css({ bottom: fontSize - chartHeight * 0.5 });
$('.bar .value').css({ bottom: chartHeight * 0.5 - fontSize });
if(legend){
legend = '<p class="legend">'+legend+'</p>';
$this.append(legend);
// $this.css({ border: '1px solid #f90'}); // temp to see the current chart size
$this.find('.legend').css({ top: chartHeight - fontSize });
}
});
$this.trigger('draw');
});
},
draw : function(n) {
$(this).trigger('draw');
}
};
$.fn.chart = function(methodOrOptions) {
if ( methods[methodOrOptions] ) {
return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
// Default to "init"
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.tooltip' );
}
};
$(document).ready(function(){
$('div.barchart').chart({
// Add font-size?
fontSize: 14,
name: 'mana cost',
dataset: [
{a: 2, label: '0'},
{a: 8, label: '1'},
{a: 9, label: '2'},
{a: 4, label: '3'},
{a: 7, label: '4'},
{a: 3, label: '5'},
{a: 1, label: '6'},
{a: 1, label: '7'},
{a: 2, label: '8'},
{a: 5, label: '9'}
]
});
});
}( jQuery ));
/* Barchart
========================================================================== */
.barchart {
color: black;
}
/* Bar
========================================================================== */
.bar {
position: absolute;
height: 0px;
width: 0px;
margin-left: 0px;
bottom: 0px;
background: black;
}
.count, .value{
z-index: 7;
position: absolute;
text-align: center;
width: 100%;
}
.legend{
position: relative;
text-align: center;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="padding: 20px;">
<div class="barchart" style="height: 100px; position: relative"></div>
</div>
为避免不必要的代码量点赞 - 干净的条形图是不需要大量库的完美示例。
如果您使用 <table>
作为创建条形图的基本结构,您可以轻松访问所需的格式设置选项,从而减少代码:
- 一行用于值,一行用于标签
- 可以通过为每个值单元格
border-bottom
设置样式来制作条形图 - 文本可以在 table 个单元格中居中
- 为图例添加一个
<caption>
,默认居中,并且可以很容易地定位在table下方,并带有caption-side
属性 - 设置值单元格
vertical-align
属性 的样式允许值直接位于条形上方(如下面的演示所示)或排在顶部(如您的插图所示)-this控制在下面js演示代码中的第20行。
毕竟,条形图只是表格数据的可视化,因此使用 table 很有意义。
一个工作演示(大约 30 行 vanilla js 和几行 css,如果需要,您可以轻松地适应您的 jquery 方法):
function barchart(containerId, options) {
var i,
html,
valueRow = '',
labelRow = '',
data = options.dataset,
maxBarHeight = 60, /* in px, could be set from options */
barWidth = 20, /* in px, could be set from options */
maxValue = Math.max.apply(
Math,
data.map(function(o) {
return o.a;
})
);
for(i = 0; i < data.length; i++){
labelRow += '<td>' + data[i].label + '</td>';
valueRow += '<td style="border-bottom:' +
(data[i].a * maxBarHeight / maxValue) +
'px solid black;vertical-align:' +
'bottom' + /* set to 'top' to get value labels lined up */
';width: ' +
barWidth + 'px">' +
data[i].a + '</td>';
}
html = '<table class="barchart" ' +
'style="font-size:' + options.fontSize + 'px">' +
'<caption>' + options.name + '</caption>' +
'<tr>' + valueRow + '</tr>' +
'<tr>' + labelRow + '</tr>' +
'</table>';
document.getElementById(containerId)
.innerHTML = html;
}
/* create a barchart */
barchart('testdiv', {
fontSize: 14,
name: 'mana cost',
dataset: [
{a: 2, label: '0'},
{a: 8, label: '1'},
{a: 9, label: '2'},
{a: 4, label: '3'},
{a: 7, label: '4'},
{a: 3, label: '5'},
{a: 1, label: '6'},
{a: 1, label: '7'},
{a: 2, label: '8'},
{a: 5, label: '9'}
]
});
.barchart td{
text-align: center;
}
.barchart{
font-family: 'arial narrow', sans-serif;
caption-side: bottom;
}
<div id="testdiv"></div>