Skip to content

Commit

Permalink
Add support for lineDash property for accessibility (#1)
Browse files Browse the repository at this point in the history
Currently only supported for charts using a fixed y-axis and linear interpolation.

Variation in axis height and different methods of interpolation affect the lineDashOffset property which represents the length of the line that overflows the canvas produced from old data.

A new example has been added to demonstrate the feature.
  • Loading branch information
microbit-robert authored Nov 27, 2024
1 parent 96626af commit f70976f
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 7 deletions.
25 changes: 25 additions & 0 deletions examples/example-dashed-line.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="../smoothie.js"></script>
<script type="text/javascript">
// Randomly add a data point every 500ms
var random = new TimeSeries();
setInterval(function () {
random.append(Date.now(), Math.random() * 10000);
}, 500);

function createTimeline() {
var chart = new SmoothieChart({tooltipLine:{strokeStyle:'#bbbbbb'},minValue:-10000,maxValue:10000}),
canvas = document.getElementById('smoothie-chart'),
series = new TimeSeries();

chart.addTimeSeries(random, {lineWidth:2,strokeStyle:'#00ff00',interpolation:'linear', lineDash: [10, 5]});
chart.streamTo(canvas, 500);
}
</script>
</head>
<body onload="createTimeline()" style="background-color: #333333">
<canvas id="smoothie-chart" width="500" height="100">
</body>
</html>
10 changes: 10 additions & 0 deletions smoothie.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export interface ITimeSeriesOptions {

export interface ITimeSeriesPresentationOptions {
strokeStyle?: string;
/**
* Only supported for charts using fixed axes and linear interpolation.
*/
lineDash?: [number, number];
fillStyle?: string;
lineWidth?: number;
/**
Expand Down Expand Up @@ -53,6 +57,12 @@ export declare class TimeSeries {
maxValue: number;

/**
* Used to maintain a consistent lineDash if used.
*/
lineDashOffset: number;

/**
*
* Hide this <code>TimeSeries</code> object in the chart.
*/
disabled: boolean;
Expand Down
37 changes: 30 additions & 7 deletions smoothie.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@
return Math.floor(position) + 0.5;
}
},
getLineLength: function(x, y) {
return Math.sqrt(x * x + y * y);
},
};

/**
Expand Down Expand Up @@ -196,6 +199,7 @@
this.data = [];
this.maxValue = Number.NaN; // The maximum value ever seen in this TimeSeries.
this.minValue = Number.NaN; // The minimum value ever seen in this TimeSeries.
this.lineDashOffset = 0; // Used to maintain a consistent lineDash if used.
};

/**
Expand Down Expand Up @@ -280,14 +284,30 @@
this.minValue = isNaN(this.minValue) ? value : Math.min(this.minValue, value);
};

TimeSeries.prototype.dropOldData = function(oldestValidTime, maxDataSetLength) {
TimeSeries.prototype.dropOldData = function(oldestValidTime, maxDataSetLength, timeToXPosition, valueToYPosition, lineWidthMaybeZero, lineDash) {
// We must always keep one expired data point as we need this to draw the
// line that comes into the chart from the left, but any points prior to that can be removed.
var removeCount = 0;
while (this.data.length - removeCount >= maxDataSetLength && this.data[removeCount + 1][0] < oldestValidTime) {
removeCount++;
}
if (removeCount !== 0) {
if (lineDash) {
for (var i = 0; i < removeCount; i++) {
const pointOne = this.data[i];
const pointTwo = this.data[i + 1];
const xDiff = timeToXPosition(pointTwo[0], lineWidthMaybeZero) - timeToXPosition(pointOne[0], lineWidthMaybeZero);
const yDiff = valueToYPosition(pointTwo[1], lineWidthMaybeZero) - valueToYPosition(pointOne[1], lineWidthMaybeZero);
const removedLineLength = Util.getLineLength(xDiff, yDiff);
this.lineDashOffset = Number((removedLineLength + this.lineDashOffset).toFixed(2));

// Reset lineDashOffset to zero where possible.
const totalDashLength = lineDash[0] + lineDash[1];
if (this.lineDashOffset % totalDashLength === 0) {
this.lineDashOffset = 0;
}
}
}
this.data.splice(0, removeCount);
}
};
Expand Down Expand Up @@ -975,19 +995,18 @@
// For each data set...
for (var d = 0; d < this.seriesSet.length; d++) {
var timeSeries = this.seriesSet[d].timeSeries,
dataSet = timeSeries.data;
dataSet = timeSeries.data,
lineWidthMaybeZero = drawStroke ? seriesOptions.lineWidth : 0,
seriesOptions = this.seriesSet[d].options;

// Delete old data that's moved off the left of the chart.
timeSeries.dropOldData(oldestValidTime, chartOptions.maxDataSetLength);
timeSeries.dropOldData(oldestValidTime, chartOptions.maxDataSetLength, timeToXPosition, valueToYPosition, lineWidthMaybeZero, seriesOptions.lineDash);
if (dataSet.length <= 1 || timeSeries.disabled) {
continue;
}
context.save();

var seriesOptions = this.seriesSet[d].options,
// Keep in mind that `context.lineWidth = 0` doesn't actually set it to `0`.
drawStroke = seriesOptions.strokeStyle && seriesOptions.strokeStyle !== 'none',
lineWidthMaybeZero = drawStroke ? seriesOptions.lineWidth : 0;
var drawStroke = seriesOptions.strokeStyle && seriesOptions.strokeStyle !== 'none';

// Draw the line...
context.beginPath();
Expand Down Expand Up @@ -1050,6 +1069,10 @@
if (drawStroke) {
context.lineWidth = seriesOptions.lineWidth;
context.strokeStyle = seriesOptions.strokeStyle;
if (seriesOptions.lineDash) {
context.setLineDash(seriesOptions.lineDash);
context.lineDashOffset = timeSeries.lineDashOffset;
}
context.stroke();
}

Expand Down

0 comments on commit f70976f

Please sign in to comment.