2017-04-04 112 views
0

我有一個使用Chart.js的圓環圖來正確顯示我的應用程序的登錄數據,但是我修改了圖表,以便登錄的總數顯示在中心切口:使用Chart.js更改圓環圖中的工具提示位置

enter image description here

我遇到的問題是與提示。當我將鼠標懸停在光水鴨一塊餅圖,如果圖表是按比例縮小,工具提示是由中央的文字重疊,像這樣:

enter image description here

我希望能夠改變工具提示的方向延伸出來,所以它不會朝向中心,而是移開,以便工具提示和中心分析都可見,但我還沒有找到關於如何更改工具提示定位的簡明說明。這裏是我目前的代碼:

var loslogged = dataset[0][0].loslogged; 
var realtorlogged = dataset[1][0].realtorlogged; 
var borrowerlogged = dataset[2][0].borrowerlogged; 

var totallogged = parseInt(loslogged) + parseInt(realtorlogged) + parseInt(borrowerlogged); 

Chart.pluginService.register({ 
    afterDraw: function (chart) { 
     if (chart.config.options.elements.center) { 
      var helpers = Chart.helpers; 
      var centerX = (chart.chartArea.left + chart.chartArea.right)/2; 
      var centerY = (chart.chartArea.top + chart.chartArea.bottom)/2; 

      var ctx = chart.chart.ctx; 
      ctx.save(); 
      var fontSize = helpers.getValueOrDefault(chart.config.options.elements.center.fontSize, Chart.defaults.global.defaultFontSize); 
      var fontStyle = helpers.getValueOrDefault(chart.config.options.elements.center.fontStyle, Chart.defaults.global.defaultFontStyle); 
      var fontFamily = helpers.getValueOrDefault(chart.config.options.elements.center.fontFamily, Chart.defaults.global.defaultFontFamily); 
      var font = helpers.fontString(fontSize, fontStyle, fontFamily); 
      ctx.font = font; 
      ctx.fillStyle = helpers.getValueOrDefault(chart.config.options.elements.center.fontColor, Chart.defaults.global.defaultFontColor); 
      ctx.textAlign = 'center'; 
      ctx.textBaseline = 'middle'; 
      ctx.fillText(chart.config.options.elements.center.text, centerX, centerY); 
      ctx.restore(); 
     } 
    } 
}); 

var loginChartData = { 
    labels: ["Loan Officers","Realtors","Borrowers"], 
    datasets: [{ 
     label: "Number of Logins", 
     data: [loslogged, realtorlogged, borrowerlogged], 
     backgroundColor: [ 
      "rgba(191, 25, 25, 0.75)", 
      "rgba(58, 73, 208, 0.75)", 
      "rgba(79, 201, 188, 0.75)" 
     ], 
     borderColor: [ 
      "rgba(255, 255, 255, 1)", 
      "rgba(255, 255, 255, 1)", 
      "rgba(255, 255, 255, 1)" 
     ], 
     borderWidth: 4 
    }], 
    gridLines: { 
     display: false 
    } 
}; 

var loginChartOptions = { 
    title: { 
     display: false 
    }, 
    cutoutPercentage: 50, 
    elements: { 
     center: { 
      text: totallogged, 
      fontColor: '#000', 
      fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", 
      fontSize: 36, 
      fontStyle: 'bold' 
     } 
    } 
}; 

var loginChart = document.getElementById('loginsChart').getContext('2d'); 
new Chart(loginChart, { 
    type: 'doughnut', 
    data: loginChartData, 
    options: loginChartOptions 
}); 

回答

1

它在以前版本的chart.js(v2.3和之前版本)中反轉工具提示過去很容易。您所要做的只是覆蓋determineAlignment工具提示方法並將其邏輯反轉。

但是,從v2.4開始,計算工具提示位置(包括determineAlignment)的函數被設置爲私有,所以不再有簡單覆蓋它們的方法(相反,您必須重複它們)。

這是一個可逆的工具提示解決方案,不幸的是需要從chart.js源代碼中進行大量的複製和粘貼操作(因爲這些方法是私有的,所以這是必需的)。這種方法的風險在於,隨時可能會在新版本中更改潛在的私有函數,並且新的反向工具提示可能會意外中斷。

就這麼說,這裏是貫穿實施的過程(底部是一個codepen示例)。

1)首先,我們擴展Chart.Tooltip對象並創建一個新的Chart.ReversedTooltip對象。我們真的只需要覆蓋update方法,因爲它執行所有的定位邏輯。事實上,這種覆蓋只是從源頭上直接複製和粘貼的,因爲我們實際上只需要修改update調用的私有determineAlignment方法。

// create a new reversed tooltip. we must overwrite the update method which is 
// where all the positioning occurs 
Chart.ReversedTooltip = Chart.Tooltip.extend({ 
    update: function(changed) { 
    var me = this; 
    var opts = me._options; 

    // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition 
    // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time 
    // which breaks any animations. 
    var existingModel = me._model; 
    var model = me._model = getBaseModel(opts); 
    var active = me._active; 

    var data = me._data; 
    var chartInstance = me._chartInstance; 

    // In the case where active.length === 0 we need to keep these at existing values for good animations 
    var alignment = { 
     xAlign: existingModel.xAlign, 
     yAlign: existingModel.yAlign 
    }; 
    var backgroundPoint = { 
     x: existingModel.x, 
     y: existingModel.y 
    }; 
    var tooltipSize = { 
     width: existingModel.width, 
     height: existingModel.height 
    }; 
    var tooltipPosition = { 
     x: existingModel.caretX, 
     y: existingModel.caretY 
    }; 

    var i, len; 

    if (active.length) { 
     model.opacity = 1; 

     var labelColors = []; 
     tooltipPosition = Chart.Tooltip.positioners[opts.position](active, me._eventPosition); 

     var tooltipItems = []; 
     for (i = 0, len = active.length; i < len; ++i) { 
     tooltipItems.push(createTooltipItem(active[i])); 
     } 

     // If the user provided a filter function, use it to modify the tooltip items 
     if (opts.filter) { 
     tooltipItems = tooltipItems.filter(function(a) { 
      return opts.filter(a, data); 
     }); 
     } 

     // If the user provided a sorting function, use it to modify the tooltip items 
     if (opts.itemSort) { 
     tooltipItems = tooltipItems.sort(function(a, b) { 
      return opts.itemSort(a, b, data); 
     }); 
     } 

     // Determine colors for boxes 
     helpers.each(tooltipItems, function(tooltipItem) { 
     labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, chartInstance)); 
     }); 

     // Build the Text Lines 
     model.title = me.getTitle(tooltipItems, data); 
     model.beforeBody = me.getBeforeBody(tooltipItems, data); 
     model.body = me.getBody(tooltipItems, data); 
     model.afterBody = me.getAfterBody(tooltipItems, data); 
     model.footer = me.getFooter(tooltipItems, data); 

     // Initial positioning and colors 
     model.x = Math.round(tooltipPosition.x); 
     model.y = Math.round(tooltipPosition.y); 
     model.caretPadding = helpers.getValueOrDefault(tooltipPosition.padding, 2); 
     model.labelColors = labelColors; 

     // data points 
     model.dataPoints = tooltipItems; 

     // We need to determine alignment of the tooltip 
     tooltipSize = getTooltipSize(this, model); 
     alignment = determineAlignment(this, tooltipSize); 
     // Final Size and Position 
     backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment); 
    } else { 
     model.opacity = 0; 
    } 

    model.xAlign = alignment.xAlign; 
    model.yAlign = alignment.yAlign; 
    model.x = backgroundPoint.x; 
    model.y = backgroundPoint.y; 
    model.width = tooltipSize.width; 
    model.height = tooltipSize.height; 

    // Point where the caret on the tooltip points to 
    model.caretX = tooltipPosition.x; 
    model.caretY = tooltipPosition.y; 

    me._model = model; 

    if (changed && opts.custom) { 
     opts.custom.call(me, model); 
    } 

    return me; 
    }, 
}); 

2)可以看到,該update方法使用的私有方法少數(例如getBaseModelcreateTooltipItemdetermineAlignment,等等)。爲了使我們的update方法真正起作用,我們必須爲這些方法中的每一個提供一個實現。這裏又是另一個來源的複製和粘貼。然而,我們需要修改的唯一方法是determineAlignment方法。這是反轉對齊邏輯的修改版本。

// modified from source to reverse the position 
function determineAlignment(tooltip, size) { 
    var model = tooltip._model; 
    var chart = tooltip._chart; 
    var chartArea = tooltip._chartInstance.chartArea; 
    var xAlign = 'center'; 
    var yAlign = 'center'; 

    // set caret position to top or bottom if tooltip y position will extend outsite the chart top/bottom 
    if (model.y < size.height) { 
    yAlign = 'top'; 
    } else if (model.y > (chart.height - size.height)) { 
    yAlign = 'bottom'; 
    } 

    var leftAlign, rightAlign; // functions to determine left, right alignment 
    var overflowLeft, overflowRight; // functions to determine if left/right alignment causes tooltip to go outside chart 
    var yAlign; // function to get the y alignment if the tooltip goes outside of the left or right edges 
    var midX = (chartArea.left + chartArea.right)/2; 
    var midY = (chartArea.top + chartArea.bottom)/2; 

    if (yAlign === 'center') { 
    leftAlign = function(x) { 
     return x >= midX; 
    }; 
    rightAlign = function(x) { 
     return x < midX; 
    }; 
    } else { 
    leftAlign = function(x) { 
     return x <= (size.width/2); 
    }; 
    rightAlign = function(x) { 
     return x >= (chart.width - (size.width/2)); 
    }; 
    } 

    overflowLeft = function(x) { 
    return x - size.width < 0; 
    }; 
    overflowRight = function(x) { 
    return x + size.width > chart.width; 
    }; 
    yAlign = function(y) { 
    return y <= midY ? 'bottom' : 'top'; 
    }; 

    if (leftAlign(model.x)) { 
    xAlign = 'left'; 

    // Is tooltip too wide and goes over the right side of the chart.? 
    if (overflowLeft(model.x)) { 
     xAlign = 'center'; 
     yAlign = yAlign(model.y); 
    } 
    } else if (rightAlign(model.x)) { 
    xAlign = 'right'; 

    // Is tooltip too wide and goes outside left edge of canvas? 
    if (overflowRight(model.x)) { 
     xAlign = 'center'; 
     yAlign = yAlign(model.y); 
    } 
    } 

    var opts = tooltip._options; 
    return { 
    xAlign: opts.xAlign ? opts.xAlign : xAlign, 
    yAlign: opts.yAlign ? opts.yAlign : yAlign 
    }; 
}; 

3)現在,我們的新Chart.ReversedTooltip完成後,我們需要使用插件系統原來的工具提示更改爲我們扭轉提示。我們可以使用afterInit插件方法來做到這一點。

Chart.plugins.register({ 
    afterInit: function (chartInstance) { 
    // replace the original tooltip with the reversed tooltip 
    chartInstance.tooltip = new Chart.ReversedTooltip({ 
     _chart: chartInstance.chart, 
     _chartInstance: chartInstance, 
     _data: chartInstance.data, 
     _options: chartInstance.options.tooltips 
    }, chartInstance); 

    chartInstance.tooltip.initialize(); 
    } 
}); 

畢竟,我們終於扭轉了工具提示!在codepen處查看完整的工作示例。

還值得一提的是,這種方法非常脆弱,正如我所提到的,可以很容易地打破加班(由於需要複製和粘貼)。另一種選擇是使用自定義工具提示,並將其放在圖表上的任何位置。

檢出此chart.js sample,顯示如何設置和使用自定義工具提示。你可以用這種方法去修改定位邏輯。

+0

您的解釋非常詳盡,謝謝。當我嘗試使用此代碼時,不會出現工具提示,並且出現錯誤「ReferenceError:找不到變量:getBaseModel」 – Jodo1992

+0

您是否從codepen複製並粘貼?聽起來你可能錯過了複製該功能? – jordanwillis