Merge pull request #1653 from nnnick/fix/1381

Fixes flickering during animations
This commit is contained in:
Tanner Linsley 2015-11-16 16:07:58 -07:00
commit ac098b8b2f
11 changed files with 1055 additions and 303 deletions

View File

@ -0,0 +1,175 @@
<!doctype html>
<html>
<head>
<title>Line Chart</title>
<script src="../Chart.js"></script>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<style>
canvas {
-webkit-box-shadow: 0 0 20px 0 rgba(0, 0, 0, .5);
}
</style>
</head>
<body>
<div style="width:100%;">
<canvas id="canvas" style="width:100%;height:100%"></canvas>
</div>
<br>
<br>
<button id="randomizeData">Randomize Data</button>
<button id="addDataset">Add Dataset</button>
<button id="removeDataset">Remove Dataset</button>
<button id="addData">Add Data</button>
<button id="removeData">Remove Data</button>
<div>
<h3>Legend</h3>
<div id="legendContainer">
</div>
</div>
<script>
var randomScalingFactor = function() {
return Math.round(Math.random() * 100);
//return 0;
};
var randomColorFactor = function() {
return Math.round(Math.random() * 255);
};
var randomColor = function(opacity) {
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
};
var config = {
type: 'line',
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
// Skip a point in the middle
data: [randomScalingFactor(), randomScalingFactor(), NaN, randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
fill: true,
borderDash: [5, 5],
}, {
label: "My Second dataset",
// Skip first and last points
data: [NaN, randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), NaN],
}]
},
options: {
responsive: true,
tooltips: {
mode: 'label',
},
hover: {
mode: 'label'
},
scales: {
xAxes: [{
display: true,
scaleLabel: {
show: true,
labelString: 'Month'
}
}],
yAxes: [{
display: true,
scaleLabel: {
show: true,
labelString: 'Value'
},
}]
}
}
};
$.each(config.data.datasets, function(i, dataset) {
dataset.borderColor = randomColor(0.4);
dataset.backgroundColor = randomColor(0.5);
dataset.pointBorderColor = randomColor(0.7);
dataset.pointBackgroundColor = randomColor(0.5);
dataset.pointBorderWidth = 1;
});
console.log(config.data);
window.onload = function() {
var ctx = document.getElementById("canvas").getContext("2d");
window.myLine = new Chart(ctx, config);
updateLegend();
};
function updateLegend() {
$legendContainer = $('#legendContainer');
$legendContainer.empty();
$legendContainer.append(window.myLine.generateLegend());
}
$('#randomizeData').click(function() {
$.each(config.data.datasets, function(i, dataset) {
dataset.data = dataset.data.map(function() {
return randomScalingFactor();
});
});
window.myLine.update();
updateLegend();
});
$('#addDataset').click(function() {
var newDataset = {
label: 'Dataset ' + config.data.datasets.length,
borderColor: randomColor(0.4),
backgroundColor: randomColor(0.5),
pointBorderColor: randomColor(0.7),
pointBackgroundColor: randomColor(0.5),
pointBorderWidth: 1,
data: [],
};
for (var index = 0; index < config.data.labels.length; ++index) {
newDataset.data.push(randomScalingFactor());
}
config.data.datasets.push(newDataset);
window.myLine.update();
updateLegend();
});
$('#addData').click(function() {
if (config.data.datasets.length > 0) {
config.data.labels.push('dataset #' + config.data.labels.length);
$.each(config.data.datasets, function(i, dataset) {
dataset.data.push(randomScalingFactor());
});
window.myLine.update();
updateLegend();
}
});
$('#removeDataset').click(function() {
config.data.datasets.splice(0, 1);
window.myLine.update();
updateLegend();
});
$('#removeData').click(function() {
config.data.labels.splice(-1, 1); // remove the label first
config.data.datasets.forEach(function(dataset, datasetIndex) {
dataset.data.pop();
});
window.myLine.update();
updateLegend();
});
</script>
</body>
</html>

View File

@ -0,0 +1,152 @@
<!doctype html>
<html>
<head>
<title>Radar Chart</title>
<script src="../Chart.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>
<body>
<div style="width:50%">
<canvas id="canvas" height="450" width="450"></canvas>
</div>
<button id="randomizeData">Randomize Data</button>
<button id="addDataset">Add Dataset</button>
<button id="removeDataset">Remove Dataset</button>
<button id="addData">Add Data</button>
<button id="removeData">Remove Data</button>
<div>
<h3>Legend</h3>
<div id="legendContainer">
</div>
</div>
<script>
var randomScalingFactor = function() {
return Math.round(Math.random() * 100);
};
var randomColorFactor = function() {
return Math.round(Math.random() * 255);
};
var randomColor = function(opacity) {
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
};
var config = {
type: 'radar',
data: {
labels: ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"],
datasets: [{
label: "Skip first dataset",
borderColor: 'rgb(255, 0, 0)',
backgroundColor: "rgba(255,255,0,0.5)",
pointBackgroundColor: "rgba(220,220,220,1)",
data: [NaN, randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
}, {
label: "Skip mid dataset",
borderColor: 'rgb(255, 0, 255)',
backgroundColor: "rgba(0, 255, 0, 0.5)",
pointBackgroundColor: "rgba(151,187,205,1)",
hoverPointBackgroundColor: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [randomScalingFactor(), randomScalingFactor(), NaN, randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
},{
label: "Skip last dataset",
borderColor: 'rgb(0, 255, 255)',
backgroundColor: "rgba(0, 0, 255, 0.5)",
pointBackgroundColor: "rgba(151,187,205,1)",
hoverPointBackgroundColor: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), NaN]
}]
},
options: {
elements: {
line: {
tension: 0.0,
}
},
scale: {
beginAtZero: true,
reverse: false
}
}
};
function updateLegend() {
$legendContainer = $('#legendContainer');
$legendContainer.empty();
$legendContainer.append(window.myRadar.generateLegend());
}
window.onload = function() {
window.myRadar = new Chart(document.getElementById("canvas"), config);
updateLegend();
};
$('#randomizeData').click(function() {
$.each(config.data.datasets, function(i, dataset) {
dataset.data = dataset.data.map(function() {
return randomScalingFactor();
});
});
window.myRadar.update();
updateLegend();
});
$('#addDataset').click(function() {
var newDataset = {
label: 'Dataset ' + config.data.datasets.length,
borderColor: randomColor(0.4),
backgroundColor: randomColor(0.5),
pointBorderColor: randomColor(0.7),
pointBackgroundColor: randomColor(0.5),
pointBorderWidth: 1,
data: [],
};
for (var index = 0; index < config.data.labels.length; ++index) {
newDataset.data.push(randomScalingFactor());
}
config.data.datasets.push(newDataset);
window.myRadar.update();
updateLegend();
});
$('#addData').click(function() {
if (config.data.datasets.length > 0) {
config.data.labels.push('dataset #' + config.data.labels.length);
$.each(config.data.datasets, function (i, dataset) {
dataset.data.push(randomScalingFactor());
});
window.myRadar.update();
updateLegend();
}
});
$('#removeDataset').click(function() {
config.data.datasets.splice(0, 1);
window.myRadar.update();
updateLegend();
});
$('#removeData').click(function() {
config.data.labels.pop(); // remove the label first
$.each(config.data.datasets, function(i, dataset) {
dataset.data.pop();
});
window.myRadar.update();
updateLegend();
});
</script>
</body>
</html>

View File

@ -41,7 +41,7 @@
label: "My First dataset",
backgroundColor: "rgba(220,220,220,0.2)",
pointBackgroundColor: "rgba(220,220,220,1)",
data: [null, randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
}, {
label: 'Hidden dataset',
hidden: true,
@ -52,7 +52,7 @@
pointBackgroundColor: "rgba(151,187,205,1)",
hoverPointBackgroundColor: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [null, randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
},]
},
options: {

View File

@ -251,28 +251,12 @@
point._model.tension
);
point._model.controlPointPreviousX = controlPoints.previous.x;
point._model.controlPointNextX = controlPoints.next.x;
// Prevent the bezier going outside of the bounds of the graph
point._model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, this.chart.chartArea.right), this.chart.chartArea.left);
point._model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
// Cap outer bezier handles to the upper/lower scale bounds
if (controlPoints.next.y > this.chart.chartArea.bottom) {
point._model.controlPointNextY = this.chart.chartArea.bottom;
} else if (controlPoints.next.y < this.chart.chartArea.top) {
point._model.controlPointNextY = this.chart.chartArea.top;
} else {
point._model.controlPointNextY = controlPoints.next.y;
}
// Cap inner bezier handles to the upper/lower scale bounds
if (controlPoints.previous.y > this.chart.chartArea.bottom) {
point._model.controlPointPreviousY = this.chart.chartArea.bottom;
} else if (controlPoints.previous.y < this.chart.chartArea.top) {
point._model.controlPointPreviousY = this.chart.chartArea.top;
} else {
point._model.controlPointPreviousY = controlPoints.previous.y;
}
point._model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, this.chart.chartArea.right), this.chart.chartArea.left);
point._model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
// Now pivot the point for animation
point.pivot();

View File

@ -197,28 +197,12 @@
point._model.tension
);
point._model.controlPointPreviousX = controlPoints.previous.x;
point._model.controlPointNextX = controlPoints.next.x;
// Prevent the bezier going outside of the bounds of the graph
point._model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, this.chart.chartArea.right), this.chart.chartArea.left);
point._model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
// Cap outer bezier handles to the upper/lower scale bounds
if (controlPoints.next.y > this.chart.chartArea.bottom) {
point._model.controlPointNextY = this.chart.chartArea.bottom;
} else if (controlPoints.next.y < this.chart.chartArea.top) {
point._model.controlPointNextY = this.chart.chartArea.top;
} else {
point._model.controlPointNextY = controlPoints.next.y;
}
// Cap inner bezier handles to the upper/lower scale bounds
if (controlPoints.previous.y > this.chart.chartArea.bottom) {
point._model.controlPointPreviousY = this.chart.chartArea.bottom;
} else if (controlPoints.previous.y < this.chart.chartArea.top) {
point._model.controlPointPreviousY = this.chart.chartArea.top;
} else {
point._model.controlPointPreviousY = controlPoints.previous.y;
}
point._model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, this.chart.chartArea.right), this.chart.chartArea.left);
point._model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
// Now pivot the point for animation
point.pivot();

View File

@ -38,7 +38,7 @@
// Init if doesn't exist
else if (!this._view[key]) {
if (typeof value === 'number') {
if (typeof value === 'number' && isNaN(this._view[key]) === false) {
this._view[key] = value * ease;
} else {
this._view[key] = value || null;
@ -61,14 +61,13 @@
}
// Number transitions
else if (typeof value === 'number') {
var startVal = this._start[key] !== undefined ? this._start[key] : 0;
var startVal = this._start[key] !== undefined && isNaN(this._start[key]) === false ? this._start[key] : 0;
this._view[key] = ((this._model[key] - startVal) * ease) + startVal;
}
// Everything else
else {
this._view[key] = value;
}
}, this);
if (ease === 1) {

View File

@ -321,27 +321,29 @@
aliasPixel = helpers.aliasPixel = function(pixelWidth) {
return (pixelWidth % 2 === 0) ? 0 : 0.5;
},
splineCurve = helpers.splineCurve = function(FirstPoint, MiddlePoint, AfterPoint, t) {
splineCurve = helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
//Props to Rob Spencer at scaled innovation for his post on splining between points
//http://scaledinnovation.com/analytics/splines/aboutSplines.html
// This function must also respect "skipped" points
var previous = FirstPoint,
current = MiddlePoint,
next = AfterPoint;
var previous = firstPoint.skip ? middlePoint : firstPoint,
current = middlePoint,
next = afterPoint.skip ? middlePoint : afterPoint;
if (previous.skip) {
previous = current;
}
if (next.skip) {
next = current;
}
var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
var s01 = d01 / (d01 + d12);
var s12 = d12 / (d01 + d12);
// If all points are the same, s01 & s02 will be inf
s01 = isNaN(s01) ? 0 : s01;
s12 = isNaN(s12) ? 0 : s12;
var fa = t * s01; // scaling factor for triangle Ta
var fb = t * s12;
var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)),
d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)),
fa = t * d01 / (d01 + d12), // scaling factor for triangle Ta
fb = t * d12 / (d01 + d12);
return {
previous: {
x: current.x - fa * (next.x - previous.x),

View File

@ -29,64 +29,38 @@
fill: true, // do we fill in the area between the line and its base axis
};
Chart.elements.Line = Chart.Element.extend({
lineToNextPoint: function(previousPoint, point, nextPoint, skipHandler, previousSkipHandler) {
var ctx = this._chart.ctx;
if (point._view.skip) {
skipHandler.call(this, previousPoint, point, nextPoint);
} else if (previousPoint._view.skip) {
previousSkipHandler.call(this, previousPoint, point, nextPoint);
} else {
// Line between points
ctx.bezierCurveTo(
previousPoint._view.controlPointNextX,
previousPoint._view.controlPointNextY,
point._view.controlPointPreviousX,
point._view.controlPointPreviousY,
point._view.x,
point._view.y
);
}
},
draw: function() {
var _this = this;
var vm = this._view;
var ctx = this._chart.ctx;
var first = this._children[0];
var last = this._children[this._children.length - 1];
ctx.save();
// Draw the background first (so the border is always on top)
helpers.each(this._children, function(point, index) {
var previous = helpers.previousItem(this._children, index);
var next = helpers.nextItem(this._children, index);
// First point moves to it's starting position no matter what
if (!index) {
ctx.moveTo(point._view.x, vm.scaleZero);
}
// Skip this point, draw to scaleZero, move to next point, and draw to next point
if (point._view.skip && !this.loop) {
ctx.lineTo(previous._view.x, vm.scaleZero);
ctx.moveTo(next._view.x, vm.scaleZero);
return;
}
// The previous line was skipped, so just draw a normal straight line to the point
if (previous._view.skip) {
ctx.lineTo(point._view.x, point._view.y);
return;
}
// Draw a bezier to point
if (vm.tension > 0 && index) {
//ctx.lineTo(point._view.x, point._view.y);
ctx.bezierCurveTo(
previous._view.controlPointNextX,
previous._view.controlPointNextY,
point._view.controlPointPreviousX,
point._view.controlPointPreviousY,
point._view.x,
point._view.y
);
return;
}
// Draw a straight line to the point
ctx.lineTo(point._view.x, point._view.y);
}, this);
// For radial scales, loop back around to the first point
if (this._loop) {
// Draw a bezier line
if (vm.tension > 0 && !first._view.skip) {
function loopBackToStart(drawLineToCenter) {
if (!first._view.skip && !last._view.skip) {
// Draw a bezier line from last to first
ctx.bezierCurveTo(
last._view.controlPointNextX,
last._view.controlPointNextY,
@ -95,23 +69,68 @@
first._view.x,
first._view.y
);
return;
} else if (drawLineToCenter) {
// Go to center
ctx.lineTo(_this._view.scaleZero.x, _this._view.scaleZero.y);
}
// Draw a straight line
ctx.lineTo(first._view.x, first._view.y);
}
ctx.save();
// If we had points and want to fill this line, do so.
if (this._children.length > 0 && vm.fill) {
//Round off the line by going to the base of the chart, back to the start, then fill.
ctx.lineTo(this._children[this._children.length - 1]._view.x, vm.scaleZero);
ctx.lineTo(this._children[0]._view.x, vm.scaleZero);
// Draw the background first (so the border is always on top)
ctx.beginPath();
helpers.each(this._children, function(point, index) {
var previous = helpers.previousItem(this._children, index);
var next = helpers.nextItem(this._children, index);
// First point moves to it's starting position no matter what
if (index === 0) {
if (this._loop) {
ctx.moveTo(vm.scaleZero.x, vm.scaleZero.y);
} else {
ctx.moveTo(point._view.x, vm.scaleZero);
}
if (point._view.skip) {
if (!this._loop) {
ctx.moveTo(next._view.x, this._view.scaleZero);
}
} else {
ctx.lineTo(point._view.x, point._view.y);
}
} else {
this.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) {
if (this._loop) {
// Go to center
ctx.lineTo(this._view.scaleZero.x, this._view.scaleZero.y);
} else {
ctx.lineTo(previousPoint._view.x, this._view.scaleZero);
ctx.moveTo(nextPoint._view.x, this._view.scaleZero);
}
}, function(previousPoint, point, nextPoint) {
// If we skipped the last point, draw a line to ourselves so that the fill is nice
ctx.lineTo(point._view.x, point._view.y);
});
}
}, this);
// For radial scales, loop back around to the first point
if (this._loop) {
loopBackToStart(true);
} else {
//Round off the line by going to the base of the chart, back to the start, then fill.
ctx.lineTo(this._children[this._children.length - 1]._view.x, vm.scaleZero);
ctx.lineTo(this._children[0]._view.x, vm.scaleZero);
}
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
ctx.closePath();
ctx.fill();
}
// Now draw the line between all the points with any borders
ctx.lineCap = vm.borderCapStyle || Chart.defaults.global.elements.line.borderCapStyle;
@ -130,62 +149,20 @@
var previous = helpers.previousItem(this._children, index);
var next = helpers.nextItem(this._children, index);
if (!index) {
ctx.moveTo(point._view.x, vm.scaleZero);
}
// Skip this point and move to the next points zeroPoint
if (point._view.skip && !this.loop) {
ctx.moveTo(next._view.x, vm.scaleZero);
return;
}
// Previous point was skipped, just move to the point
if (previous._view.skip) {
if (index === 0) {
ctx.moveTo(point._view.x, point._view.y);
return;
} else {
this.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) {
ctx.moveTo(nextPoint._view.x, nextPoint._view.y);
}, function(previousPoint, point, nextPoint) {
// If we skipped the last point, move up to our point preventing a line from being drawn
ctx.moveTo(point._view.x, point._view.y);
});
}
// If First point, move to the point ahead of time (so a line doesn't get drawn up the left axis)
if (!index) {
ctx.moveTo(point._view.x, point._view.y);
}
// Draw a bezier line to the point
if (vm.tension > 0 && index) {
ctx.bezierCurveTo(
previous._view.controlPointNextX,
previous._view.controlPointNextY,
point._view.controlPointPreviousX,
point._view.controlPointPreviousY,
point._view.x,
point._view.y
);
return;
}
// Draw a straight line to the point
ctx.lineTo(point._view.x, point._view.y);
}, this);
if (this._loop && !first._view.skip) {
// Draw a bezier line to the first point
if (vm.tension > 0) {
ctx.bezierCurveTo(
last._view.controlPointNextX,
last._view.controlPointNextY,
first._view.controlPointPreviousX,
first._view.controlPointPreviousY,
first._view.x,
first._view.y
);
return;
}
// Draw a straight line to the first point
ctx.lineTo(first._view.x, first._view.y);
if (this._loop) {
loopBackToStart();
}
ctx.stroke();

View File

@ -272,7 +272,10 @@
return index * angleMultiplier - (Math.PI / 2);
},
getDistanceFromCenterForValue: function(value) {
if (value === null) return 0; // null always in center
if (value === null) {
return 0; // null always in center
}
// Take into account half font size + the yPadding of the top value
var scalingFactor = this.drawingArea / (this.max - this.min);
if (this.options.reverse) {

View File

@ -192,7 +192,7 @@ describe('Line controller tests', function() {
chartArea: {
bottom: 200,
left: xScale.left,
right: 200,
right: xScale.left + 200,
top: 0
},
data: data,

View File

@ -22,6 +22,8 @@ describe('Line element tests', function() {
_view: {
x: 0,
y: 10,
controlPointNextX: 0,
controlPointNextY: 10
}
}));
points.push(new Chart.elements.Point({
@ -30,6 +32,10 @@ describe('Line element tests', function() {
_view: {
x: 5,
y: 0,
controlPointPreviousX: 5,
controlPointPreviousY: 0,
controlPointNextX: 5,
controlPointNextY: 0
}
}));
points.push(new Chart.elements.Point({
@ -38,6 +44,10 @@ describe('Line element tests', function() {
_view: {
x: 15,
y: -10,
controlPointPreviousX: 15,
controlPointPreviousY: -10,
controlPointNextX: 15,
controlPointNextY: -10
}
}));
points.push(new Chart.elements.Point({
@ -46,6 +56,10 @@ describe('Line element tests', function() {
_view: {
x: 19,
y: -5,
controlPointPreviousX: 19,
controlPointPreviousY: -5,
controlPointNextX: 19,
controlPointNextY: -5
}
}));
@ -68,21 +82,6 @@ describe('Line element tests', function() {
expect(mockContext.getCalls()).toEqual([{
name: 'save',
args: [],
}, {
name: 'moveTo',
args: [0, 0]
}, {
name: 'lineTo',
args: [0, 10]
}, {
name: 'lineTo',
args: [5, 0]
}, {
name: 'lineTo',
args: [15, -10]
}, {
name: 'lineTo',
args: [19, -5]
}, {
name: 'setLineCap',
args: ['butt']
@ -106,24 +105,18 @@ describe('Line element tests', function() {
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [0, 0]
}, {
name: 'moveTo',
args: [0, 10]
}, {
name: 'lineTo',
args: [0, 10]
name: 'bezierCurveTo',
args: [0, 10, 5, 0, 5, 0]
}, {
name: 'lineTo',
args: [5, 0]
name: 'bezierCurveTo',
args: [5, 0, 15, -10, 15, -10]
}, {
name: 'lineTo',
args: [15, -10]
}, {
name: 'lineTo',
args: [19, -5]
name: 'bezierCurveTo',
args: [15, -10, 19, -5, 19, -5]
}, {
name: 'stroke',
args: [],
@ -143,7 +136,9 @@ describe('Line element tests', function() {
_index: 0,
_view: {
x: 0,
y: 10
y: 10,
controlPointNextX: 0,
controlPointNextY: 10
}
}));
points.push(new Chart.elements.Point({
@ -151,7 +146,11 @@ describe('Line element tests', function() {
_index: 1,
_view: {
x: 5,
y: 0
y: 0,
controlPointPreviousX: 5,
controlPointPreviousY: 0,
controlPointNextX: 5,
controlPointNextY: 0
}
}));
points.push(new Chart.elements.Point({
@ -159,7 +158,11 @@ describe('Line element tests', function() {
_index: 2,
_view: {
x: 15,
y: -10
y: -10,
controlPointPreviousX: 15,
controlPointPreviousY: -10,
controlPointNextX: 15,
controlPointNextY: -10
}
}));
points.push(new Chart.elements.Point({
@ -167,7 +170,11 @@ describe('Line element tests', function() {
_index: 3,
_view: {
x: 19,
y: -5
y: -5,
controlPointPreviousX: 19,
controlPointPreviousY: -5,
controlPointNextX: 19,
controlPointNextY: -5
}
}));
@ -198,6 +205,9 @@ describe('Line element tests', function() {
var expected = [{
name: 'save',
args: [],
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [0, 2]
@ -205,14 +215,14 @@ describe('Line element tests', function() {
name: 'lineTo',
args: [0, 10]
}, {
name: 'lineTo',
args: [5, 0]
name: 'bezierCurveTo',
args: [0, 10, 5, 0, 5, 0]
}, {
name: 'lineTo',
args: [15, -10]
name: 'bezierCurveTo',
args: [5, 0, 15, -10, 15, -10]
}, {
name: 'lineTo',
args: [19, -5]
name: 'bezierCurveTo',
args: [15, -10, 19, -5, 19, -5]
}, {
name: 'lineTo',
args: [19, 2]
@ -251,24 +261,171 @@ describe('Line element tests', function() {
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [0, 10]
}, {
name: 'bezierCurveTo',
args: [0, 10, 5, 0, 5, 0]
}, {
name: 'bezierCurveTo',
args: [5, 0, 15, -10, 15, -10]
}, {
name: 'bezierCurveTo',
args: [15, -10, 19, -5, 19, -5]
}, {
name: 'stroke',
args: [],
}, {
name: 'restore',
args: []
}];
expect(mockContext.getCalls()).toEqual(expected);
});
it ('should skip points correctly', function() {
var mockContext = window.createMockContext();
// Create our points
var points = [];
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 0,
_view: {
x: 0,
y: 10,
controlPointNextX: 0,
controlPointNextY: 10
}
}));
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 1,
_view: {
x: 5,
y: 0,
controlPointPreviousX: 5,
controlPointPreviousY: 0,
controlPointNextX: 5,
controlPointNextY: 0
}
}));
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 2,
_view: {
x: 15,
y: -10,
controlPointPreviousX: 15,
controlPointPreviousY: -10,
controlPointNextX: 15,
controlPointNextY: -10,
skip: true
}
}));
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 3,
_view: {
x: 19,
y: -5,
controlPointPreviousX: 19,
controlPointPreviousY: -5,
controlPointNextX: 19,
controlPointNextY: -5
}
}));
var line = new Chart.elements.Line({
_datasetindex: 2,
_chart: {
ctx: mockContext,
},
_children: points,
// Need to provide some settings
_view: {
fill: true,
scaleZero: 2, // for filling lines
tension: 0.0, // no bezier curve for now
}
});
line.draw();
var expected = [{
name: 'save',
args: []
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [0, 2]
}, {
name: 'lineTo',
args: [0, 10]
}, {
name: 'bezierCurveTo',
args: [0, 10, 5, 0, 5, 0]
}, {
name: 'lineTo',
args: [5, 2]
}, {
name: 'moveTo',
args: [19, 2]
}, {
name: 'lineTo',
args: [19, -5]
}, {
name: 'lineTo',
args: [19, 2]
}, {
name: 'lineTo',
args: [0, 2]
}, {
name: 'setFillStyle',
args: ['rgba(0,0,0,0.1)']
}, {
name: 'closePath',
args: []
}, {
name: 'fill',
args: []
}, {
name: 'setLineCap',
args: ['butt']
}, {
name: 'setLineDash',
args: [
[]
]
}, {
name: 'setLineDashOffset',
args: [0.0]
}, {
name: 'setLineJoin',
args: ['miter']
}, {
name: 'setLineWidth',
args: [3]
}, {
name: 'setStrokeStyle',
args: ['rgba(0,0,0,0.1)']
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [0, 10]
}, {
name: 'lineTo',
args: [0, 10]
name: 'bezierCurveTo',
args: [0, 10, 5, 0, 5, 0]
}, {
name: 'lineTo',
args: [5, 0]
name: 'moveTo',
args: [19, -5]
}, {
name: 'lineTo',
args: [15, -10]
}, {
name: 'lineTo',
args: [19, -5]
name: 'moveTo',
args: [19, -5]
}, {
name: 'stroke',
args: [],
@ -289,7 +446,11 @@ describe('Line element tests', function() {
_index: 0,
_view: {
x: 0,
y: 10
y: 10,
controlPointPreviousX: 0,
controlPointPreviousY: 10,
controlPointNextX: 0,
controlPointNextY: 10
}
}));
points.push(new Chart.elements.Point({
@ -297,7 +458,11 @@ describe('Line element tests', function() {
_index: 1,
_view: {
x: 5,
y: 0
y: 0,
controlPointPreviousX: 5,
controlPointPreviousY: 0,
controlPointNextX: 5,
controlPointNextY: 0
}
}));
points.push(new Chart.elements.Point({
@ -305,7 +470,11 @@ describe('Line element tests', function() {
_index: 2,
_view: {
x: 15,
y: -10
y: -10,
controlPointPreviousX: 15,
controlPointPreviousY: -10,
controlPointNextX: 15,
controlPointNextY: -10
}
}));
points.push(new Chart.elements.Point({
@ -313,7 +482,11 @@ describe('Line element tests', function() {
_index: 3,
_view: {
x: 19,
y: -5
y: -5,
controlPointPreviousX: 19,
controlPointPreviousY: -5,
controlPointNextX: 19,
controlPointNextY: -5
}
}));
@ -326,9 +499,12 @@ describe('Line element tests', function() {
_loop: true, // want the line to loop back to the first point
// Need to provide some settings
_view: {
fill: false, // don't want to fill
fill: true, // don't want to fill
tension: 0.0, // no bezier curve for now
scaleZero: 0,
scaleZero: {
x: 3,
y: 2
},
}
});
@ -337,24 +513,36 @@ describe('Line element tests', function() {
expect(mockContext.getCalls()).toEqual([{
name: 'save',
args: [],
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [0, 0]
args: [3, 2]
}, {
name: 'lineTo',
args: [0, 10]
}, {
name: 'lineTo',
args: [5, 0]
name: 'bezierCurveTo',
args: [0, 10, 5, 0, 5, 0]
}, {
name: 'lineTo',
args: [15, -10]
name: 'bezierCurveTo',
args: [5, 0, 15, -10, 15, -10]
}, {
name: 'lineTo',
args: [19, -5]
name: 'bezierCurveTo',
args: [15, -10, 19, -5, 19, -5]
}, {
name: 'lineTo',
args: [0, 10]
name: 'bezierCurveTo',
args: [19, -5, 0, 10, 0, 10]
}, {
name: 'setFillStyle',
args: ['rgba(0,0,0,0.1)']
}, {
name: 'closePath',
args: []
}, {
name: 'fill',
args: []
}, {
name: 'setLineCap',
args: ['butt']
@ -378,27 +566,21 @@ describe('Line element tests', function() {
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [0, 0]
}, {
name: 'moveTo',
args: [0, 10]
}, {
name: 'lineTo',
args: [0, 10]
name: 'bezierCurveTo',
args: [0, 10, 5, 0, 5, 0]
}, {
name: 'lineTo',
args: [5, 0]
name: 'bezierCurveTo',
args: [5, 0, 15, -10, 15, -10]
}, {
name: 'lineTo',
args: [15, -10]
name: 'bezierCurveTo',
args: [15, -10, 19, -5, 19, -5]
}, {
name: 'lineTo',
args: [19, -5]
}, {
name: 'lineTo',
args: [0, 10]
name: 'bezierCurveTo',
args: [19, -5, 0, 10, 0, 10]
}, {
name: 'stroke',
args: [],
@ -408,7 +590,7 @@ describe('Line element tests', function() {
}]);
});
it('should draw with bezier curves if tension > 0', function() {
it('should be able to draw with a loop back to the beginning point when there is a skip in the middle of the dataset', function() {
var mockContext = window.createMockContext();
// Create our points
@ -419,10 +601,10 @@ describe('Line element tests', function() {
_view: {
x: 0,
y: 10,
controlPointNextX: 1,
controlPointNextY: 1,
controlPointPreviousX: 0,
controlPointPreviousY: 0,
controlPointPreviousY: 10,
controlPointNextX: 0,
controlPointNextY: 10
}
}));
points.push(new Chart.elements.Point({
@ -431,10 +613,11 @@ describe('Line element tests', function() {
_view: {
x: 5,
y: 0,
controlPointNextX: 6,
controlPointNextY: 7,
controlPointPreviousX: 4,
controlPointPreviousY: 3,
controlPointPreviousX: 5,
controlPointPreviousY: 0,
controlPointNextX: 5,
controlPointNextY: 0,
skip: true
}
}));
points.push(new Chart.elements.Point({
@ -443,10 +626,10 @@ describe('Line element tests', function() {
_view: {
x: 15,
y: -10,
controlPointNextX: 16,
controlPointNextY: 17,
controlPointPreviousX: 14,
controlPointPreviousY: 13,
controlPointPreviousX: 15,
controlPointPreviousY: -10,
controlPointNextX: 15,
controlPointNextY: -10
}
}));
points.push(new Chart.elements.Point({
@ -455,10 +638,10 @@ describe('Line element tests', function() {
_view: {
x: 19,
y: -5,
controlPointNextX: 20,
controlPointNextY: 21,
controlPointPreviousX: 18,
controlPointPreviousY: 17,
controlPointPreviousX: 19,
controlPointPreviousY: -5,
controlPointNextX: 19,
controlPointNextY: -5
}
}));
@ -468,51 +651,47 @@ describe('Line element tests', function() {
ctx: mockContext,
},
_children: points,
_loop: true, // want the line to loop back to the first point
// Need to provide some settings
_view: {
fill: true,
scaleZero: 2, // for filling lines
tension: 0.5, // have bezier curves
borderCapStyle: 'round',
borderColor: 'rgb(255, 255, 0)',
borderDash: [2, 2],
borderDashOffset: 1.5,
borderJoinStyle: 'bevel',
borderWidth: 4,
backgroundColor: 'rgb(0, 0, 0)'
fill: true, // don't want to fill
tension: 0.0, // no bezier curve for now
scaleZero: {
x: 3,
y: 2
},
}
});
line.draw();
var expected = [{
expect(mockContext.getCalls()).toEqual([{
name: 'save',
args: [],
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [0, 2]
args: [3, 2]
}, {
name: 'lineTo',
args: [0, 10]
}, {
name: 'bezierCurveTo',
args: [1, 1, 4, 3, 5, 0]
}, {
name: 'bezierCurveTo',
args: [6, 7, 14, 13, 15, -10]
}, {
name: 'bezierCurveTo',
args: [16, 17, 18, 17, 19, -5]
name: 'lineTo',
args: [3, 2]
}, {
name: 'lineTo',
args: [19, 2]
args: [15, -10]
}, {
name: 'lineTo',
args: [0, 2]
name: 'bezierCurveTo',
args: [15, -10, 19, -5, 19, -5]
}, {
name: 'bezierCurveTo',
args: [19, -5, 0, 10, 0, 10]
}, {
name: 'setFillStyle',
args: ['rgb(0, 0, 0)']
args: ['rgba(0,0,0,0.1)']
}, {
name: 'closePath',
args: []
@ -521,52 +700,349 @@ describe('Line element tests', function() {
args: []
}, {
name: 'setLineCap',
args: ['round']
args: ['butt']
}, {
name: 'setLineDash',
args: [
[2, 2]
[]
]
}, {
name: 'setLineDashOffset',
args: [1.5]
args: [0.0]
}, {
name: 'setLineJoin',
args: ['bevel']
args: ['miter']
}, {
name: 'setLineWidth',
args: [4]
args: [3]
}, {
name: 'setStrokeStyle',
args: ['rgb(255, 255, 0)']
args: ['rgba(0,0,0,0.1)']
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [0, 2]
args: [0, 10]
}, {
name: 'moveTo',
args: [0, 10]
args: [15, -10]
}, {
name: 'lineTo',
args: [0, 10]
name: 'moveTo',
args: [15, -10]
}, {
name: 'bezierCurveTo',
args: [1, 1, 4, 3, 5, 0]
args: [15, -10, 19, -5, 19, -5]
}, {
name: 'bezierCurveTo',
args: [6, 7, 14, 13, 15, -10]
}, {
name: 'bezierCurveTo',
args: [16, 17, 18, 17, 19, -5]
args: [19, -5, 0, 10, 0, 10]
}, {
name: 'stroke',
args: [],
}, {
name: 'restore',
args: []
}];
expect(mockContext.getCalls()).toEqual(expected);
}]);
});
it('should be able to draw with a loop back to the beginning point when the first point is skipped', function() {
var mockContext = window.createMockContext();
// Create our points
var points = [];
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 0,
_view: {
x: 0,
y: 10,
controlPointPreviousX: 0,
controlPointPreviousY: 10,
controlPointNextX: 0,
controlPointNextY: 10,
skip: true
}
}));
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 1,
_view: {
x: 5,
y: 0,
controlPointPreviousX: 5,
controlPointPreviousY: 0,
controlPointNextX: 5,
controlPointNextY: 0,
}
}));
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 2,
_view: {
x: 15,
y: -10,
controlPointPreviousX: 15,
controlPointPreviousY: -10,
controlPointNextX: 15,
controlPointNextY: -10
}
}));
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 3,
_view: {
x: 19,
y: -5,
controlPointPreviousX: 19,
controlPointPreviousY: -5,
controlPointNextX: 19,
controlPointNextY: -5
}
}));
var line = new Chart.elements.Line({
_datasetindex: 2,
_chart: {
ctx: mockContext,
},
_children: points,
_loop: true, // want the line to loop back to the first point
// Need to provide some settings
_view: {
fill: true, // don't want to fill
tension: 0.0, // no bezier curve for now
scaleZero: {
x: 3,
y: 2
},
}
});
line.draw();
expect(mockContext.getCalls()).toEqual([{
name: 'save',
args: [],
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [3, 2]
}, {
name: 'lineTo',
args: [5, 0]
}, {
name: 'bezierCurveTo',
args: [5, 0, 15, -10, 15, -10]
}, {
name: 'bezierCurveTo',
args: [15, -10, 19, -5, 19, -5]
}, {
name: 'lineTo',
args: [3, 2]
}, {
name: 'setFillStyle',
args: ['rgba(0,0,0,0.1)']
}, {
name: 'closePath',
args: []
}, {
name: 'fill',
args: []
}, {
name: 'setLineCap',
args: ['butt']
}, {
name: 'setLineDash',
args: [
[]
]
}, {
name: 'setLineDashOffset',
args: [0.0]
}, {
name: 'setLineJoin',
args: ['miter']
}, {
name: 'setLineWidth',
args: [3]
}, {
name: 'setStrokeStyle',
args: ['rgba(0,0,0,0.1)']
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [0, 10]
}, {
name: 'moveTo',
args: [5, 0]
}, {
name: 'bezierCurveTo',
args: [5, 0, 15, -10, 15, -10]
}, {
name: 'bezierCurveTo',
args: [15, -10, 19, -5, 19, -5]
}, {
name: 'stroke',
args: [],
}, {
name: 'restore',
args: []
}]);
});
it('should be able to draw with a loop back to the beginning point when the last point is skipped', function() {
var mockContext = window.createMockContext();
// Create our points
var points = [];
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 0,
_view: {
x: 0,
y: 10,
controlPointPreviousX: 0,
controlPointPreviousY: 10,
controlPointNextX: 0,
controlPointNextY: 10
}
}));
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 1,
_view: {
x: 5,
y: 0,
controlPointPreviousX: 5,
controlPointPreviousY: 0,
controlPointNextX: 5,
controlPointNextY: 0,
}
}));
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 2,
_view: {
x: 15,
y: -10,
controlPointPreviousX: 15,
controlPointPreviousY: -10,
controlPointNextX: 15,
controlPointNextY: -10
}
}));
points.push(new Chart.elements.Point({
_datasetindex: 2,
_index: 3,
_view: {
x: 19,
y: -5,
controlPointPreviousX: 19,
controlPointPreviousY: -5,
controlPointNextX: 19,
controlPointNextY: -5,
skip: true
}
}));
var line = new Chart.elements.Line({
_datasetindex: 2,
_chart: {
ctx: mockContext,
},
_children: points,
_loop: true, // want the line to loop back to the first point
// Need to provide some settings
_view: {
fill: true, // don't want to fill
tension: 0.0, // no bezier curve for now
scaleZero: {
x: 3,
y: 2
},
}
});
line.draw();
expect(mockContext.getCalls()).toEqual([{
name: 'save',
args: [],
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [3, 2]
}, {
name: 'lineTo',
args: [0, 10]
}, {
name: 'bezierCurveTo',
args: [0, 10, 5, 0, 5, 0]
}, {
name: 'bezierCurveTo',
args: [5, 0, 15, -10, 15, -10]
}, {
name: 'lineTo',
args: [3, 2]
}, {
name: 'lineTo',
args: [3, 2]
}, {
name: 'setFillStyle',
args: ['rgba(0,0,0,0.1)']
}, {
name: 'closePath',
args: []
}, {
name: 'fill',
args: []
}, {
name: 'setLineCap',
args: ['butt']
}, {
name: 'setLineDash',
args: [
[]
]
}, {
name: 'setLineDashOffset',
args: [0.0]
}, {
name: 'setLineJoin',
args: ['miter']
}, {
name: 'setLineWidth',
args: [3]
}, {
name: 'setStrokeStyle',
args: ['rgba(0,0,0,0.1)']
}, {
name: 'beginPath',
args: []
}, {
name: 'moveTo',
args: [0, 10]
}, {
name: 'bezierCurveTo',
args: [0, 10, 5, 0, 5, 0]
}, {
name: 'bezierCurveTo',
args: [5, 0, 15, -10, 15, -10]
}, {
name: 'moveTo',
args: [19, -5]
}, {
name: 'stroke',
args: [],
}, {
name: 'restore',
args: []
}]);
});
});