摘要:繼上一篇實戰繪制餅圖之后,筆者研究了一下如何給餅圖加鼠標停留時顯示的提示框。為了方便保存,創建一個構造函數。最終我們的方法成品圖源碼地址
繼上一篇HTML5 Canvas(實戰:繪制餅圖)之后,筆者研究了一下如何給餅圖加鼠標停留時顯示的提示框。
Plot對象在開始Coding之前,筆者能夠想到的最easy的方式,就是給餅圖的每一個區域添加mousemove事件,鼠標在其上移動時則顯示對應的提示框,so easy!可事實不是這樣子滴~
我們肉眼上看上去是一塊一塊的東西,canvas并沒有真的把它們分成一塊一塊的HTMLElement,我們只能給canvas綁定事件。那么如何得知鼠標當前停留在哪塊區域呢,可以通過計算鼠標位置與圓心連線與基準線給的夾角是否在區域的起始角度與終止角度之間,為此,我們需要保存每個區域的角度信息。
為了方便保存,創建一個構造函數Plot。
function Plot(start, end, color, data) { this.start = start; this.end = end; this.color = color; this.data = data; }
可以將上一篇文章中的繪制圖例方法和繪制餅圖區域的方法都放進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(實戰:繪制餅圖) 可以看出,在我們的最初設計中,Tooltip上顯示的內容是可以定制化的,用戶可以設定一個如下的模板:
Year: {{year}}, Data: {{data}}
我們的目標是將上面的模板轉化成:
Year: 2017, Data: 3000
新建一個工具方法,接受template字符串,以及鼠標當前停留plot中的數據,返回實際顯示的字符串:
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; }
注意,從代碼中可以看出,不要習慣性的在{{和}}之間加入空格。
鼠標在哪為了判斷鼠標停留的區域,我們需要完成如下兩步:
計算鼠標位置和圓心之間的弧度angle
遍歷plots,判斷angle是否位于某一個plot的startAngle與endAngle之間,如果找到了這個plot,判斷這個plot是否是上一次的鼠標所在的區域,如果是,說明沒有必要繪制Tooltip,如果不是,重繪圖表。假如沒有找到對應的區域,說明鼠標不在canvas的餅圖區域,可能指向圖例、標題或者空白區域,此時應該清空全局變量currentPlot并重繪畫布。
關于如何判斷鼠標位置與圓心之間的弧度,小編畫了如下的一個餅圖,只能幫到這兒了...
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(); }
現在我們知道了鼠標當前停留的位置,也可以定制要提示的文字,現在可以繪制提示框啦,以下代碼有些累贅,計算過程也有些問題,筆者改天再重新算算~
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時都需要重繪餅圖,而startAngle endAngle在每次繪制時都會修改,因此繪制前需要重置。
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...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95159.html
摘要:繼上一篇實戰繪制餅圖之后,筆者研究了一下如何給餅圖加鼠標停留時顯示的提示框。為了方便保存,創建一個構造函數。最終我們的方法成品圖源碼地址 繼上一篇HTML5 Canvas(實戰:繪制餅圖)之后,筆者研究了一下如何給餅圖加鼠標停留時顯示的提示框。 Plot對象 在開始Coding之前,筆者能夠想到的最easy的方式,就是給餅圖的每一個區域添加mousemove事件,鼠標在其上移動時則顯示...
摘要:綜上,最后我們的工具函數應該長成下面這個樣子首先獲取繪圖上下文,仍要注意先判斷是否存在方法。把標題繪制在畫布頂部的中間,距離頁面頂部留有像素的空隙,并且根據參數,繪制具有特定內容和樣式的標題。 有了canvas之后,我們可以很容易地創建一個簡單圖標,不需要任何插件,不過,有的小伙伴覺得它很難,筆者仔細思考一番之后,只能吐嘈一下他們的繪圖技能...于是在開始繪制之前,我們首先畫一下草圖~...
閱讀 2571·2021-08-20 09:38
閱讀 1355·2019-08-30 15:43
閱讀 593·2019-08-29 17:13
閱讀 1601·2019-08-29 14:01
閱讀 1314·2019-08-29 13:29
閱讀 2322·2019-08-23 18:29
閱讀 2047·2019-08-23 17:51
閱讀 1915·2019-08-23 17:16