摘要:繼上一篇實(shí)戰(zhàn)繪制餅圖之后,筆者研究了一下如何給餅圖加鼠標(biāo)停留時(shí)顯示的提示框。為了方便保存,創(chuàng)建一個(gè)構(gòu)造函數(shù)。最終我們的方法成品圖源碼地址
繼上一篇HTML5 Canvas(實(shí)戰(zhàn):繪制餅圖)之后,筆者研究了一下如何給餅圖加鼠標(biāo)停留時(shí)顯示的提示框。
Plot對(duì)象在開(kāi)始Coding之前,筆者能夠想到的最easy的方式,就是給餅圖的每一個(gè)區(qū)域添加mousemove事件,鼠標(biāo)在其上移動(dòng)時(shí)則顯示對(duì)應(yīng)的提示框,so easy!可事實(shí)不是這樣子滴~
我們?nèi)庋凵峡瓷先ナ且粔K一塊的東西,canvas并沒(méi)有真的把它們分成一塊一塊的HTMLElement,我們只能給canvas綁定事件。那么如何得知鼠標(biāo)當(dāng)前停留在哪塊區(qū)域呢,可以通過(guò)計(jì)算鼠標(biāo)位置與圓心連線(xiàn)與基準(zhǔn)線(xiàn)給的夾角是否在區(qū)域的起始角度與終止角度之間,為此,我們需要保存每個(gè)區(qū)域的角度信息。
為了方便保存,創(chuàng)建一個(gè)構(gòu)造函數(shù)Plot。
function Plot(start, end, color, data) { this.start = start; this.end = end; this.color = color; this.data = data; }
可以將上一篇文章中的繪制圖例方法和繪制餅圖區(qū)域的方法都放進(jìn)Plot的原型鏈中
Plot.prototype.drawLegend = function() { ctx.fillRect(legend_posX, legend_posY, legend_width, legend_height); ctx.font = "bold 12px Arial"; var percent = this.data.label + " : " + (this.data.portion * 100).toFixed(2) + "%"; ctx.fillText(percent, legend_textX, legend_textY); }
Plot.prototype.drawPlot = function() { ctx.fillStyle = this.color; ctx.beginPath(); ctx.moveTo(center.x, center.y); ctx.arc(center.x, center.y, radius, this.start, this.end, false); ctx.closePath(); ctx.fill(); }定制的Tooltip
在上一篇文章 HTML5 Canvas(實(shí)戰(zhàn):繪制餅圖) 可以看出,在我們的最初設(shè)計(jì)中,Tooltip上顯示的內(nèi)容是可以定制化的,用戶(hù)可以設(shè)定一個(gè)如下的模板:
Year: {{year}}, Data: {{data}}
我們的目標(biāo)是將上面的模板轉(zhuǎn)化成:
Year: 2017, Data: 3000
新建一個(gè)工具方法,接受template字符串,以及鼠標(biāo)當(dāng)前停留plot中的數(shù)據(jù),返回實(shí)際顯示的字符串:
function replaceAttr(text, data) { while (text.indexOf("{{") != -1) { var start = text.indexOf("{{"), end = text.indexOf("}}"), attr = text.substring(start + 2, end); text = text.replace("{{" + attr + "}}", data[attr]); } return text; }
注意,從代碼中可以看出,不要習(xí)慣性的在{{和}}之間加入空格。
鼠標(biāo)在哪為了判斷鼠標(biāo)停留的區(qū)域,我們需要完成如下兩步:
計(jì)算鼠標(biāo)位置和圓心之間的弧度angle
遍歷plots,判斷angle是否位于某一個(gè)plot的startAngle與endAngle之間,如果找到了這個(gè)plot,判斷這個(gè)plot是否是上一次的鼠標(biāo)所在的區(qū)域,如果是,說(shuō)明沒(méi)有必要繪制Tooltip,如果不是,重繪圖表。假如沒(méi)有找到對(duì)應(yīng)的區(qū)域,說(shuō)明鼠標(biāo)不在canvas的餅圖區(qū)域,可能指向圖例、標(biāo)題或者空白區(qū)域,此時(shí)應(yīng)該清空全局變量currentPlot并重繪畫(huà)布。
關(guān)于如何判斷鼠標(biāo)位置與圓心之間的弧度,小編畫(huà)了如下的一個(gè)餅圖,只能幫到這兒了...
function getAngle(cx, cy, mx, my) { var x = Math.abs(cx - mx), y = Math.abs(cy - my), z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), cos = y / z, radina = Math.acos(cos); if (mx > cx && my > cy) { return radina; } else if (mx < cx && my > cy) { return Math.PI / 2 + radina; } else if (mx > cx && my < cy) { return 3 * Math.PI / 2 - radina } else { return 3 * Math.PI / 2 + radina } }
function onMouseMove(e) { var ex = e.pageX - cv.offsetLeft, ey = e.pageY - cv.offsetTop; var angle = getAngle(center.x, center.y, ex, ey); for (let i = 0; i < plots.length; i++) { if (plots[i].start < angle && plots[i].end > angle) { if (currentPlot != plots[i]) { currentPlot = plots[i]; draw(); } return; } } currentPlot = null; draw(); }
現(xiàn)在我們知道了鼠標(biāo)當(dāng)前停留的位置,也可以定制要提示的文字,現(xiàn)在可以繪制提示框啦,以下代碼有些累贅,計(jì)算過(guò)程也有些問(wèn)題,筆者改天再重新算算~
Plot.prototype.drawTooltip = function() { var text = replaceAttr(op.tooltip.template, this.data); var width_tooltipText = ctx.measureText(text).width, height_tooltipText = parseInt(op.tooltip.font.size, 10), angle = (this.start + this.end) / 2 / (2 * Math.PI) *360; var tan = Math.tanh(angle), x = 0, y = 0; if (angle < 90)((x = radius / 2 * tan + center.x) || true) && ((y = -radius / 2 + center.y) || true) else if (angle > 90 && angle < 180)((x = radius / 2 * tan + center.x) || true) && ((y = radius / 2 + center.y) || true) else if (angle > 180 && angle < 270)((x = -radius / 2 * tan + center.x) || true) && ((y = radius / 2 + center.y) || true) else if (angle > 270 && angle < 360)((x = -radius / 2 * tan + center.x) || true) && ((y = -radius / 2 + center.y) || true) var tooltip_box_x = x - radius / 4, tooltip_box_y = y, tooltip_box_width = width_tooltipText + 10, tooltip_box_height = height_tooltipText + 10, tooltip_text_x = x - radius / 4 + 5, tooltip_text_y = y + 10 + 2; ctx.fillStyle = "white"; ctx.fillRect(tooltip_box_x, tooltip_box_y, tooltip_box_width, tooltip_box_height); ctx.fillStyle = "#000"; ctx.fillText(text, tooltip_text_x, tooltip_text_y); }
每次重繪Tooltip時(shí)都需要重繪餅圖,而startAngle endAngle在每次繪制時(shí)都會(huì)修改,因此繪制前需要重置。
function clear() { ctx.clearRect(0, 0, cv.width, cv.height); startAngle = 0; endAngle = 0; cv.onmousemove = null; }
最終我們的draw方法~
function draw() { clear(); title_text = op.title.text; ctx.font = op.title.font.weight + " " + op.title.font.size + "px " + op.title.font.family; title_width = ctx.measureText(title_text).width; title_height = op.title.font.size; title_position = { x: (width, title_width) / 2, y: 20 + title_height }; ctx.fillText(title_text, title_position.x, title_position.y); radius = (height - title_height - title_position.y - 20) / 2; center = { x: radius + 20, y: radius + 30 + title_position.y }; legend_width = op.legend.font.size * 2.5; legend_height = op.legend.font.size * 1.2; legend_posX = center.x * 2 + 20; legend_posY = 80; legend_textX = legend_posX + legend_width + 5; legend_textY = legend_posY + op.legend.font.size * 0.9; ctx.strokeStyle = "grey"; ctx.lineWidth = 3; ctx.strokeRect(0, 0, width, height); for (var i = 0, len = data_c.length; i < len; i++) { endAngle += data_c[i].portion * 2 * Math.PI; var plot = new Plot(startAngle, endAngle, data_c[i].color, data_c[i]) plots.push(plot); plot.drawPlot(); startAngle = endAngle; legend_posY += (10 + legend_height); legend_textY += (10 + legend_height); plot.drawLegend(); } if (currentPlot) { currentPlot.drawTooltip(); } cv.onmousemove = onMouseMove; }
成品圖:
源碼地址:https://github.com/Sue1024/ca...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/95159.html
摘要:繼上一篇實(shí)戰(zhàn)繪制餅圖之后,筆者研究了一下如何給餅圖加鼠標(biāo)停留時(shí)顯示的提示框。為了方便保存,創(chuàng)建一個(gè)構(gòu)造函數(shù)。最終我們的方法成品圖源碼地址 繼上一篇HTML5 Canvas(實(shí)戰(zhàn):繪制餅圖)之后,筆者研究了一下如何給餅圖加鼠標(biāo)停留時(shí)顯示的提示框。 Plot對(duì)象 在開(kāi)始Coding之前,筆者能夠想到的最easy的方式,就是給餅圖的每一個(gè)區(qū)域添加mousemove事件,鼠標(biāo)在其上移動(dòng)時(shí)則顯示...
摘要:綜上,最后我們的工具函數(shù)應(yīng)該長(zhǎng)成下面這個(gè)樣子首先獲取繪圖上下文,仍要注意先判斷是否存在方法。把標(biāo)題繪制在畫(huà)布頂部的中間,距離頁(yè)面頂部留有像素的空隙,并且根據(jù)參數(shù),繪制具有特定內(nèi)容和樣式的標(biāo)題。 有了canvas之后,我們可以很容易地創(chuàng)建一個(gè)簡(jiǎn)單圖標(biāo),不需要任何插件,不過(guò),有的小伙伴覺(jué)得它很難,筆者仔細(xì)思考一番之后,只能吐嘈一下他們的繪圖技能...于是在開(kāi)始繪制之前,我們首先畫(huà)一下草圖~...
閱讀 2590·2021-08-20 09:38
閱讀 1367·2019-08-30 15:43
閱讀 606·2019-08-29 17:13
閱讀 1615·2019-08-29 14:01
閱讀 1325·2019-08-29 13:29
閱讀 2347·2019-08-23 18:29
閱讀 2058·2019-08-23 17:51
閱讀 1928·2019-08-23 17:16