HTML5 + JavaScript绘柱状图

文章介绍了如何利用HTML5和JavaScript来创建柱状图,展示不同品牌类型的年度分布情况。通过定义二维数组存储品牌类型和数量数据,然后使用自定义的BarChart类绘制图形,包括标题、图例、横纵坐标和数据柱状条。这种方法使得比较历年品牌数据变得直观易懂。
摘要由CSDN通过智能技术生成

之前用HTML5 + JavaScript绘柱状图,可以直观显示各类型产品或品牌的所占比例大小。详见:

HTML5 + JavaScript绘柱状图+1

现在需要针对每年获得各类品牌数据进行对比,绘制柱状图会更直观。

首先我们定义二维数组aBrandType,存放品牌类型及其对应的颜色:

const aBrandType = new Array(
//0:品牌名称,1:颜色
 ["区域公用品牌",'purple'], //0
 ["农业企业品牌",'blue'], //1
 ["农产品品牌", '#ff9900'], //2
 ["广西农交会金奖", '#339933'], //3
 ["广西农交会银奖", '#cc0000'],//4
 ["中国国际农交会金奖",'#00cccc']//5
); 

然后我们定义二维数组aBrands,存放每个年度获得各类品牌的数量:

var aBrands = new Array(
 //0:年份,1:品牌总数量,2:区域公用品牌数量,3:农业企业数量;4:农产品品牌数量;5:广西农交会金奖数量……
 [2018,7,2,1,4],
 [2019,11,3,4,4],
 [2020,3,0,2,1,2],
 [2021,7,0,4,3],
 [2022,6,1,4,1]
);

接着我们与绘制饼图时定义一个饼图对象相类似的,定义一个柱状图对象BarChart

BarChart.prototype = {
……
}//BarChart.prototype


柱状图对象BarChart具有如下公开属性:

var bcBarChart = new BarChart({
    ctx:    cbcCtx,    //画布
    width:    cbc.width,//画布宽度
    height:    cbc.height,//画布高度
    data:    aBrands,//柱状数据
    legend:    aBrandType,//图例
    title:    bcBarChartTitle //标题
  });

其中bcBarChartTitle是一个对象,包括以下属性:

var  bcBarChartTitle =
{
    text:'品牌时间分布', //标题文字
    marginTop:25, //标题与画布顶部间距
    fontSize:20    //标题字体大小
};

为了简便便起见,我们默认标题颜色为黑色,字体为微软雅黑,所以这里没有标题的颜色和字体属性。

在柱状图对象BarChart内部有几个方法。

第一个是初始化方法init

    init: function ()
    {
        this.scaleX = 60;    //横坐标刻度间距
        this.scaleY = 28;    //纵坐标刻度间距 
    } //init

在初始化方法中,我们可以定义对象内部的一些属性。


第二个是绘制标题方法:

    //绘制标题
    drawTitle: function()
    {
        //如果标题属性已赋值且长度大于0,则输出标题文字
        if (typeof(this.title) != "undefined" && 0 < this.title.text.length)
        {
            this.ctx.font = "bolder " + this.title.fontSize + "px 微软雅黑";
            this.ctx.fillStyle = 'black';
            this.ctx.shadowBlur=20;
            this.ctx.shadowColor="gray";
            this.ctx.fillText(this.title.text, (this.width - this.title.text.length * this.title.fontSize)/2, this.title.marginTop);
        }//if
    },//drawTitle

第三个是绘制图例方法,包括绘制色块和对应的文字说明:

    //绘制图例
    drawLegend: function()
    {
        var maxLen = -1;
        var fontSize = 20;//字体大小
        var boxSize = 20;//色块尺寸
        var sb = 5; //间距Space between

        //取图例中文字说明字数的最大值
        this.legend.forEach(function (v) 
        {
            if (v[1] > maxLen)
            {
                maxLen = v[1].length;
            }
        });

        taDbg.value += '\n maxLen=' + maxLen;

        // 文字说明的横坐标
        //boxX = this.width - maxLen * fontSize - legendBoxSize - sb;
        var textX = this.width - maxLen * fontSize - sb*2 - 200;

        //色块的的横坐标
        var boxX = textX - boxSize - sb;

        //纵坐标
        var y = this.title.marginTop + this.title.fontSize + sb;
        this.ctx.font = fontSize + "px 宋体";

        for (var i=0; i < this.legend.length; i++)
        {
            this.ctx.fillStyle = this.legend[i][1];
            this.ctx.fillRect(boxX, y, boxSize, boxSize);
            this.ctx.fillStyle = 'black';
            this.ctx.fillText(this.legend[i][0], textX, y + fontSize*3/4);
            y += sb*2 + boxSize;
        }//for
    },//drawLegend

第四个是绘制纵坐标方法:

    //绘制纵坐标
    drawOrdinate: function()
    {
        var fontSize = 20;
        var datumPointX = 30;//坐标原点x
        var datumPointY = this.height - 50;//坐标原点y

        //1、取纵坐标最大值
        var maxOrdinate = -1;
        this.data.forEach(function (v)
        {
            if (v[1] > maxOrdinate)
            {
                maxOrdinate = v[1];
            }
        });
        taDbg.value +=     '\n maxOrdinate=' + maxOrdinate;


        //画纵坐标轴
        this.ctx.moveTo(datumPointX, datumPointY);
        this.ctx.lineTo(datumPointX,  this.title.fontSize + this.title.marginTop + fontSize);
        this.ctx.stroke();

        //画纵坐标刻度
        for (var i=0; i <= maxOrdinate; i++)
        {
            var j =  datumPointY - i * this.scaleY;
            this.ctx.moveTo(datumPointX, j);
            //this.ctx.lineTo(this.width - 50 , j);
            this.ctx.lineTo(this.width - (this.data.length-1) * this.scaleX, j);
            this.ctx.stroke();
        }

        //绘制纵坐标刻度值
        //this.ctx.textAlign = "end";
        this.ctx.font = fontSize + "px 宋体";
        for (var i=1; i <= maxOrdinate; i++)
        {
            this.ctx.fillText(i, 0, datumPointY - i * this.scaleY + fontSize/3);
        }

        //输出纵坐标名称
        //this.ctx.fillText('数量', 0, datumPointY -  maxOrdinate * this.scaleY - fontSize);
        this.ctx.fillText('数量', 0, datumPointY -  maxOrdinate * this.scaleY - fontSize*2);

        //taDbg.value +=     '\n minOrdinate=' + minOrdinate + '  maxOrdinate=' + maxOrdinate;
    },//drawOrdinate

第五个是绘制横坐标方法:

    //绘制横坐标
    drawDiascissa: function()
    {
        var fontSize = 20;

        this.ctx.font = fontSize + "px 宋体";
        for (var i=0; i < this.data.length; i++)
        {
            this.ctx.fillText(this.data[i][0], (i+1) * this.scaleX , this.height - fontSize);
        }
        //this.ctx.fillText('年度',  this.width - 4*fontSize, this.height - 50 + fontSize/3);
        this.ctx.fillText('年度',  this.width - (this.data.length-1) * this.scaleX, this.height - fontSize);    
    },//drawDiascissa

最后一个是绘制数据柱状条方法:

    //绘制数据柱状条
    drawBar: function()
    {
        var fontSize = 20;

        this.ctx.font = fontSize + "px 宋体";
        for (var i=0; i < this.data.length; i++)
        {

            var k = this.height - 50;
            for (var j=2; j < this.data[i].length; j++)
            {
                if ("undefined"!=this.data[i][j] && this.data[i][j] > 0) 
                {
                    this.ctx.fillStyle = this.legend[j-2][1];
                    k -= this.data[i][j] * 28;
                    this.ctx.fillRect((i+1) * this.scaleX, k, fontSize * 2, this.data[i][j] * this.scaleY);

                    this.ctx.fillStyle = 'white';
                    this.ctx.fillText(this.data[i][j], (i+1) * this.scaleX + Math.round(fontSize*2/3), k + this.scaleY*this.data[i][j]*2/3);
                
                    //taDbg.value += '\n this.data[' + i + '][' + j + ']=' + this.data[i][j] + '  this.ctx.fillStyle=' + this.ctx.fillStyle;
                }//if
            }//for
        }
    },//drawBar

 
完整的代码如下:

<!DOCTYPE html>
<html>
 <head>
  <meta charset="UTF-8">
  <meta name="Author" content="PurpleEndurer">
  <title>柱状图</title>
 </head>
 <body>
  <canvas id="canvasBarChart" width="600" height="450" style="border:1px solid black; margin:50px; overflow:auto;"> 你的浏览器不支持HTML5 canvas </canvas>
  <textarea border="1" id="taDebug" cols="80" rows="15">debug:</textarea>
    <script type="text/javascript">
var taDbg = document.getElementById("taDebug");


const aBrandType = new Array(
//0:品牌名称,1:颜色
 ["区域公用品牌",'purple'], //0
 ["农业企业品牌",'blue'], //1
 ["农产品品牌", '#ff9900'], //2
 ["广西农交会金奖", '#339933'], //3
 ["广西农交会银奖", '#cc0000'],//4
 ["中国国际农交会金奖",'#00cccc']//5
); 

var aBrands = new Array(
 //0:年份,1:品牌总数量,2:区域公用品牌数量,3:农业企业数量;4:农产品品牌数量;5:广西农交会金奖数量……
 [2018,7,2,1,4],
 [2019,11,3,4,4],
 [2020,3,0,2,1,2],
 [2021,7,0,4,3],
 [2022,6,1,4,1]
);

var cbc = document.getElementById("canvasBarChart");
cbc.height	= 450;
cbc.width	= 600;
var cbcCtx = cbc.getContext("2d");

function BarChart(obj)
{
	//导入外部公开的属性
	for(var key in obj)
	{
		this[key] = obj[key];
	}

	this.init();//初始化
	this.drawTitle();//绘制标题
	this.drawLegend();//绘制图例
	this.drawOrdinate();//绘制横坐标
	this.drawDiascissa();//绘制纵坐标
	this.drawBar();//绘制数据柱状条
}

BarChart.prototype = {
	//绘制纵坐标
	drawOrdinate: function()
	{
		//var scale = 28;//刻度
		var fontSize = 20;
		var datumPointX = 30;//坐标原点x
		var datumPointY = this.height - 50;//坐标原点y

		//1、取纵坐标最大值
		var maxOrdinate = -1;
		this.data.forEach(function (v)
		{
			if (v[1] > maxOrdinate)
			{
				maxOrdinate = v[1];
			}
		});
		taDbg.value +=	 '\n maxOrdinate=' + maxOrdinate;


		//画纵坐标轴
		
		this.ctx.moveTo(datumPointX, datumPointY);
		this.ctx.lineTo(datumPointX,  this.title.fontSize + this.title.marginTop + fontSize);
		this.ctx.stroke();

		//画纵坐标刻度
		for (var i=0; i <= maxOrdinate; i++)
		{
			var j =  datumPointY - i * this.scaleY;
			this.ctx.moveTo(datumPointX, j);
			//this.ctx.lineTo(this.width - 50 , j);
			this.ctx.lineTo(this.width - (this.data.length-1) * this.scaleX, j);
			this.ctx.stroke();
		}

		//绘制纵坐标刻度值
		//this.ctx.textAlign = "end";
		this.ctx.font = fontSize + "px 宋体";
		for (var i=1; i <= maxOrdinate; i++)
		{
			this.ctx.fillText(i, 0, datumPointY - i * this.scaleY + fontSize/3);
		}

		//输出纵坐标名称
		//this.ctx.fillText('数量', 0, datumPointY -  maxOrdinate * this.scaleY - fontSize);
		this.ctx.fillText('数量', 0, datumPointY -  maxOrdinate * this.scaleY - fontSize*2);

		//taDbg.value +=	 '\n minOrdinate=' + minOrdinate + '  maxOrdinate=' + maxOrdinate;
	},//drawOrdinate

	//绘制横坐标
	drawDiascissa: function()
	{
		var fontSize = 20;

		this.ctx.font = fontSize + "px 宋体";
		for (var i=0; i < this.data.length; i++)
		{
			this.ctx.fillText(this.data[i][0], (i+1) * this.scaleX , this.height - fontSize);
		}
		//this.ctx.fillText('年度',  this.width - 4*fontSize, this.height - 50 + fontSize/3);
		this.ctx.fillText('年度',  this.width - (this.data.length-1) * this.scaleX, this.height - fontSize);	
	},//drawDiascissa

	//绘制数据柱状条
	drawBar: function()
	{
		var fontSize = 20;

		this.ctx.font = fontSize + "px 宋体";
		for (var i=0; i < this.data.length; i++)
		{
			var k = this.height - 50;
			for (var j=2; j < this.data[i].length; j++)
			{
				if ("undefined"!=this.data[i][j] && this.data[i][j] > 0) 
				{
					this.ctx.fillStyle = this.legend[j-2][1];
					k -= this.data[i][j] * 28;
					this.ctx.fillRect((i+1) * this.scaleX, k, fontSize * 2, this.data[i][j] * this.scaleY);
					this.ctx.fillStyle = 'white';
					this.ctx.fillText(this.data[i][j], (i+1) * this.scaleX + Math.round(fontSize*2/3), k + this.scaleY*this.data[i][j]*2/3);
				
					//taDbg.value += '\n this.data[' + i + '][' + j + ']=' + this.data[i][j] + '  this.ctx.fillStyle=' + this.ctx.fillStyle;
				}//if
			}//for
		}
	},//drawBar
	
	//绘制图例
	drawLegend: function()
	{
		var maxLen = -1;
		var fontSize = 20;//字体大小
		var boxSize = 20;//色块尺寸
		var sb = 5; //间距Space between

		//取图例中文字说明字数的最大值
		this.legend.forEach(function (v) 
		{
			if (v[1] > maxLen)
			{
				maxLen = v[1].length;
			}
		});

		taDbg.value += '\n maxLen=' + maxLen;

		// 文字说明的横坐标
		//boxX = this.width - maxLen * fontSize - legendBoxSize - sb;
		var textX = this.width - maxLen * fontSize - sb*2 - 200;

		//色块的的横坐标
		var boxX = textX - boxSize - sb;

		//纵坐标
		var y = this.title.marginTop + this.title.fontSize + sb;
		this.ctx.font = fontSize + "px 宋体";

		for (var i=0; i < this.legend.length; i++)
		{
			this.ctx.fillStyle = this.legend[i][1];
			this.ctx.fillRect(boxX, y, boxSize, boxSize);
			this.ctx.fillStyle = 'black';
			this.ctx.fillText(this.legend[i][0], textX, y + fontSize*3/4);
			y += sb*2 + boxSize;
		}//for
	},//drawLegend

	//绘制标题
	drawTitle: function()
	{

		//如果标题属性已赋值且长度大于0,则输出标题文字
		if (typeof(this.title) != "undefined" && 0 < this.title.text.length)
		{
			this.ctx.font = "bolder " + this.title.fontSize + "px 微软雅黑";
			this.ctx.fillStyle = 'black';//默认黑色
			this.ctx.shadowBlur=20;
			this.ctx.shadowColor="gray";
			this.ctx.fillText(this.title.text, (this.width - this.title.text.length * this.title.fontSize)/2, this.title.marginTop);
		}//if
	},//drawTitle

	//初始化
	init: function ()
	{
		this.scaleX = 60;	//横坐标刻度间距
		this.scaleY = 28;	//纵坐标刻度间距 
	} //init
}//BarChart.prototype

var  bcBarChartTitle =
{
	text:'品牌时间分布', //标题文字
	marginTop:25, //标题与画布顶部间距
	fontSize:20	//标题字体
};

var bcBarChart = new BarChart({
	ctx:	cbcCtx,
	width:	cbc.width,
	height:	cbc.height,
	data:	aBrands,
	legend:	aBrandType,
	title:	bcBarChartTitle
  });
    </script>
 </body>
</html>

代码显示效果如下图:

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

紫郢剑侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值