'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; var globalDefaults = Chart.defaults.global; Chart.defaults.global.elements.line = { tension: 0.4, backgroundColor: globalDefaults.defaultColor, borderWidth: 3, borderColor: globalDefaults.defaultColor, borderCapStyle: 'butt', borderDash: [], borderDashOffset: 0.0, borderJoinStyle: 'miter', capBezierPoints: true, fill: true // do we fill in the area between the line and its base axis }; Chart.elements.Line = Chart.Element.extend({ draw: function() { var me = this; var vm = me._view; var spanGaps = vm.spanGaps; var scaleZero = vm.scaleZero; var loop = me._loop; var ctx = me._chart.ctx; ctx.save(); // Helper function to draw a line to a point function lineToPoint(previousPoint, point) { var pointVM = point._view; if (point._view.steppedLine === true) { ctx.lineTo(pointVM.x, previousPoint._view.y); ctx.lineTo(pointVM.x, pointVM.y); } else if (point._view.tension === 0) { ctx.lineTo(pointVM.x, pointVM.y); } else { ctx.bezierCurveTo( previousPoint._view.controlPointNextX, previousPoint._view.controlPointNextY, pointVM.controlPointPreviousX, pointVM.controlPointPreviousY, pointVM.x, pointVM.y ); } } var points = me._children.slice(); // clone array var lastDrawnIndex = -1; // If we are looping, adding the first point again if (loop && points.length) { points.push(points[0]); } var index, current, previous, currentVM; // Fill Line if (points.length && vm.fill) { ctx.beginPath(); for (index = 0; index < points.length; ++index) { current = points[index]; previous = helpers.previousItem(points, index); currentVM = current._view; // First point moves to it's starting position no matter what if (index === 0) { if (loop) { ctx.moveTo(scaleZero.x, scaleZero.y); } else { ctx.moveTo(currentVM.x, scaleZero); } if (!currentVM.skip) { lastDrawnIndex = index; ctx.lineTo(currentVM.x, currentVM.y); } } else { previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; if (currentVM.skip) { // Only do this if this is the first point that is skipped if (!spanGaps && lastDrawnIndex === (index - 1)) { if (loop) { ctx.lineTo(scaleZero.x, scaleZero.y); } else { ctx.lineTo(previous._view.x, scaleZero); } } } else { if (lastDrawnIndex !== (index - 1)) { // There was a gap and this is the first point after the gap. If we've never drawn a point, this is a special case. // If the first data point is NaN, then there is no real gap to skip if (spanGaps && lastDrawnIndex !== -1) { // We are spanning the gap, so simple draw a line to this point lineToPoint(previous, current); } else if (loop) { ctx.lineTo(currentVM.x, currentVM.y); } else { ctx.lineTo(currentVM.x, scaleZero); ctx.lineTo(currentVM.x, currentVM.y); } } else { // Line to next point lineToPoint(previous, current); } lastDrawnIndex = index; } } } if (!loop && lastDrawnIndex !== -1) { ctx.lineTo(points[lastDrawnIndex]._view.x, scaleZero); } ctx.fillStyle = vm.backgroundColor || globalDefaults.defaultColor; ctx.closePath(); ctx.fill(); } // Stroke Line Options var globalOptionLineElements = globalDefaults.elements.line; ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; // IE 9 and 10 do not support line dash if (ctx.setLineDash) { ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); } ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; // Stroke Line ctx.beginPath(); lastDrawnIndex = -1; for (index = 0; index < points.length; ++index) { current = points[index]; previous = helpers.previousItem(points, index); currentVM = current._view; // First point moves to it's starting position no matter what if (index === 0) { if (!currentVM.skip) { ctx.moveTo(currentVM.x, currentVM.y); lastDrawnIndex = index; } } else { previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; if (!currentVM.skip) { if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { // There was a gap and this is the first point after the gap ctx.moveTo(currentVM.x, currentVM.y); } else { // Line to next point lineToPoint(previous, current); } lastDrawnIndex = index; } } } ctx.stroke(); ctx.restore(); } }); };