繁体   English   中英

使用chart.js在圆环图中心的事件处理程序

[英]event handler on center of doughnut chart using chart.js

我在一个页面上创建了4个圆环图,中间有一些文本,我知道这可以通过在中心放置DIV来完成,但我不能使用它,因为当图表下载为PNG时文本不会被导出:

演示: https//jsfiddle.net/cmyker/ooxdL2vj/

我需要跟踪中心文本的点击,我尝试使用pageX,pageY来确定是否在中心部分进行了点击。

坐标是矩形部分的角,它位于圆环图的中心孔内,并且可能有文本。

jQuery('#canvas').on('click',function(e){
  var pageX = e.pageX;                                  
  var pageY = e.pageY;
      if((pageY >= 379 && pageY <= 571) && (pageX >= 440 && pageX <= 629)){   //coordinates are of rectangular area which has text inside the center of doughnut chart.
             //do something                                          
      }
});

但是如果屏幕的分辨率不同,这将不起作用,因为坐标会有所不同。

有什么想法吗?

我试图使用raphael.js使中心可点击但不太确定这个尝试。 我正在尝试使用container方法在甜甜圈的中心孔中创建一个圆圈,可以在其上附加一个点击处理程序。

使用Raphael JS的代码信息

Chart.pluginService.register({
                  beforeDraw: function(chart) {
                  if(chart['data']['midNum']){
                      var width = chart.chart.width,
                          height = chart.chart.height,
                          ctx = chart.chart.ctx;

                      ctx.restore();
                      var fontSize = (height / 114).toFixed(2);
                      ctx.font = fontSize + "em sans-serif";
                      ctx.textBaseline = "middle";

                      var text = chart['data']['midNum'],
                          textX = Math.round((width - ctx.measureText(text).width) / 2),
                          textY = height / 2.5;
                        var chartID = chart['chart']['canvas']['id']; //the ID of element on which this donut was created

                        var paper  = Raphael(chartID,textX,textY); //trying to use the container approach
                        var circle = paper.circle(textX, textY, 10);
                            circle.attr("fill", "#f00");
                      //ctx.clearRect(0,0,width,height);
                      //ctx.fillText(text, textX, textY);
                      //ctx.save();
                    }
                  }
                })

这是关于此代码的原始问题的答案。 自发布以来,问题已经多次更改 - 添加了保存为PNG的要求,图表的数量从原始代码中的1更改为4,并且使用的框架从HTML Canvas上的Chart.js呈现更改为Raphaël渲染SVG。 我将离开我发布的解决方案,希望将来对某人有用。

我这里的想法很少:

寻找像素

一种较慢但可靠的方法:知道您对黑色像素感兴趣,您可以迭代画布的所有像素并记住4个数字:您找到的任何黑色像素的最小和最大x和y坐标。 您可以为此添加一些边距,并且您将拥有一个始终点亮的矩形,即使库在未来版本中开始在不同的位置写入文本也是如此。

重绘画布后,每次重新调整窗口时都必须重新计算它。

要使其工作,您的文本必须采用画布上任何其他位置都不存在的颜色(目前情况就是如此)。

猜猜坐标

您可以猜测坐标 - 或者更准确地计算它们 - 知道它是如何绘制的。 似乎大圆圈占据了较小尺寸上的整个空间(在这种情况下为整个高度),并且在另一个轴上居中(在这种情况下,它是水平居中的)。

使用这些知识,您可以计算内部(白色)圆的大小和位置,只有画布尺寸的方式与此类似:

找到画布的宽度或高度较小,并将其用作基数。 将它除以2会得到R - 大圆的半径。 除以R/2将粗略地给出r - 小的内部白色圆的半径。 计算xy - 画布中心的坐标 - x = width/2y = height /2

现在,您可以尝试文本所在的矩形。 它可能类似于:左边和右边的x - 0.7*rx + 0.7*r ,底边和顶边的y - 0.4*ry + 0.4*r 这些仅仅是示例,您可以将这些数字调整到令您满意的程度。

这些数字不一定非常完美,因为无论如何你应该在文本周围留下几个像素的边距。

这里它可能无法工作,当图书馆开始在未来完全不同地绘制它,但它可能不会像这样的简单图表。

好处是你不必寻找特定的颜色,并且检查每个像素的计算速度会更快。

同样,如果图表以不同的大小重新绘制,则必须重新计算这些维度。

更改您的pluginService

另一个想法是改变你pluginServicebeforeDraw函数,以便它保存已有的数字。

特别是,你已经拥有:

textX = Math.round((width - ctx.measureText(text).width) / 2),
textY = height / 2;

如果您将其更改为:

var measured = ctx.measureText(text);
textX = Math.round((width - measured.width) / 2),
textY = height / 2;

(只是为了避免以后重新计算文本测量)然后你可以存储以下数字:

要么textXtextY以及measured.widthmeasured.height要么是具有以下属性的对象:

var textPos = {
    x1: textX,
    y1: textY,
    x2: textX + measured.width,
    y2: textY + measured.height
}

如果需要,请务必使用四舍五入。 您可以将该对象存储在某个全局对象中,或者存储为某些HTML元素的data-*属性(如画布本身)。

最后一个解决方案很好,因为你不必担心颜色,你不必猜测文本的放置位置,因为你完全知道,并且你不必担心重新调整大小,因为每次绘制文本本身时代码都会运行。

缺点是您需要修改pluginService

画布上的div

另一种方法是在画布上放置div并将文本放在div而不是画布中。 这样你就可以方便地添加事件监听器等。

你可以这样做:

把你的画布和空div(或更多div)放在一个更大的div中:

<div id="chart">
  <canvas id="myChart"></canvas>
  <div class="chart-text" id="text1"></div>
</div>

您可以为更多的圆/图表添加更多div,例如text1,如下所示:

<div id="chart">
  <canvas id="myChart"></canvas>
  <div class="chart-text" id="text1"></div>
  <div class="chart-text" id="text2"></div>
</div>

添加此CSS以使它们正确堆叠:

#chart { position: relative; }
.chart-text { position: absolute; }

现在,您将文本添加到内部div而不是在画布上绘制它:

var text1 = document.getElementById('text1');
text1.addEventListener("click", function (e) {
  alert("CLICKED!");
});

Chart.pluginService.register({
  beforeDraw: function(chart) {
    var width = chart.chart.width,
        height = chart.chart.height;

    var fontSize = (height / 114).toFixed(2);
    text1.style.font = fontSize + "em sans-serif";

    var text = "75%";
    text1.innerText = text;
    var r = text1.getBoundingClientRect();
    text1.style.left = ((width-r.width)/2)+"px";
    text1.style.top = ((height-r.height)/2)+"px";
  }
});

DEMO

它可以简化,但将文本放在画布中可能更简单,并且您可以拥有事件侦听器或简单的CSS样式。 例如添加:

.chart-text:hover { color: red; }

将在悬停时使其变红。

画布上的空div

在评论中未包含在问题中的其他要求之后,这是另一个更新。

您可以像上面的版本中一样使用此HTML:

<div id="chart">
<canvas id="myChart"></canvas>
<div class="chart-text" id="text1"></div>
</div>

但是这次你可以在画布上添加一个空div,这样文本就会包含在画布中,保存它将包含文本。

这是需要的CSS:

#chart { position: relative; }
.chart-text { position: absolute; }

这是CSS将显示隐形div的位置:

#chart { position: relative; }
.chart-text { position: absolute; border: 1px solid red; }

现在代码将div放在应该的位置:

var text1 = document.getElementById('text1');
text1.addEventListener("click", function (e) {
  alert("CLICKED!");
});

Chart.pluginService.register({
  beforeDraw: function(chart) {
    var width = chart.chart.width,
        height = chart.chart.height,
        ctx = chart.chart.ctx;

    ctx.restore();
    var fontSize = (height / 114).toFixed(2);
    ctx.font = fontSize + "em sans-serif";
    ctx.textBaseline = "middle";

    var text = "75%",
        m = ctx.measureText(text),
        textX = Math.round((width - m.width) / 2),
        textY = height / 2;

    var emInPx = 16;
    text1.style.left = textX + "px";
    text1.style.top = (textY - fontSize*emInPx/2) + "px";
    text1.style.width = m.width + "px";
    text1.style.height = fontSize+"em";

    ctx.fillText(text, textX, textY);
    ctx.save();
  }
});

确保emInPx每个em单元具有正确的px (CSS像素)块。 您以em单位定义fontSize ,我们需要像素来计算正确的位置。

看到DEMO (它有一个红色边框使div可见 - 只需删除border: 1px solid red;从CSS中删除它)

在画布上的大空div

另一个例子 - 这次div大于文本:

var text1 = document.getElementById('text1');
text1.addEventListener("click", function (e) {
  alert("CLICKED!");
});

Chart.pluginService.register({
  beforeDraw: function(chart) {
    var width = chart.chart.width,
        height = chart.chart.height,
        ctx = chart.chart.ctx;

    ctx.restore();
    var fontSize = (height / 114).toFixed(2);
    ctx.font = fontSize + "em sans-serif";
    ctx.textBaseline = "middle";

    var text = "75%",
        m = ctx.measureText(text),
        textX = Math.round((width - m.width) / 2),
        textY = height / 2;

    var d = Math.min(width, height);
    var a = d/2.5;

    text1.style.left = ((width - a) / 2) + "px";
    text1.style.top = ((height - a) / 2) + "px";
    text1.style.width = a + "px";
    text1.style.height = a + "px";

    ctx.fillText(text, textX, textY);
    ctx.save();
  }
});

DEMO 它不依赖于pxem大小和文本大小。 这一行改变了正方形的大小:

var a = d / 2.5;

您可以尝试将2.5更改为2或3或其他内容。

在画布上的圆形空div

这是一个变体,它使用border-radius来制作圆形div而不是矩形,并且似乎完美地填充内部白色圆圈。

HTML:

<div id="chart">
<canvas id="myChart"></canvas>
<div class="chart-text" id="text1"></div>
</div>

CSS:

#chart, #myChart, .chart-text {  padding: 0; margin: 0; }
#chart { position: relative; }
.chart-text { position: absolute; border-radius: 100%; }

JS:

var text1 = document.getElementById('text1');
text1.addEventListener("click", function (e) {
  alert("CLICKED!");
});

Chart.pluginService.register({
  beforeDraw: function(chart) {
    var width = chart.chart.width,
        height = chart.chart.height,
        ctx = chart.chart.ctx;

    ctx.restore();
    var fontSize = (height / 114).toFixed(2);
    ctx.font = fontSize + "em sans-serif";
    ctx.textBaseline = "middle";

    var text = "75%",
        m = ctx.measureText(text),
        textX = Math.round((width - m.width) / 2),
        textY = height / 2;

    var d = Math.min(width, height);
    var a = d / 2;

    text1.style.left = (((width - a) / 2 - 1)|0) + "px";
    text1.style.top = (((height - a) / 2 - 1)|0) + "px";
    text1.style.width = a + "px";
    text1.style.height = a + "px";

    ctx.fillText(text, textX, textY);
    ctx.save();
  }
});

DEMO

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM