When building time-series charts, how you format axis labels can make or break readability — especially when users zoom across seconds to years. CanvasJS provides excellent out-of-the-box support for time-based axis labels, but when working with sub-minute or sub-hour data (e.g., sensor readings, real-time dashboards), the default behavior might not always deliver the best readability. This guide shows how to refine axis labels for small time ranges (seconds, minutes) while retaining CanvasJS’s automatic interval calculations for larger ranges.
The Challenge
For small time windows (e.g., 30 seconds or 10 minutes), CanvasJS might:
- Omit seconds in labels, even when critical for precision.
- Choose intervals that create cluttered labels (e.g., 1-second intervals for a 30-second range).
- Fail to align label formats with user expectations (e.g., showing 14:00 instead of 14:00:00).
Solution Overview
We’ll override the valueFormatString property only for sub-hour ranges, while letting CanvasJS handle larger ranges automatically.
Key Improvements:
- Seconds precision for ranges < 1 minute.
- Clean intervals (e.g., 5-second or 1-minute increments).
- Dynamic transitions between formats (seconds → minutes → hours).
Strategy: Let CanvasJS Handle Long Ranges, You Handle Short Ranges
Zoom Level (Visible Range) | CanvasJS Auto Format OK? | Custom Format Suggested |
---|---|---|
≥ 1 month | ✅ Yes | ❌ No |
≤ 10 days | ⚠ Decent | ✅ MMM DD hh:mm |
≤ 1 day | ❌ No | ✅ hh:mm TT |
≤ 1 hour | ❌ No | ✅ hh:mm TT |
≤ 1 minute | ❌ No | ✅ hh:mm:ss TT |
≤ 10 seconds | ❌ No | ✅ hh:mm:ss TT |
Dynamic Axis Label Formatting for Short Ranges
function getFormatString(axis) {
var min = axis.viewportMinimum || axis.minimum;
var max = axis.viewportMaximum || axis.maximum;
var rangeMs = max - min;
var MINUTE = 60 * 1000;
var HOUR = 60 * MINUTE;
var DAY = 24 * HOUR;
var MONTH = 30 * DAY; // Approximation
var YEAR = 365 * DAY;
if (rangeMs < 1 * MINUTE)
return "HH:mm:ss"; // <1 minute: show seconds
if (rangeMs < 1 * HOUR)
return "HH:mm"; // 1min–1hr: minutes
if (rangeMs < 1 * DAY)
return "HH:mm"; // 1hr–1 day: hours
if (rangeMs < 10 * DAY)
return "MMM DD HH:mm"; // 1 day–10 days: day + month + hours
if (rangeMs < 3 * MONTH)
return "MMM DD"; // 10 days–3 months: day + month
if (rangeMs < 3 * YEAR)
return "MMM YYYY"; // 3 months–3 years: month + year
return "YYYY"; // >3 years: year only
}
Hook Into Chart Events
var chart = new CanvasJS.Chart("chartContainer", {
zoomEnabled: true,
axisX: {
valueFormatString: "MMM DD, YYYY" // Initial format, may be overridden
},
data: [{
type: "line",
xValueType: "dateTime",
dataPoints: generateDataPoints() // Generate your time-series data
}],
rangeChanged: function (e) {
var formatString = null;
if (e.trigger != "reset")
formatString = getFormatString(e.axisX[0]);
e.chart.axisX[0].set("valueFormatString", formatString);
}
});
chart.render();
// chart.axisX[0].set(“valueFormatString”,
// getFormatString(chart.axisX[0])); // Handle initial format, if not fixed
Why This Approach Works
- Saves time: No need to manage complex interval settings.
- Optimized for clarity: Users see what’s most relevant at each zoom level.
- Minimal code: Focuses only on improving what CanvasJS doesn’t handle well.
- Scalable: Works across dashboards, analytics apps, stock charts, and more.
By overriding intervals and formats only for sub-hour ranges, this approach balances customization with CanvasJS’s built-in intelligence. Developers gain fine-grained control over seconds/minutes without sacrificing the library’s robustness for larger timeframes. Implement this to build dashboards that shine at all zoom levels—from milliseconds to decades!