Chart.JS插件,动画后绘制

时间:2018-03-07 13:34:32

标签: javascript charts chart.js

我试图编写一个chart.js(v.2.7)插件来显示我的散点图/线图上的错误栏。 我设法从图表中获取像素位置并在画布上绘制错误栏,但我无法让时机看起来正确。我希望它们在线条动画后出现,然后在数据集被隐藏 - >显示时保持附着。

我试过了:

  • afterDraw / afterDataset(s)Draw / beforeDraw挂钩:错误栏已经在线条动画之前的情节中显示(如示例所示)。当隐藏 - >显示时,错误栏就位。
  • afterRender / afterEvent hook:在动画完成后绘制它们,但每次隐藏数据集时重绘它们 - >显示(暂停后)
  • beforeRender /任何早期的挂钩:不提供错误栏
  • 绘图功能上的
  • setTimout(),或者在其中的不同位置:什么都不做
  • 在绘制函数或其他地方之前
  • sleep():减慢整个动画的速度,但错误栏不受影响。
  • 我无法在动画后找到调用插件函数的方法,或者通过options.animation.onComplete
  • 找到方法

有没有办法让错误栏像示例一样运行,但是初始外观发生在线动画之后(带插件?)



   var errorbarPlugin = {

	calcPoints: function(chartInstance, dataList){
		var ds = chartInstance.data.datasets
      var meta = chartInstance.getDatasetMeta(0)
        var yScale = chartInstance.scales[meta.yAxisID];
      var xScale = chartInstance.scales[meta.xAxisID];
      
      
		var yList = []; var xList = [];
			for(var i = 0; i < dataList.length; i++){
              
				var yValue = dataList[i].y
				var yPixel = yScale.getPixelForValue(yValue)
				yList.push(yPixel)
                
				var xValue = dataList[i].x
				var xPixel = xScale.getPixelForValue(xValue)
				xList.push(xPixel)
			}
      
		return {yList: yList, xList: xList}
	},

	calcErrorbars: function(chartInstance, ds_num){
		var ds = chartInstance.data.datasets
			var data_list = ds[ds_num].data
      
			var isHidden = ds[ds_num]._meta[Object.keys(chartInstance.data.datasets[ds_num]._meta)[0]].hidden;
			var yList = this.calcPoints(chartInstance, data_list).yList
			var xList = this.calcPoints(chartInstance, data_list).xList
			if(ds[ds_num].errors){
				var errors = ds[ds_num].errors
			} else {errors = 0}
      
		return [xList, yList, errors, isHidden]
	},


	drawErrorbars: function(chartInstance){
		var ctx = chartInstance.chart.ctx
		var ds = chartInstance.data.datasets
        		
		for(var ds_num = 0; ds_num < ds.length; ds_num++){
			var errCalc = this.calcErrorbars(chartInstance, ds_num)
			
			var isHidden = errCalc[3]
			var yList = errCalc[1]
			var xList = errCalc[0]
			var errors = errCalc[2]
			var errWidth = 3
			var capLen = 5

			if(!isHidden){
				for(var k = 0; k < xList.length; k++){
					
                  ctx.strokeStyle = "red"
				                  ctx.beginPath();
                  ctx.moveTo(xList[k], yList[k]-errors[k]);
                  ctx.lineTo(xList[k], yList[k]+errors[k]);
                  ctx.moveTo(xList[k]-capLen, yList[k]+errors[k]);
                  ctx.lineTo(xList[k]+capLen, yList[k]+errors[k]);
                  ctx.moveTo(xList[k]-capLen, yList[k]-errors[k]);
                  ctx.lineTo(xList[k]+capLen, yList[k]-errors[k]);
                  ctx.stroke()
					
				}
			}
		}
	},

	afterDatasetsDraw: function(chartInstance) {
		this.drawErrorbars(chartInstance)
	 },

}
&#13;
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.js"></script>



<script type="text/javascript" src="{% static 'js/charts/errorbarPlugin3.js' %}"></script>

	<div id="canvas-holder" class="col-sm-3">
		<canvas id="chart-gamma" width="200" height="200"/></canvas>
	</div>

<script defer>
var gammaChartData = {
	datasets: [
		{
		label: 'Red',
		data: [{x: 15, y: 30}, {x: 35, y: 17}, {x: 55, y: 37}, {x: 72, y: 45},],
		borderColor: "red",
		errors: [10, 28, 30, 34],
		},
	]
	}

var ctx_gamma = document.getElementById("chart-gamma").getContext("2d");

	window.onload = function() {
	var gamma_chart = new Chart(ctx_gamma, {
		type: 'scatter',
		data: gammaChartData,
		plugins: [errorbarPlugin],
		options: {showLines: true},
	});
};
</script>


</body>

</html>
&#13;
&#13;
&#13;

编辑:通过删除格式和默认选项缩短代码段

2 个答案:

答案 0 :(得分:0)

这是我想出的(下面的代码段)

错误栏既可以固定到数据,也可以独立显示,并且/中的动画与数据一起隐藏/显示。

该插件可以添加x或y轴错误,表示为条形(带/不带帽)或椭圆形/圆形(实心或透明)。

"use strict";
var errorbarPlugin = {
    afterDraw: function (chart) {
        var type = chart.config.type;
        var plugConfig = chart.config.options.errorbarPlugin;
        if (plugConfig) {
            if (plugConfig.showErrors) {
                var showErrors = plugConfig.showErrors;
            }
        }
        else
            showErrors = true;
        if (showErrors !== false) {
            if (["line", "scatter"].includes(type)) {
                errorbarPlugin.scatterErrorbars(chart);
            }
            else if (type == "bar") {
                console.log("Bar charts not supported yet");
            }
        }
    },
    scatterErrorbars: function (chart) {
        var ctx = chart.ctx;
        var plugConfig = chart.config.options.errorbarPlugin;
        chart.data.datasets.forEach(function (dataset, i) {
            var ds = dataset;
            var meta = chart.getDatasetMeta(i);
            var showErrors;
            (ds.showErrors === false) ? showErrors = false : showErrors = true;
            var errWidth;
            (ds.errWidth) ? errWidth = ds.errWidth : errWidth = 1;
            var showCap;
            (ds.showCap) ? showCap = ds.showCap : showCap = true;
            var capLen;
            (ds.capLen) ? capLen = ds.capLen : capLen = 3;
            var errStyle;
            (ds.errStyle) ? errStyle = ds.errStyle : errStyle = "T";
            var errFillColor;
            (ds.errFillColor) ? errFillColor = ds.errFillColor : errFillColor = "rgba(0,0,0,0)";
            if (!meta.hidden && showErrors) {
                meta.data.forEach(function (element, index) {
                    var x_point = element._model.x;
                    var y_point = element._model.y;
                    var errColor;
                    (ds.errColor) ? errColor = ds.errColor : errColor = element._view.borderColor;
                    var dataPoint = ds.data[index];
                    var yError;
                    var xError;
                    if (typeof (dataPoint) === "object" && 'r' in dataPoint) {
                        yError = dataPoint.r;
                    }
                    else if (ds.errors) {
                        yError = ds.errors[index];
                    }
                    else {
                        yError = null;
                    }
                    if (typeof (dataPoint) === "object" && dataPoint.e) {
                        xError = dataPoint.e;
                    }
                    else if (ds.xErrors) {
                        xError = ds.xErrors[index];
                    }
                    else {
                        xError = null;
                    }
                    var position = element.tooltipPosition();
                    if (errStyle == "circle") {
                        ctx.beginPath();
                        ctx.arc(position.x, position.y, yError, 0, 2 * Math.PI, false);
                        if (ds.hidden === true && meta.hidden === null) {
                            ctx.strokeStyle = "rgba(0,0,0,0)";
                            ctx.fillStyle = "rgba(0,0,0,0)";
                        }
                        else {
                            ctx.strokeStyle = errColor;
                            ctx.fillStyle = errFillColor;
                        }
                        console.log(meta.hidden);
                        ctx.fill();
                        ctx.stroke();
                    }
                    else if (errStyle == "oval" || errStyle == "ellipse") {
                        if (xError) {
                            var scaleFac = (xError) / yError;
                        }
                        else
                            scaleFac = 10 / yError;
                        ctx.beginPath();
                        ctx.save();
                        ctx.scale(scaleFac, 1);
                        ctx.arc(position.x / scaleFac, position.y, yError, 0, 2 * Math.PI, false);
                        ctx.restore();
                        if (ds.hidden === true && meta.hidden === null) {
                            ctx.strokeStyle = "rgba(0,0,0,0)";
                        }
                        else {
                            ctx.strokeStyle = errColor;
                        }
                        ctx.stroke();
                    }
                    else {
                        ctx.beginPath();
                        ctx.moveTo(position.x, position.y - yError);
                        ctx.lineTo(position.x, position.y + yError);
                        if (xError) {
                            ctx.moveTo(position.x - xError, position.y);
                            ctx.lineTo(position.x + xError, position.y);
                        }
                        if (ds.hidden === true && meta.hidden === null) {
                            ctx.strokeStyle = "rgba(0,0,0,0)";
                        }
                        else {
                            ctx.strokeStyle = errColor;
                        }
                        ctx.stroke();
                        if (showCap) {
                            ctx.beginPath();
                            ctx.moveTo(position.x - capLen, position.y - yError);
                            ctx.lineTo(position.x + capLen, position.y - yError);
                            ctx.moveTo(position.x - capLen, position.y + yError);
                            ctx.lineTo(position.x + capLen, position.y + yError);
                            if (xError) {
                                ctx.moveTo(position.x - xError, position.y - capLen);
                                ctx.lineTo(position.x - xError, position.y + capLen);
                                ctx.moveTo(position.x + xError, position.y - capLen);
                                ctx.lineTo(position.x + xError, position.y + capLen);
                            }
                            ctx.stroke();
                        }
                    }
                });
            }
        });
    }
};
<!DOCTYPE html>

<!--DOCTYPE html -->
<html>
<head>

<script src='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js'></script>



<body>
 
<div style = "position:relative;
    width:60%;"      >

	<div id="canvas-holder" class="col-sm-6">
		<canvas id="chart-gamma" width="500" height="500"/></canvas>
	</div>

		<div id="canvas-holderbf2" class="col-sm-6">
			<canvas id="chart-humid" width="500" height="500"/></canvas>
		</div>

<script defer>

Chart.defaults.global.legend.display = true
Chart.defaults.global.legend.position = 'right'
// Chart.defaults.global.legend.onHover = function(){}
// Chart.defaults.global.legend.onClick = function(){}
Chart.defaults.global.legend.labels.usePointStyle = true
Chart.defaults.global.legend.labels.fontsize = 12
Chart.defaults.global.legend.labels.padding = 10

var gammaChartData = {
	datasets: [
		{
		label: 'Eu',
		data: [{x: 5, y: 45}, {x: 10, y: 100}, {x: 25, y: 120}, {x: 50, y: 125}, {x: 100, y: 150}, {x: 120, y: 250},],
		borderColor: "red",
		//fillColor: "pink",
		errors: [15, 20, 30, 12, 10, 10],
		xErrors: [3, 7, 16, 12, 12, 30, 10],
		//hidden: true,
		errColor: "blue",
		errStyle: "circle",
		errFillColor: "pink",
		hidden: true,
		errWidth: 2,
		showCap: true,
		capLen: 3,
		showErrors: true,
		},
		{
		label: 'Am',
		data: [{x: 15, y: 85, r: 14}, {x: 25, y: 37, r: 8}, {x: 62, y: 135, r: 44},],
		borderColor: "blue",
		errColor: "red",
		errStyle: "circle",
		showErrors: true,
		},
	]

	}
var options_gamma = {
	animation: {
		duration: 1000,
	},
	errorbarPlugin: {
					showErrors: true,
					marginsOfError: [100, 50, 10],
	},
	elements: {
		line: { fill: false,
				borderWidth: 1,
		},
		point: { radius: 0,
				pointStyle: 'circle',
				borderWidth: 1,
				hitRadius: 18, //size if should hover
				// hoverBorderWidth: 13,
				hoverRadius: 10, //size when hovered
		},
	},
	annotation: {
		annotations: [{
				id: 'h-line-01', // optional
				type: 'line',
				mode: 'horizontal',
				scaleID: 'y-axis-0',
				value: '125',
				borderColor: 'red',
				borderDash: [2, 2],
				borderWidth: 2,
				label: {
						enabled: true,
						backgroundColor: 'rgba(255,255,255,1)', // Background color of label, default below
						//fontFamily: "sans-serif", // Font family of text, inherits from global
						fontStyle: "normal", // Font style of text, default "bold"
						fontSize: 12, // Font size of text, inherits from global
						fontColor: "red",// Font color of text, default below
						xPadding: 5,// Padding of label to add top/bottom, default below
						yPadding: 5,// Radius of label rectangle, default below
						cornerRadius: 10, // Anchor position of label on line, can be one of: top, bottom, left, right, center. Default below.
						position: "left",	// Adjustment along x-axis (left-right) of label relative to above number (can be negative)
											// For horizontal lines positioned left or right, negative values move the label toward the edge, and negative values toward the center.
						xAdjust: 290,			// Adjustment along y-axis (top-bottom) of label relative to above number (can be negative)
											// For vertical lines positioned top or bottom, negative values move the label toward the edge, and negative values toward the center.
						yAdjust: 0,			// Whether the label is enabled and should be displayed
							// Text to display in label - default is null
						content: "Max"
					},
					onClick: function(e) { // Fires when the user clicks this annotation on the chart (be sure to enable the event in the events array below).
					}
		}],
	},
	responsive: true,
	showLines: true,
	hoverMode: 'single', // should always use single for a scatter chart
	legend: {},
	scales: {
		yAxes: [{
			display: true,
			position: 'left',
			id: 'y-axis-0',
			ticks: {min: 0, 	//beginAtZero:true,
					max: 200,
					//display: true,
					//fontColor: "black"
			},
			scaleLabel: {display: true,	labelString: 'Number'},
			gridLines: {color: "black",
						//display: true,
						drawOnChartArea: false,
						zeroLineColor: "black",
						//drawTicks: true,
			},
		}],
		xAxes: [{
			display: true,
			type: 'linear',
			id: 'x-axis-0',
			position: 'bottom',
			ticks: {min: 0,
					max: 100,
					//display: true,
					//fontColor: "black",
			},
			scaleLabel: {display: true,	labelString: 'Volume'},
			gridLines: {color: "black",
						zeroLineColor: "black",
						drawOnChartArea: false,
			},
		}],
	},
}
var ctx_gamma = document.getElementById("chart-gamma").getContext("2d");

var humidChartData = {
	datasets: [
		{
		label: 'B errors',
		data: [{x: 5, y: 45}, {x: 10, y: 100}, {x: 25, y: 120}, {x: 50, y: 125}, {x: 100, y: 150}, {x: 120, y: 250},],
		borderColor: "green",
		errors: [15, 20, 30, 12, 10, 10],
		xErrors: [3, 7, 16, 12, 12, 30, 10],
		errStyle: "oval",
		showLine: false,
		errColor: "border",
		//pointBackgroundColor: "white",
		//pointBordercolor: "white",
		backgroundColor: "rgba(0,0,0,0)",
		hidden: true,
		errWidth: 2,
		showCap: true,
		capLen: 3,
		radius: 0,
		showErrors: true,
		},
		{
			label: 'B trend',
			data: [{x: 5, y: 45}, {x: 10, y: 100}, {x: 25, y: 120}, {x: 50, y: 125}, {x: 100, y: 150}, {x: 120, y: 250},],
			borderColor: "green",
			errors: [15, 20, 30, 12, 10, 10],
			xErrors: [3, 7, 16, 12, 12, 30, 10],
			pointStyle: "line",
			showErrors: false,
		radius: 0,
		},
		{
			label: 'B data',
			data: [{x: 5, y: 45}, {x: 10, y: 100}, {x: 25, y: 120}, {x: 50, y: 125}, {x: 100, y: 150}, {x: 120, y: 250},],
			borderColor: "green",
			backgroundColor: "green",
			errors: [15, 20, 30, 12, 10, 10],
			xErrors: [3, 7, 16, 12, 12, 30, 10],
			showErrors: false,
			showLine: false,
		},
		{
		label: '',
		data: [],
		borderColor: "rgba(0,0,0,0)",
		backgroundColor: "rgba(0,0,0,0)",
		},
		{
		label: 'C data',
		data: [{x: 15, y: 85, r: 14}, {x: 25, y: 37, r: 8}, {x: 62, y: 135, r: 44},],
		borderColor: "blue",
		backgroundColor: "rgba(0,0,0,0)",
        xErrors: [3, 7, 16, 12, 12, 30, 10],
		showLine: true,
		showErrors: true,
		},
	]

	}
var options_humid = {
	hoverMode: 'single',
	elements: {
		line: { fill: false,
				borderWidth: 2,
		},
		point: { radius: 3,
				pointStyle: 'circle',
				borderWidth: 1,
				hitRadius: 0,
				// hoverBorderWidth: 13,
				hoverRadius: 9,
		},
	},
	responsive: true,
	showLines: true,
	hoverMode: 'single', // should always use single for a scatter chart
	legend: {
		labels: {
			usePointStyle: true,
			// generateLabels: function() {	}
		}
	},
		scales: {
			yAxes: [{
				display: true,
				position: 'left',
				id: 'y-axis-0',
				ticks: {min: 0, 	//beginAtZero:true,
						max: 300 },
				scaleLabel: {display: true,	labelString: 'Number'},
				gridLines: {zeroLineColor: "black", },
			}],
			xAxes: [{
				display: true,
				type: 'linear',
				id: 'x-axis-0',
				position: 'bottom',
				ticks: {min: 0,
						max: 200 },
				scaleLabel: {display: true,	labelString: 'Month'},
				gridLines: {zeroLineColor: "black", },
			}],
		},
}
var ctx_humid = document.getElementById("chart-humid").getContext("2d");

window.onload = function() {
	var humidChart = new Chart(ctx_humid, {
		type: 'line',
		data: humidChartData,
		plugins: [errorbarPlugin],
		options: options_humid,
	});

	var gamma_chart = new Chart(ctx_gamma, {
		type: 'scatter',
		data: gammaChartData,
		plugins: [errorbarPlugin],
		options: options_gamma,
	});
};

</script>





</div>

</body>
</html>

答案 1 :(得分:0)

我在渲染文字时遇到类似的问题

pow()

要解决此问题,请在float sqr_sum = a.real * a.real + a.imaginary * a.imaginary; 循环内使用:

    plugins:[{
      afterDatasetsDraw: function(chart, options) {
        var ctx = chart.ctx;
        ctx.font = Chart.defaults.global.defaultFontStyle;
        ctx.fillStyle = "#666666";
        ctx.textAlign = 'center';
        ctx.textBaseline = 'bottom';
        chart.data.datasets.forEach(function(dataset, i) {
          var meta = chart.controller.getDatasetMeta(i);
          meta.data.forEach(function (bar, index) {
            ctx.fillText(Math.round(dataset.data[index]), bar._model.x, bar._model.y);
          });
        });
      }
    }]

(这是@Yobmod的文章,但似乎关键是要使用bar.tooltipPosition()位置作为所呈现内容的位置。)