HTML canves 饼图2
在上一个框架上修改的另外一种战绩展示饼图
源代码:
1 //饼图 2 var pieChart = { 3 width: 600, 4 height: 400, 5 series: [], 6 unit: "场", 7 chartCanvas: null, 8 selectable : true, 9 title: "Pie Chart", 10 legend : { 11 enable : true 12 }, 13 edge : { 14 width: 10, 15 height: 10 16 }, 17 animation: { 18 enable: true, 19 animCanvas : null, 20 hh: 1, // trick is here!! for animation play 21 pctx: null 22 }, 23 tooltips: { 24 enable: true, 25 tooltipCanvas : null, 26 ttContext: null, 27 index: -1 28 }, 29 circle : { 30 cx: 0, 31 cy: 0, 32 radius: 0 33 34 }, 35 text : { 36 enable: false, 37 content:[] 38 }, 39 40 init: function (config) { 41 this.chartCanvas = config.canvas; 42 this.chartCanvas.width = config.width; 43 this.chartCanvas.height = config.height; 44 this.width = config.width ; 45 this.height = config.height ; 46 this.series = config.series; 47 this.title = config.title; 48 this.unit = config.unit; 49 this.count = config.count; 50 this.max = config.max; 51 this.shadow = config.shadow; 52 53 var ctx = this.chartCanvas.getContext("2d"); 54 this.devicePixelRatio = window.devicePixelRatio || 1; 55 this.backingStoreRatio = ctx.webkitBackingStorePixelRatio || 56 ctx.mozBackingStorePixelRatio || 57 ctx.msBackingStorePixelRatio || 58 ctx.oBackingStorePixelRatio || 59 ctx.backingStorePixelRatio || 1; 60 this.devicePixelRatio = this.devicePixelRatio/this.backingStoreRatio; 61 this.chartCanvas.height = this.height * this.devicePixelRatio; 62 this.chartCanvas.width = this.width * this.devicePixelRatio; 63 64 this.chartCanvas.style.height = this.height + "px"; 65 this.chartCanvas.style.width = this.width + "px"; 66 this.width = config.width * this.devicePixelRatio-5* this.devicePixelRatio; 67 this.height = config.height * this.devicePixelRatio; 68 this.edge.width = 40 * this.devicePixelRatio; 69 this.edge.height = 40 * this.devicePixelRatio; 70 if(config.tooltips != undefined) { 71 this.tooltips.enable = config.tooltips.enable; 72 } 73 if(config.animation != undefined) { 74 this.animation.enable = config.animation.enable; 75 } 76 if(config.legend != undefined) { 77 this.legend.enable = config.legend.enable; 78 } 79 if(config.text != undefined) { 80 this.text.enable = config.text.enable; 81 } 82 this.render(); 83 }, 84 85 render : function() { 86 // initialization circle 87 this.circle.cx = this.width/3.8; 88 this.circle.cy = this.height/2; 89 this.circle.radius = Math.min(this.width/2.4, this.height/2.4) - 12*this.devicePixelRatio; 90 var ctx = null; 91 if(this.animation.enable) { 92 this.animation.animCanvas = document.createElement("canvas"); 93 this.animation.animCanvas.width = this.width; 94 this.animation.animCanvas.height = this.height; 95 ctx = this.animation.animCanvas.getContext("2d"); 96 } else { 97 ctx = this.chartCanvas.getContext("2d"); 98 this.renderBorder(ctx); 99 } 100 101 if(this.circle.radius <= 0) { 102 ctx.strokeText("Can not reader the chart, Circle is too small."); 103 return; 104 } 105 106 // draw each arc according to data series 107 var sum = 0; 108 var nums = this.series.length; 109 for(var i=0; i<nums; i++) { 110 sum += this.series[i].value; 111 } 112 113 // draw title 114 var pt = 18*this.devicePixelRatio; 115 ctx.font = pt+'px Microsoft YaHeii'; 116 ctx.fillText(this.title, this.width/2 - this.edge.width, 30); 117 ctx.save(); 118 var deltaArc = 0; 119 for(var i=0; i<nums; i++) { 120 var precent = this.series[i].value/sum; 121 this.renderPie(ctx, i, precent, deltaArc); 122 deltaArc += 2*Math.PI * precent; 123 } 124 ctx.restore(); 125 126 if(this.shadow){ 127 // add blur shadow 128 ctx.save(); 129 ctx.shadowColor = "black"; 130 ctx.shadowOffsetX = 0; 131 ctx.shadowOffsetY = 0; 132 ctx.shadowBlur = 10; 133 ctx.beginPath(); 134 ctx.arc(this.circle.cx, this.circle.cy, this.circle.radius, 0, Math.PI * 2, false); 135 ctx.closePath(); 136 ctx.lineWidth = 1; 137 ctx.strokeStyle = "RGBA(127,127,127,1)"; 138 ctx.stroke(); 139 ctx.restore(); 140 } 141 142 // 画内部空白 143 ctx.beginPath(); 144 ctx.moveTo(this.circle.cx, this.circle.cy); 145 ctx.arc(this.circle.cx, this.circle.cy, this.circle.radius*0.25, 0, Math.PI * 2, true); 146 ctx.closePath(); 147 ctx.fillStyle = 'rgba(255,255,255,1)'; 148 ctx.fill(); 149 150 ctx.moveTo(this.circle.cx-10*this.devicePixelRatio, this.circle.cy); 151 ctx.font = 'bold 12px 微软雅黑'; //斜体 30像素 微软雅黑字体 152 var pt = 12*this.devicePixelRatio; 153 ctx.font = pt+'px Microsoft YaHeii'; 154 ctx.fillStyle = "black"; //"#000000"; 155 var percent = this.count+this.unit; 156 ctx.fillText(percent,this.circle.cx-10*this.devicePixelRatio, this.circle.cy+5*this.devicePixelRatio); 157 158 // render legend 159 ctx.save(); 160 this.renderLegend(ctx, sum); 161 ctx.restore(); 162 163 // play animation 164 if(this.animation.enable) { 165 var parent = this; 166 this.animation.pctx = this.chartCanvas.getContext("2d"); 167 this.renderBorder(this.animation.pctx); 168 setTimeout(function(){ parent.playAnimation(parent);},0) 169 } 170 }, 171 172 showTooltips : function(loc, ctx) { 173 if(!this.tooltips.enable) { 174 return; 175 } 176 var dx = loc.x - this.width/2; 177 var dy = loc.y - this.height/2; 178 var dis = Math.floor(Math.sqrt(dx * dx + dy * dy)); 179 if(dis <= this.circle.radius) { 180 // draw tool tip text 181 var angle = Math.atan2(dy,dx); 182 if(angle <= 0) { 183 // if[-Math.PI, 0], make it[Math.PI, 2*Math.PI] 184 angle = angle + 2*Math.PI; 185 } 186 187 var sum = 0; 188 var nums = this.series.length; 189 for(var s=0; s<nums; s++) { 190 sum += this.series[s].value; 191 } 192 193 var deltaArc = 0; 194 var index = 0; 195 for(var i=0; i<nums; i++) { 196 var precent = this.series[i].value/sum; 197 deltaArc += 2*Math.PI * precent; 198 if(angle<=deltaArc) { 199 index = i; 200 break; 201 } 202 } 203 if(this.tooltips.tooltipCanvas == null) { 204 this.tooltips.tooltipCanvas = document.createElement("canvas"); 205 this.tooltips.ttContext = this.tooltips.tooltipCanvas.getContext("2d"); 206 this.tooltips.tooltipCanvas.width = 150; 207 this.tooltips.tooltipCanvas.height = 100; 208 } 209 210 // only draw once 211 // if(index == this.tooltips.index){ 212 // return; 213 // } 214 this.clearTooltips(ctx); 215 216 this.tooltips.index = index; 217 var m_context = this.tooltips.ttContext; 218 m_context.save(); 219 m_context.clearRect(0, 0, this.tooltips.tooltipCanvas.width, this.tooltips.tooltipCanvas.height); 220 m_context.lineWidth = 2; 221 m_context.strokeStyle = this.series[index].color; 222 m_context.fillStyle="RGBA(255,255,255,0.7)"; 223 // m_context.strokeRect(2, 2, this.tooltips.tooltipCanvas.width-4, this.tooltips.tooltipCanvas.height-4); 224 // m_context.fillRect(2,2,this.tooltips.tooltipCanvas.width-4, this.tooltips.tooltipCanvas.height-4); 225 //m_context.font="14px Arial"; 226 var pt = 14*this.devicePixelRatio; 227 m_context.font = pt+'px Microsoft YaHeii'; 228 m_context.fillStyle="RGBA(0,0,0,1)"; 229 m_context.fillText(this.series[index].name + ": " + this.series[index].value + this.unit, 5, 40); 230 m_context.restore(); 231 232 // make tool-tip rectangle is always visible 233 if((loc.x + this.tooltips.tooltipCanvas.width)> this.width) { 234 loc.x = loc.x - this.tooltips.tooltipCanvas.width; 235 } 236 if((loc.y - this.tooltips.tooltipCanvas.height) <= 0) { 237 loc.y = loc.y + this.tooltips.tooltipCanvas.height; 238 } 239 ctx.drawImage(this.tooltips.tooltipCanvas, 0, 0, this.tooltips.tooltipCanvas.width, this.tooltips.tooltipCanvas.height, 240 loc.x, loc.y-this.tooltips.tooltipCanvas.height, this.tooltips.tooltipCanvas.width, this.tooltips.tooltipCanvas.height); 241 } else { 242 this.tooltips.index = -1; 243 this.clearTooltips(ctx); 244 } 245 }, 246 247 clearTooltips : function(ctx) { 248 ctx.clearRect(0,0,this.width, this.height); 249 this.renderBorder(ctx); 250 ctx.drawImage(this.animation.animCanvas, 0, 0, this.width, this.height, 0, 0, this.width, this.height); 251 }, 252 253 renderBorder : function(ctx) { 254 //canvas 边框 255 //ctx.save(); 256 //ctx.fillStyle="white"; 257 //ctx.strokeStyle="black"; 258 //ctx.fillRect(0, 0, this.width, this.height); 259 //ctx.strokeRect(0, 0, this.width, this.height); 260 //ctx.restore(); 261 }, 262 263 renderPie : function(ctx, index, precent, deltaArc) { 264 var endAngle = deltaArc + 2*Math.PI*precent; 265 ctx.save(); 266 ctx.lineWidth=this.devicePixelRatio; 267 ctx.beginPath(); 268 ctx.arc(this.circle.cx, this.circle.cy, this.circle.radius, deltaArc, endAngle, false); 269 ctx.moveTo(this.circle.cx, this.circle.cy); 270 ctx.lineTo(this.circle.cx + this.circle.radius * Math.cos(deltaArc), this.circle.cy + this.circle.radius * Math.sin(deltaArc)); 271 ctx.lineTo(this.circle.cx + this.circle.radius * Math.cos(endAngle), this.circle.cy + this.circle.radius * Math.sin(endAngle)); 272 ctx.lineTo(this.circle.cx, this.circle.cy); 273 ctx.closePath(); 274 ctx.fillStyle = this.series[index].color; 275 ctx.fill(); 276 ctx.restore(); 277 //画扇形 278 var avgAngle = (endAngle - deltaArc)/2; 279 var _deltaArc = deltaArc; 280 var _endAngle = endAngle; 281 var _circlex = this.circle.cx+this.circle.radius+(this.width/5)*this.devicePixelRatio; 282 var _circley = index*(this.height/3.5)*this.devicePixelRatio + this.edge.height*1.7*this.devicePixelRatio; 283 var _radius = this.circle.radius/3; 284 ctx.fillStyle = this.series[index].color; 285 ctx.sector(_circlex,_circley,_radius,_deltaArc,_endAngle,false).fill(); 286 287 ctx.beginPath(); 288 ctx.strokeStyle = "#CCC5C5"; 289 ctx.arc(_circlex,_circley,_radius,0,Math.PI*2); 290 ctx.stroke(); 291 292 var size = this.text.content.length; 293 var tipStr = this.series[index].tips; 294 var pt = 10*this.devicePixelRatio; 295 ctx.font = pt+'pt Microsoft YaHeii'; 296 ctx.textAlign="left"; 297 var textArr = tipStr.split(","); 298 for(var textIndex = 0; textIndex < textArr.length;textIndex++){ 299 ctx.fillStyle = "#666"; 300 ctx.fillText(textArr[textIndex],_circlex+_radius+10*this.devicePixelRatio,_circley-((textArr.length-1)*10*this.devicePixelRatio)+20*this.devicePixelRatio*textIndex); 301 } 302 303 // 渲染文字 304 if(this.text.enable) { 305 var halfEndAngle = deltaArc + Math.PI*precent; 306 var hx = this.circle.cx + this.circle.radius * Math.cos(halfEndAngle); 307 var hy = this.circle.cy + this.circle.radius * Math.sin(halfEndAngle); 308 309 var avgx = Math.abs(hx - this.circle.cx)/2; 310 var avgy = Math.abs(hy - this.circle.cy)/2; 311 var textPos = (hx < this.circle.cx) ? (hx - this.edge.width)*0.9 : (hx + this.edge.width)*1.1; 312 var textPosX = (hx < this.circle.cx) ? hx + avgx: hx - avgx; 313 var textPosY = (hy < this.circle.cy) ? hy + avgy : hy - avgy; 314 precent = Math.round (precent*100) / 100; 315 var size = this.text.content.length; 316 var tipStr = (size > index) ? this.text.content[index] : this.series[index].name + ": " + ((this.series[index].value/this.count)*100).toFixed(0) + "%"; 317 var pt = 12*this.devicePixelRatio; 318 ctx.font = pt+'pt Microsoft YaHeii'; 319 320 //给圆环加文本 321 ctx.fillStyle="white"; 322 323 ctx.textAlign="center"; 324 if(this.series[index].value > 0)ctx.fillText(tipStr, textPosX, textPosY); 325 } 326 }, 327 328 renderLegend : function(ctx, sum) { 329 if(!this.legend.enable) return; 330 var nums = this.series.length; 331 var pt = 12*this.devicePixelRatio; 332 ctx.font = pt+'px Microsoft YaHeii'; 333 var pos = (this.width/2 > (this.circle.radius+50 * this.devicePixelRatio)) ? 50*this.devicePixelRatio : (this.circle.cx - this.circle.radius); 334 for(var i=0; i<nums; i++) { 335 var x = this.series[i].value/sum; 336 x = Math.round (x*100) / 100; 337 var tipStr = this.series[i].name + ": " + this.series[i].value + "场"; 338 this.series[i].precent = tipStr; 339 ctx.fillStyle = this.series[i].color; 340 ctx.fillRect((pos - 40*this.devicePixelRatio), (20*i+10) * this.devicePixelRatio, 10 * this.devicePixelRatio, 10 * this.devicePixelRatio); 341 ctx.fillStyle = "black"; 342 ctx.fillText(tipStr, (pos - 25*this.devicePixelRatio), (20*i+20) * this.devicePixelRatio); 343 } 344 }, 345 346 playAnimation : function(parent) { 347 if(parent.animation.hh < parent.height) { 348 parent.animation.pctx.save(); 349 parent.animation.pctx.globalAlpha=0.5; 350 parent.animation.pctx.clearRect(0,0,parent.width, parent.height); 351 parent.renderBorder(parent.animation.pctx); 352 parent.animation.pctx.drawImage(parent.animation.animCanvas, 0, 0, parent.width, this.animation.hh, 0, 0, parent.width, this.animation.hh); 353 parent.animation.hh = parent.animation.hh + 10; 354 parent.animation.pctx.restore(); 355 setTimeout(function() {parent.playAnimation(parent);}, 0); 356 } else { 357 parent.animation.pctx.clearRect(0,0,parent.width, parent.height); 358 parent.renderBorder(parent.animation.pctx); 359 parent.animation.pctx.drawImage(parent.animation.animCanvas, 0, 0, parent.width, parent.height, 0, 0, parent.width, parent.height); 360 361 // enable tool-tip functionality 362 if(parent.animation.enable && parent.legend.enable) { 363 parent.chartCanvas.addEventListener('mousemove', function(event) { 364 var x = event.pageX; 365 var y = event.pageY; 366 var canvas = event.target; 367 var bbox = canvas.getBoundingClientRect(); 368 var loc = { x: x - bbox.left * (canvas.width / bbox.width), 369 y: y - bbox.top * (canvas.height / bbox.height)}; 370 371 parent.showTooltips(loc, (parent.animation.enable ? parent.animation.pctx : ctx)); 372 }, false); 373 } 374 } 375 } 376 377 }; 378 //扇形 379 CanvasRenderingContext2D.prototype.sector = function (x, y, radius, sDeg, eDeg,fx) { 380 // 初始保存 381 this.save(); 382 // 位移到目标点 383 this.translate(x, y); 384 this.beginPath(); 385 // 画出圆弧 386 this.arc(0,0,radius,sDeg, eDeg,fx); 387 // 再次保存以备旋转 388 this.save(); 389 // 旋转至起始角度 390 this.rotate(eDeg); 391 // 移动到终点,准备连接终点与圆心 392 this.moveTo(radius,0); 393 // 连接到圆心 394 this.lineTo(0,0); 395 // 还原 396 this.restore(); 397 // 旋转至起点角度 398 this.rotate(sDeg); 399 // 从圆心连接到起点 400 this.lineTo(radius,0); 401 this.closePath(); 402 // 还原到最初保存的状态 403 this.restore(); 404 return this; 405 }
调用方式:
1 var canvas = document.getElementById("myCanvas"); 2 var seriesData = [{name:"胜", value:5, color:"#ec362c",tips:"主胜概率:74.78%,1.主赔<客赔,2. 主强且主胜,水位下降,低于初盘达2.56%"}, 3 {name:"平", value:3, color:"#b3b3b3",tips:"主平概率:12.61%,1. 平赔:7"}, 4 {name:"负", value:1, color:"#599bee",tips:"主负概率:12.61%,1. 平赔:14"}] 5 var config = { 6 width : 600 , 7 height: 400, 8 series: seriesData, 9 canvas: canvas, 10 count:8, 11 max:0, 12 unit: "场", 13 title:"", 14 shadow:false, 15 legend : { 16 enable : false 17 }, 18 text : { 19 enable: true 20 }, 21 }; 22 pieChart.init(config);
最终效果:


浙公网安备 33010602011771号