From a6d7bc2e569c97af93cbb90ceb04b78e5b640bf2 Mon Sep 17 00:00:00 2001 From: WofWca Date: Tue, 7 Sep 2021 06:54:02 +0300 Subject: [PATCH 01/16] fix: a memory leak appearing when some `timeSeries.disabled === true` Introduced in 86574e --- smoothie.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/smoothie.js b/smoothie.js index ad40ed5..178cdd4 100644 --- a/smoothie.js +++ b/smoothie.js @@ -910,18 +910,18 @@ // For each data set... for (var d = 0; d < this.seriesSet.length; d++) { - context.save(); var timeSeries = this.seriesSet[d].timeSeries; + + // Delete old data that's moved off the left of the chart. + timeSeries.dropOldData(oldestValidTime, chartOptions.maxDataSetLength); if (timeSeries.disabled) { continue; } + context.save(); var dataSet = timeSeries.data, seriesOptions = this.seriesSet[d].options; - // Delete old data that's moved off the left of the chart. - timeSeries.dropOldData(oldestValidTime, chartOptions.maxDataSetLength); - // Set style for this dataSet. context.lineWidth = seriesOptions.lineWidth; context.strokeStyle = seriesOptions.strokeStyle; From 85cb245f7c0df6660f6218a4416f0f2fde454cbb Mon Sep 17 00:00:00 2001 From: WofWca Date: Tue, 7 Sep 2021 07:11:05 +0300 Subject: [PATCH 02/16] docs: update changelog --- smoothie.js | 1 + 1 file changed, 1 insertion(+) diff --git a/smoothie.js b/smoothie.js index 178cdd4..9486b87 100644 --- a/smoothie.js +++ b/smoothie.js @@ -95,6 +95,7 @@ * Add title option, by @mesca * Fix data drop stoppage by rejecting NaNs in append(), by @timdrysdale * Allow setting interpolation per time series, by @WofWca (#123) + * Fix a memory leak appearing when some `timeSeries.disabled === true`, by @WofWca (#132) */ ;(function(exports) { From f7d2f9e1006d05cfc0bb1ed1ca31c657409fac5e Mon Sep 17 00:00:00 2001 From: WofWca Date: Fri, 24 Sep 2021 20:34:03 +0800 Subject: [PATCH 03/16] perf: improve `render()` performance a bit No behavior changes --- smoothie.js | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/smoothie.js b/smoothie.js index 9486b87..538da3f 100644 --- a/smoothie.js +++ b/smoothie.js @@ -911,17 +911,17 @@ // For each data set... for (var d = 0; d < this.seriesSet.length; d++) { - var timeSeries = this.seriesSet[d].timeSeries; + var timeSeries = this.seriesSet[d].timeSeries, + dataSet = timeSeries.data; // Delete old data that's moved off the left of the chart. timeSeries.dropOldData(oldestValidTime, chartOptions.maxDataSetLength); - if (timeSeries.disabled) { + if (dataSet.length <= 1 || timeSeries.disabled) { continue; } context.save(); - var dataSet = timeSeries.data, - seriesOptions = this.seriesSet[d].options; + var seriesOptions = this.seriesSet[d].options; // Set style for this dataSet. context.lineWidth = seriesOptions.lineWidth; @@ -930,7 +930,7 @@ context.beginPath(); // Retain lastX, lastY for calculating the control points of bezier curves. var firstX = 0, firstY = 0, lastX = 0, lastY = 0; - for (var i = 0; i < dataSet.length && dataSet.length !== 1; i++) { + for (var i = 0; i < dataSet.length; i++) { var x = timeToXPixel(dataSet[i][0]), y = valueToYPixel(dataSet[i][1]); @@ -978,27 +978,26 @@ lastX = x; lastY = y; } - if (dataSet.length > 1) { - if (seriesOptions.fillStyle) { - // Close up the fill region. - if (chartOptions.scrollBackwards) { - context.lineTo(lastX, dimensions.height + seriesOptions.lineWidth); - context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth); - context.lineTo(firstX, firstY); - } else { - context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, lastY); - context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, dimensions.height + seriesOptions.lineWidth + 1); - context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth); - } - context.fillStyle = seriesOptions.fillStyle; - context.fill(); + if (seriesOptions.fillStyle) { + // Close up the fill region. + if (chartOptions.scrollBackwards) { + context.lineTo(lastX, dimensions.height + seriesOptions.lineWidth); + context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth); + context.lineTo(firstX, firstY); + } else { + context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, lastY); + context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, dimensions.height + seriesOptions.lineWidth + 1); + context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth); } + context.fillStyle = seriesOptions.fillStyle; + context.fill(); + } - if (seriesOptions.strokeStyle && seriesOptions.strokeStyle !== 'none') { - context.stroke(); - } - context.closePath(); + if (seriesOptions.strokeStyle && seriesOptions.strokeStyle !== 'none') { + context.stroke(); } + context.closePath(); + context.restore(); } From 73c387749148c77aa0f45e3cdc2a27fe4a16e76b Mon Sep 17 00:00:00 2001 From: WofWca Date: Fri, 24 Sep 2021 20:55:56 +0800 Subject: [PATCH 04/16] perf: improve `render()` performance a bit --- smoothie.js | 78 ++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/smoothie.js b/smoothie.js index 538da3f..f495b44 100644 --- a/smoothie.js +++ b/smoothie.js @@ -929,49 +929,47 @@ // Draw the line... context.beginPath(); // Retain lastX, lastY for calculating the control points of bezier curves. - var firstX = 0, firstY = 0, lastX = 0, lastY = 0; - for (var i = 0; i < dataSet.length; i++) { + var firstX = timeToXPixel(dataSet[0][0]), + firstY = valueToYPixel(dataSet[0][1]), + lastX = firstX, + lastY = firstY; + context.moveTo(firstX, firstY); + for (var i = 1; i < dataSet.length; i++) { var x = timeToXPixel(dataSet[i][0]), y = valueToYPixel(dataSet[i][1]); - if (i === 0) { - firstX = x; - firstY = y; - context.moveTo(x, y); - } else { - switch (seriesOptions.interpolation || chartOptions.interpolation) { - case "linear": - case "line": { - context.lineTo(x,y); - break; - } - case "bezier": - default: { - // Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves - // - // Assuming A was the last point in the line plotted and B is the new point, - // we draw a curve with control points P and Q as below. - // - // A---P - // | - // | - // | - // Q---B - // - // Importantly, A and P are at the same y coordinate, as are B and Q. This is - // so adjacent curves appear to flow as one. - // - context.bezierCurveTo( // startPoint (A) is implicit from last iteration of loop - Math.round((lastX + x) / 2), lastY, // controlPoint1 (P) - Math.round((lastX + x)) / 2, y, // controlPoint2 (Q) - x, y); // endPoint (B) - break; - } - case "step": { - context.lineTo(x,lastY); - context.lineTo(x,y); - break; - } + switch (seriesOptions.interpolation || chartOptions.interpolation) { + case "linear": + case "line": { + context.lineTo(x,y); + break; + } + case "bezier": + default: { + // Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves + // + // Assuming A was the last point in the line plotted and B is the new point, + // we draw a curve with control points P and Q as below. + // + // A---P + // | + // | + // | + // Q---B + // + // Importantly, A and P are at the same y coordinate, as are B and Q. This is + // so adjacent curves appear to flow as one. + // + context.bezierCurveTo( // startPoint (A) is implicit from last iteration of loop + Math.round((lastX + x) / 2), lastY, // controlPoint1 (P) + Math.round((lastX + x)) / 2, y, // controlPoint2 (Q) + x, y); // endPoint (B) + break; + } + case "step": { + context.lineTo(x,lastY); + context.lineTo(x,y); + break; } } From 9d606d8d8cde6546512673bb5edadf05dc60432a Mon Sep 17 00:00:00 2001 From: WofWca Date: Tue, 28 Sep 2021 11:50:25 +0800 Subject: [PATCH 05/16] perf: improve `.append()` performance a bit Reduce the amount of checks --- smoothie.js | 54 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/smoothie.js b/smoothie.js index f495b44..b61b4bd 100644 --- a/smoothie.js +++ b/smoothie.js @@ -215,30 +215,42 @@ if (isNaN(timestamp) || isNaN(value)){ return } - // Rewind until we hit an older timestamp - var i = this.data.length - 1; - while (i >= 0 && this.data[i][0] > timestamp) { - i--; - } - if (i === -1) { - // This new item is the oldest data - this.data.splice(0, 0, [timestamp, value]); - } else if (this.data.length > 0 && this.data[i][0] === timestamp) { - // Update existing values in the array - if (sumRepeatedTimeStampValues) { - // Sum this value into the existing 'bucket' - this.data[i][1] += value; - value = this.data[i][1]; - } else { - // Replace the previous value - this.data[i][1] = value; + var lastI = this.data.length - 1; + if (lastI >= 0) { + // Rewind until we find the place for the new data + var i = lastI; + while (true) { + var iThData = this.data[i]; + if (timestamp >= iThData[0]) { + if (timestamp === iThData[0]) { + // Update existing values in the array + if (sumRepeatedTimeStampValues) { + // Sum this value into the existing 'bucket' + iThData[1] += value; + value = iThData[1]; + } else { + // Replace the previous value + iThData[1] = value; + } + } else { + // Splice into the correct position to keep timestamps in order + this.data.splice(i + 1, 0, [timestamp, value]); + } + + break; + } + + i--; + if (i < 0) { + // This new item is the oldest data + this.data.splice(0, 0, [timestamp, value]); + + break; + } } - } else if (i < this.data.length - 1) { - // Splice into the correct position to keep timestamps in order - this.data.splice(i + 1, 0, [timestamp, value]); } else { - // Add to the end of the array + // It's the first element this.data.push([timestamp, value]); } From 6ae1d1c150b371cfa6924590da627787945dda0f Mon Sep 17 00:00:00 2001 From: WofWca Date: Wed, 6 Oct 2021 17:38:35 +0800 Subject: [PATCH 06/16] perf: improve `render()` performance a bit --- smoothie.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/smoothie.js b/smoothie.js index b61b4bd..05038ac 100644 --- a/smoothie.js +++ b/smoothie.js @@ -947,8 +947,9 @@ lastY = firstY; context.moveTo(firstX, firstY); for (var i = 1; i < dataSet.length; i++) { - var x = timeToXPixel(dataSet[i][0]), - y = valueToYPixel(dataSet[i][1]); + var iThData = dataSet[i], + x = timeToXPixel(iThData[0]), + y = valueToYPixel(iThData[1]); switch (seriesOptions.interpolation || chartOptions.interpolation) { case "linear": From 42b26b174378bfe36c95ac1e76d98cee62237bcb Mon Sep 17 00:00:00 2001 From: WofWca Date: Wed, 6 Oct 2021 21:10:12 +0800 Subject: [PATCH 07/16] perf: labels: use a variable for a commonly accessed property --- smoothie.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/smoothie.js b/smoothie.js index 05038ac..b5ba29f 100644 --- a/smoothie.js +++ b/smoothie.js @@ -1024,19 +1024,20 @@ } this.updateTooltip(); + var labelsOptions = chartOptions.labels; // Draw the axis values on the chart. - if (!chartOptions.labels.disabled && !isNaN(this.valueRange.min) && !isNaN(this.valueRange.max)) { - var maxValueString = chartOptions.yMaxFormatter(this.valueRange.max, chartOptions.labels.precision), - minValueString = chartOptions.yMinFormatter(this.valueRange.min, chartOptions.labels.precision), + if (!labelsOptions.disabled && !isNaN(this.valueRange.min) && !isNaN(this.valueRange.max)) { + var maxValueString = chartOptions.yMaxFormatter(this.valueRange.max, labelsOptions.precision), + minValueString = chartOptions.yMinFormatter(this.valueRange.min, labelsOptions.precision), maxLabelPos = chartOptions.scrollBackwards ? 0 : dimensions.width - context.measureText(maxValueString).width - 2, minLabelPos = chartOptions.scrollBackwards ? 0 : dimensions.width - context.measureText(minValueString).width - 2; - context.fillStyle = chartOptions.labels.fillStyle; - context.fillText(maxValueString, maxLabelPos, chartOptions.labels.fontSize); + context.fillStyle = labelsOptions.fillStyle; + context.fillText(maxValueString, maxLabelPos, labelsOptions.fontSize); context.fillText(minValueString, minLabelPos, dimensions.height - 2); } // Display intermediate y axis labels along y-axis to the left of the chart - if ( chartOptions.labels.showIntermediateLabels + if ( labelsOptions.showIntermediateLabels && !isNaN(this.valueRange.min) && !isNaN(this.valueRange.max) && chartOptions.grid.verticalSections > 0) { // show a label above every vertical section divider @@ -1047,10 +1048,10 @@ if (chartOptions.grid.sharpLines) { gy -= 0.5; } - var yValue = chartOptions.yIntermediateFormatter(this.valueRange.min + (v * step), chartOptions.labels.precision); + var yValue = chartOptions.yIntermediateFormatter(this.valueRange.min + (v * step), labelsOptions.precision); //left of right axis? intermediateLabelPos = - chartOptions.labels.intermediateLabelSameAxis + labelsOptions.intermediateLabelSameAxis ? (chartOptions.scrollBackwards ? 0 : dimensions.width - context.measureText(yValue).width - 2) : (chartOptions.scrollBackwards ? dimensions.width - context.measureText(yValue).width - 2 : 0); From 13bfabdaa02c97127f561e5f234ba2e6ac58b0ae Mon Sep 17 00:00:00 2001 From: WofWca Date: Wed, 6 Oct 2021 21:38:24 +0800 Subject: [PATCH 08/16] perf: improve `render` performance for `TimeSeries` with no stroke --- smoothie.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/smoothie.js b/smoothie.js index b5ba29f..d5a4903 100644 --- a/smoothie.js +++ b/smoothie.js @@ -935,9 +935,6 @@ var seriesOptions = this.seriesSet[d].options; - // Set style for this dataSet. - context.lineWidth = seriesOptions.lineWidth; - context.strokeStyle = seriesOptions.strokeStyle; // Draw the line... context.beginPath(); // Retain lastX, lastY for calculating the control points of bezier curves. @@ -1005,6 +1002,8 @@ } if (seriesOptions.strokeStyle && seriesOptions.strokeStyle !== 'none') { + context.lineWidth = seriesOptions.lineWidth; + context.strokeStyle = seriesOptions.strokeStyle; context.stroke(); } context.closePath(); From f7b488eb545cfb307968bf9fbe20cfd1eb031f18 Mon Sep 17 00:00:00 2001 From: WofWca Date: Wed, 6 Oct 2021 23:21:59 +0800 Subject: [PATCH 09/16] perf: remove unnecessary `context.save()`& `context.restore()` --- smoothie.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/smoothie.js b/smoothie.js index d5a4903..4a74007 100644 --- a/smoothie.js +++ b/smoothie.js @@ -844,10 +844,6 @@ context.font = chartOptions.labels.fontSize + 'px ' + chartOptions.labels.fontFamily; - // Save the state of the canvas context, any transformations applied in this method - // will get removed from the stack at the end of this method when .restore() is called. - context.save(); - // Move the origin. context.translate(dimensions.left, dimensions.top); @@ -859,14 +855,11 @@ context.clip(); // Clear the working area. - context.save(); context.fillStyle = chartOptions.grid.fillStyle; context.clearRect(0, 0, dimensions.width, dimensions.height); context.fillRect(0, 0, dimensions.width, dimensions.height); - context.restore(); // Grid lines... - context.save(); context.lineWidth = chartOptions.grid.lineWidth; context.strokeStyle = chartOptions.grid.strokeStyle; // Vertical (time) dividers. @@ -904,7 +897,6 @@ context.strokeRect(0, 0, dimensions.width, dimensions.height); context.closePath(); } - context.restore(); // Draw any horizontal lines... if (chartOptions.horizontalLines && chartOptions.horizontalLines.length) { @@ -931,7 +923,6 @@ if (dataSet.length <= 1 || timeSeries.disabled) { continue; } - context.save(); var seriesOptions = this.seriesSet[d].options; @@ -1007,8 +998,6 @@ context.stroke(); } context.closePath(); - - context.restore(); } if (chartOptions.tooltip && this.mouseX >= 0) { @@ -1106,8 +1095,6 @@ context.fillStyle = chartOptions.title.fillStyle; context.fillText(chartOptions.title.text, titleXPos, titleYPos); } - - context.restore(); // See .save() above. }; // Sample timestamp formatting function From cd380b69f6ad119ad36f985125092fbd5fb38974 Mon Sep 17 00:00:00 2001 From: WofWca Date: Thu, 7 Oct 2021 10:56:58 +0800 Subject: [PATCH 10/16] perf: don't access `canvas.clientWidth` on each `render()` This changes behavior a bit - when `render(` is passed a canvas different from `this.canvas` --- smoothie.js | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/smoothie.js b/smoothie.js index 4a74007..6c283c3 100644 --- a/smoothie.js +++ b/smoothie.js @@ -555,6 +555,10 @@ */ SmoothieChart.prototype.streamTo = function(canvas, delayMillis) { this.canvas = canvas; + + this.clientWidth = parseInt(this.canvas.getAttribute('width')); + this.clientHeight = parseInt(this.canvas.getAttribute('height')); + this.delay = delayMillis; this.start(); }; @@ -658,24 +662,33 @@ this.canvas.setAttribute('height', (Math.floor(height * dpr)).toString()); this.canvas.getContext('2d').scale(dpr, dpr); } - } else if (dpr !== 1) { - // Older behaviour: use the canvas's inner dimensions and scale the element's size - // according to that size and the device pixel ratio (eg: high DPI) + + this.clientWidth = width; + this.clientHeight = height; + } else { width = parseInt(this.canvas.getAttribute('width')); height = parseInt(this.canvas.getAttribute('height')); - if (!this.originalWidth || (Math.floor(this.originalWidth * dpr) !== width)) { - this.originalWidth = width; - this.canvas.setAttribute('width', (Math.floor(width * dpr)).toString()); - this.canvas.style.width = width + 'px'; - this.canvas.getContext('2d').scale(dpr, dpr); - } + if (dpr !== 1) { + // Older behaviour: use the canvas's inner dimensions and scale the element's size + // according to that size and the device pixel ratio (eg: high DPI) - if (!this.originalHeight || (Math.floor(this.originalHeight * dpr) !== height)) { - this.originalHeight = height; - this.canvas.setAttribute('height', (Math.floor(height * dpr)).toString()); - this.canvas.style.height = height + 'px'; - this.canvas.getContext('2d').scale(dpr, dpr); + if (Math.floor(this.clientWidth * dpr) !== width) { + this.canvas.setAttribute('width', (Math.floor(width * dpr)).toString()); + this.canvas.style.width = width + 'px'; + this.clientWidth = width; + this.canvas.getContext('2d').scale(dpr, dpr); + } + + if (Math.floor(this.clientHeight * dpr) !== height) { + this.canvas.setAttribute('height', (Math.floor(height * dpr)).toString()); + this.canvas.style.height = height + 'px'; + this.clientHeight = height; + this.canvas.getContext('2d').scale(dpr, dpr); + } + } else { + this.clientWidth = width; + this.clientHeight = height; } } }; @@ -824,7 +837,8 @@ var context = canvas.getContext('2d'), chartOptions = this.options, - dimensions = { top: 0, left: 0, width: canvas.clientWidth, height: canvas.clientHeight }, + // Using `this.clientWidth` instead of `canvas.clientWidth` because the latter is slow. + dimensions = { top: 0, left: 0, width: this.clientWidth, height: this.clientHeight }, // Calculate the threshold time for the oldest data points. oldestValidTime = time - (dimensions.width * chartOptions.millisPerPixel), valueToYPixel = function(value) { From 54647068a0d3482895e7936eb290b72d1ea2b6b2 Mon Sep 17 00:00:00 2001 From: WofWca Date: Wed, 6 Oct 2021 20:27:34 +0800 Subject: [PATCH 11/16] perf: improve `updateTooltip` performance --- smoothie.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smoothie.js b/smoothie.js index 6c283c3..9923479 100644 --- a/smoothie.js +++ b/smoothie.js @@ -592,7 +592,7 @@ // x pixel to time var t = this.options.scrollBackwards ? time - this.mouseX * this.options.millisPerPixel - : time - (this.canvas.offsetWidth - this.mouseX) * this.options.millisPerPixel; + : time - (this.clientWidth - this.mouseX) * this.options.millisPerPixel; var data = []; From 465976bbf6886ffe4ebbf68e3cceafd64e80501d Mon Sep 17 00:00:00 2001 From: WofWca Date: Thu, 7 Oct 2021 15:00:12 +0800 Subject: [PATCH 12/16] perf: render: don't check interpolation unnecessarily --- smoothie.js | 65 +++++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/smoothie.js b/smoothie.js index 9923479..728b00b 100644 --- a/smoothie.js +++ b/smoothie.js @@ -946,48 +946,55 @@ var firstX = timeToXPixel(dataSet[0][0]), firstY = valueToYPixel(dataSet[0][1]), lastX = firstX, - lastY = firstY; + lastY = firstY, + draw; context.moveTo(firstX, firstY); - for (var i = 1; i < dataSet.length; i++) { - var iThData = dataSet[i], - x = timeToXPixel(iThData[0]), - y = valueToYPixel(iThData[1]); - - switch (seriesOptions.interpolation || chartOptions.interpolation) { - case "linear": - case "line": { + switch (seriesOptions.interpolation || chartOptions.interpolation) { + case "linear": + case "line": { + draw = function(x, y, lastX, lastY) { context.lineTo(x,y); - break; } - case "bezier": - default: { - // Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves - // - // Assuming A was the last point in the line plotted and B is the new point, - // we draw a curve with control points P and Q as below. - // - // A---P - // | - // | - // | - // Q---B - // - // Importantly, A and P are at the same y coordinate, as are B and Q. This is - // so adjacent curves appear to flow as one. - // + break; + } + case "bezier": + default: { + // Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves + // + // Assuming A was the last point in the line plotted and B is the new point, + // we draw a curve with control points P and Q as below. + // + // A---P + // | + // | + // | + // Q---B + // + // Importantly, A and P are at the same y coordinate, as are B and Q. This is + // so adjacent curves appear to flow as one. + // + draw = function(x, y, lastX, lastY) { context.bezierCurveTo( // startPoint (A) is implicit from last iteration of loop Math.round((lastX + x) / 2), lastY, // controlPoint1 (P) Math.round((lastX + x)) / 2, y, // controlPoint2 (Q) x, y); // endPoint (B) - break; } - case "step": { + break; + } + case "step": { + draw = function(x, y, lastX, lastY) { context.lineTo(x,lastY); context.lineTo(x,y); - break; } + break; } + } + for (var i = 1; i < dataSet.length; i++) { + var iThData = dataSet[i], + x = timeToXPixel(iThData[0]), + y = valueToYPixel(iThData[1]); + draw(x, y, lastX, lastY); lastX = x; lastY = y; } From 6641ce51400091ccf178c780b08eed61b607b3d9 Mon Sep 17 00:00:00 2001 From: WofWca Date: Fri, 8 Oct 2021 15:31:00 +0800 Subject: [PATCH 13/16] perf: remove unnecessary `closePath` --- smoothie.js | 1 - 1 file changed, 1 deletion(-) diff --git a/smoothie.js b/smoothie.js index 728b00b..8ba2aca 100644 --- a/smoothie.js +++ b/smoothie.js @@ -1018,7 +1018,6 @@ context.strokeStyle = seriesOptions.strokeStyle; context.stroke(); } - context.closePath(); } if (chartOptions.tooltip && this.mouseX >= 0) { From 7f108bc3f3121bacf7e03c4a1efc07b7543464f5 Mon Sep 17 00:00:00 2001 From: WofWca Date: Fri, 8 Oct 2021 16:19:38 +0800 Subject: [PATCH 14/16] docs: update changelog --- smoothie.js | 1 + 1 file changed, 1 insertion(+) diff --git a/smoothie.js b/smoothie.js index 8ba2aca..82e4469 100644 --- a/smoothie.js +++ b/smoothie.js @@ -96,6 +96,7 @@ * Fix data drop stoppage by rejecting NaNs in append(), by @timdrysdale * Allow setting interpolation per time series, by @WofWca (#123) * Fix a memory leak appearing when some `timeSeries.disabled === true`, by @WofWca (#132) + * Improve performance, by @WofWca (#135) */ ;(function(exports) { From cedec28b58b093fe96fe15de833bbed6bf79fe9f Mon Sep 17 00:00:00 2001 From: WofWca Date: Fri, 8 Oct 2021 15:54:14 +0800 Subject: [PATCH 15/16] fix: series fill & stroke being inconsistent for last data time < render time It would depend on `scrollBackwards` and `fillStyle` being `undefined` or not. Depending on this, it would either continue the line up to the edge of the canvas or leave it at `lastX`. This makes it so that it is always left at `lastX`. --- smoothie.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/smoothie.js b/smoothie.js index 82e4469..54b179c 100644 --- a/smoothie.js +++ b/smoothie.js @@ -999,26 +999,20 @@ lastX = x; lastY = y; } - if (seriesOptions.fillStyle) { - // Close up the fill region. - if (chartOptions.scrollBackwards) { - context.lineTo(lastX, dimensions.height + seriesOptions.lineWidth); - context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth); - context.lineTo(firstX, firstY); - } else { - context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, lastY); - context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, dimensions.height + seriesOptions.lineWidth + 1); - context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth); - } - context.fillStyle = seriesOptions.fillStyle; - context.fill(); - } - if (seriesOptions.strokeStyle && seriesOptions.strokeStyle !== 'none') { context.lineWidth = seriesOptions.lineWidth; context.strokeStyle = seriesOptions.strokeStyle; context.stroke(); } + + if (seriesOptions.fillStyle) { + // Close up the fill region. + context.lineTo(lastX, dimensions.height + seriesOptions.lineWidth + 1); + context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth + 1); + + context.fillStyle = seriesOptions.fillStyle; + context.fill(); + } } if (chartOptions.tooltip && this.mouseX >= 0) { From fd47202d2910790b9e77c5ed41d21ededb45443c Mon Sep 17 00:00:00 2001 From: WofWca Date: Fri, 8 Oct 2021 16:17:49 +0800 Subject: [PATCH 16/16] docs: update changelog --- smoothie.js | 1 + 1 file changed, 1 insertion(+) diff --git a/smoothie.js b/smoothie.js index 54b179c..5c41ec2 100644 --- a/smoothie.js +++ b/smoothie.js @@ -97,6 +97,7 @@ * Allow setting interpolation per time series, by @WofWca (#123) * Fix a memory leak appearing when some `timeSeries.disabled === true`, by @WofWca (#132) * Improve performance, by @WofWca (#135) + * Fix series fill & stroke being inconsistent for last data time < render time, by @WofWca (#138) */ ;(function(exports) {