利用难得的假期继续改进“圳品”信息系统,在另一个模块中使用之前的Pie()代码编制饼图。
代码详见:改进“圳品”信息系统网页上的饼图:加标题+文字说明换行显示
却发现饼图标题没显示出来。
研究源代码后发现问题出在计算饼图标题输出位置的代码上,即:
this.ctx.fillText(this.title, (tcCanvas.width - this.title.length*fontSize)/2, 25);
在这里我们使用了tcCanvas.width,由于tcCanvas是一个实例,在另一个实例中调用这段代码就会出现问题。
改进的代码是把Canvas的width作为一个属性传递进来,即分为两步。
第一步,增加width属性:
var typeCountPie = new Pie({ ctx: ctx, x: tcCanvas.width / 2, y: tcCanvas.height / 2, r: 150, width:tcCanvas.width,//增加width属性 data:aTypeCount, title:'“圳品”产品类别分析' });
第2步,修改计算饼图标题输出位置的代码,改为:
this.ctx.fillText(this.title, (this.width - this.title.length*fontSize)/2, 25);
也就是用this.width 替代 了 tcCanvas.width,增强了代码通用性。完整的代码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="Author" content="PurpleEndurer"> <meta name="Keywords" content=""> <meta name="Description" content=""> <title>“圳品”信息系统</title> </head> <body> <script> const aType = [ "水果",//0 "粮食",//1,谷类、豆类、薯类 "食用油",//2 "饮用水",//3 "畜",//4 "禽",//5 "渔",//6 "其它"//7 ]; //结构: //类别,数量,颜色 var aTypeCount = [ [0, 3, "purple"], [1, 5, "gray"], [2,3, 'yellow'], [3,4,"green"], [4,8,"red"] ]; </script> <canvas id="typeCountCanvas" style="border:1px solid red"></canvas> <div> <textarea border="1" id="taDebug" cols="80" rows="15">debug:</textarea> </div> <script> var tcCanvas = document.getElementById("typeCountCanvas"); //var w = window.innerWidth; //var h = window.innerHeight; tcCanvas.height = 480; tcCanvas.width = 640; var ctx = tcCanvas.getContext('2d'); var taDbg = document.getElementById("taDebug"); function Pie(obj) { for(var key in obj) { this[key] = obj[key]; } this.init(); this.render(this.slice); this.drawText(); } Pie.prototype = { init: function () { this.start = 0; //1、累计数据求合 var sum = 0; this.data.forEach(function (v) { sum += v[1]; }); //2、计算每一个数据所占的比重 this.slice = this.data.map(function (v) { var obj = {}; obj.number = v[1]; obj.ratio = v[1] / sum;//每个数据占据的比重 obj.radian = 2 * Math.PI * v[1] / sum;//该扇形所占据的弧度 obj.start = this.start; obj.end = this.start + obj.radian; this.start = obj.end; obj.color = v[2]; obj.text = aType[v[0]] + "类产品"+ v[1] + "个,占比" + Math.ceil(obj.ratio*100) + "%"; return obj; },this); }, //渲染页面(画饼图) render: function (obj) { //3、计算每一个扇形的起始弧度和结束弧度 this.slice.forEach(function (v, i) { var obj = {}; //第一个扇形的起始弧度:start 结束:start+第一个扇形占据的弧度差 obj.start = this.start; obj.end = this.start + v.radian; this.start += v.radian; //绘制扇形 this.ctx.beginPath(); this.ctx.moveTo(this.x, this.y); this.ctx.arc(this.x, this.y, this.r, obj.start, obj.end); this.ctx.fillStyle = v.color; this.ctx.fill(); }, this); }, //输出文字说明 drawText:function(){ var fontSize = 20;//标题文字大小 //如果标题属性已赋值且长度大于0,则输出标题文件 if (typeof(this.title) != "undefined" && 0 < this.title.length) { this.ctx.font = "bolder "+ fontSize + "px 微软雅黑"; this.ctx.fillStyle = 'black'; this.ctx.fillText(this.title, (this.width - this.title.length*fontSize)/2, 25); } fontSize= 16; this.ctx.font = fontSize+"px 微软雅黑"; var textWidth= 100; this.slice.forEach(function(obj){ this.ctx.fillStyle = obj.color;// 'black'; //计算文字所在的弧度 r2 = obj.start + obj.radian/2; //计算相对于圆心文字偏移的位置 b = this.r * Math.cos(r2) ; h = this.r * Math.sin(r2); //文字的x坐标位置 var x2 = this.x + b; if (x2 <= this.x) { //在圆心的左侧 x2 -= textWidth+10; } //文字的y坐标位置 var y2 = this.y + h; if (y2 >= this.y) { y2 += fontSize; } else { //在圆心的上方 y2 -= fontSize; } //this.ctx.fillText(obj.text, x2, y2);//在一行输出 taDbg.value += "\nthis.r=" + this.r + " obj.text= " + obj.text + " x2=" + x2 + " y2=" + y2; //换行输出方法1 var charNumPerLine = Math.ceil(textWidth / fontSize); var t = obj.text, i = 0; while (t.length > charNumPerLine) { this.ctx.fillText(t.substr(i,charNumPerLine), x2,y2);//绘制截取部分 i += charNumPerLine; t = t.substring(i); y2 += fontSize; } if (i <= obj.text.length) { this.ctx.fillText(t, x2,y2);//绘制剩余部分 } /* //换行输出方法2 var i, lastSubStrIndex = lineWidth=0; for (i=0;i< obj.text.length; i++) { taDbg.value += '\nobj.text['+i +']=' + obj.text[i]; lineWidth += ctx.measureText(obj.text[i]).width; if (lineWidth > textWidth) { this.ctx.fillText(obj.text.substring(lastSubStrIndex,i), x2,y2);//绘制截取部分 y2 += fontSize; lineWidth = 0; lastSubStrIndex = i; } if(i==obj.text.length-1) { //绘制剩余部分 this.ctx.fillText(obj.text.substring(lastSubStrIndex,i+1),x2,y2); break; } }//for*/ },this); } }; var typeCountPie = new Pie({ ctx: ctx, x: tcCanvas.width / 2, y: tcCanvas.height / 2, r: 150, width:tcCanvas.width,//增加width属性 data:aTypeCount, title:'“圳品”产品类别分析' }); </script> </body> </html>