资讯专栏INFORMATION COLUMN

HTML5 Canvas(实战:绘制饼图2 Tooltip)

songze / 2845人阅读

摘要:继上一篇实战绘制饼图之后,笔者研究了一下如何给饼图加鼠标停留时显示的提示框。为了方便保存,创建一个构造函数。最终我们的方法成品图源码地址

继上一篇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是否位于某一个plotstartAngleendAngle之间,如果找到了这个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...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/52397.html

相关文章

  • HTML5 Canvas实战绘制饼图2 Tooltip

    摘要:继上一篇实战绘制饼图之后,笔者研究了一下如何给饼图加鼠标停留时显示的提示框。为了方便保存,创建一个构造函数。最终我们的方法成品图源码地址 继上一篇HTML5 Canvas(实战:绘制饼图)之后,笔者研究了一下如何给饼图加鼠标停留时显示的提示框。 Plot对象 在开始Coding之前,笔者能够想到的最easy的方式,就是给饼图的每一个区域添加mousemove事件,鼠标在其上移动时则显示...

    CoderBear 评论0 收藏0
  • HTML5 Canvas实战绘制饼图

    摘要:综上,最后我们的工具函数应该长成下面这个样子首先获取绘图上下文,仍要注意先判断是否存在方法。把标题绘制在画布顶部的中间,距离页面顶部留有像素的空隙,并且根据参数,绘制具有特定内容和样式的标题。 有了canvas之后,我们可以很容易地创建一个简单图标,不需要任何插件,不过,有的小伙伴觉得它很难,笔者仔细思考一番之后,只能吐嘈一下他们的绘图技能...于是在开始绘制之前,我们首先画一下草图~...

    tinna 评论0 收藏0

发表评论

0条评论

songze

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<