簡體   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