Advanced Chart (linear, bar, combination …)

Description

This interactive SVG chart visualizes time series data as a line chart, bar chart, or both.
It features:

  • Chart type switching (Line, Bar, Both)
  • Polynomial trend line (degree selectable)
  • Customizable decimal places
  • Adjustable chart offsets (left, right, y)
  • Show/hide data point circles and adjust their size
  • Custom date formats for X-axis labels
  • Dynamic X-axis label spacing
  • Tooltip on hover
  • Min/Max/Avg stats with corresponding dates
  • Responsive design
  • Manual and auto update options

Download

Source code + demo

Demo

Demo of code


Description of Function Variables

Variable / OptionTypeDescription
containerElementThe DOM element where the chart and controls will be rendered.
dataArrayThe main data array, typically an array of [timestamp, value] pairs.
NNumberNumber of data points (often data.length).
nowNumberThe base timestamp (e.g., Date.now()).
chartTypeStringType of chart to display: "line""bar", or "both".
leftOffsetNumberSpace (in px) between the left edge and the chart area.
rightOffsetNumberSpace (in px) between the right edge and the chart area.
yOffsetNumberVertical offset (in px) for the chart area.
polyDegreeNumberDegree of the polynomial for the trend line (1=linear, 2=quadratic, etc.).
decimalsNumberNumber of decimal places to show for values and axis labels.
showCirclesBooleanWhether to display circles at data points.
circleSizeNumberRadius of the data point circles.
dateFormatModeStringFormat for X-axis labels: "datetime""date""month""year", etc.
headerLabelStringTitle or label for the chart (e.g., “Living Room”).
unitsStringUnits for the Y-axis and tooltips (e.g., "°C").
frontColorStopsArrayArray of {value, color} objects for gradient coloring based on value.
minVal, maxVal, avgValNumberMinimum, maximum, and average values in the data.
minTime, maxTime, avgTimeNumberTimestamps corresponding to min, max, and avg values.
tooltipIdStringThe ID of the tooltip element for mouseover events.
renderChart()FunctionThe function that draws or updates the chart based on current settings and data.

Example Usage in a Function

function createAdvancedChart(container, options) {
  // options.data: array of [timestamp, value]
  // options.chartType: "line", "bar", or "both"
  // options.leftOffset, options.rightOffset, options.yOffset
  // options.polyDegree, options.decimals, options.showCircles, options.circleSize
  // options.dateFormatMode, options.headerLabel, options.units, options.frontColorStops
  // ...etc.

  // Use these options to render the chart and controls
}

Typical Data Structure Example

let data = [
  [timestamp1, value1],
  [timestamp2, value2],
  ...
];

Summary Table

VariableExample ValueDescription
data[[ts1, 12], [ts2, 15]]Chart data points
chartType"line"Chart type
leftOffset60Left margin in px
rightOffset24Right margin in px
yOffset0Vertical offset in px
polyDegree2Polynomial trend degree
decimals1Number of decimals
showCirclestrueShow data point circles
circleSize7Circle radius
dateFormatMode"datetime"X-axis label format
headerLabel"Living Room"Chart title
units"°C"Y-axis units
frontColorStops[{value:-7,color:"#2196f3"}, ...]Value-based color stops

Demo Snippet

A minimal working example with chart type, offsets, and update button:

<!DOCTYPE html>
<html>
<head>
  <title>Chart Demo</title>
  <style>
    .chart-controls { text-align: center; margin-bottom: 24px; }
    .chart-container { background: #fff; border-radius: 18px; box-shadow: 0 4px 24px 0 rgba(0,0,0,0.10); margin: 0 auto; max-width: 900px; padding: 20px; }
    .update-btn { font-size:18px; padding:6px 24px; border-radius:6px; border:1px solid #2196f3; background:#2196f3; color:#fff; cursor:pointer; margin-bottom: 32px; }
  </style>
</head>
<body>
  <div class="chart-controls">
    <button id="btn-line" class="active">Line Chart</button>
    <button id="btn-bar">Bar Chart</button>
    <button id="btn-both">Both</button>
    <label>Left offset: <input id="x-offset" type="number" value="60" min="0" max="200" style="width:50px;"></label>
    <label>Right offset: <input id="right-offset" type="number" value="24" min="0" max="200" style="width:50px;"></label>
    <label>Y offset: <input id="y-offset" type="number" value="0" min="0" max="200" style="width:50px;"></label>
  </div>
  <div style="text-align:center;">
    <button id="update-chart" class="update-btn">Update Chart</button>
  </div>
  <div class="chart-container">
    <div id="chart"></div>
  </div>
  <script>
    // Demo data
    const N = 30;
    const now = Date.now();
    const data = Array.from({length: N}, (_, i) => [
      now + i * 3600 * 1000,
      10 + 5 * Math.sin(i/10) + (Math.random()-0.5)
    ]);
    function renderChart() {
      const leftOffset = parseInt(document.getElementById('x-offset').value, 10);
      const rightOffset = parseInt(document.getElementById('right-offset').value, 10);
      const yOffset = parseInt(document.getElementById('y-offset').value, 10);
      let type = 'line';
      if (document.getElementById('btn-bar').classList.contains('active')) type = 'bar';
      if (document.getElementById('btn-both').classList.contains('active')) type = 'both';
      const width = 700, height = 300, chartX = leftOffset, chartW = width - leftOffset - rightOffset, chartY = 40 + yOffset, chartH = height - chartY - 40;
      const values = data.map(d => d[1]);
      const min = Math.min(...values), max = Math.max(...values);
      const getX = i => chartX + (i / (N-1)) * chartW;
      const getY = v => chartY + chartH - ((v - min) / (max - min)) * chartH;
      let svg = `<svg viewBox="0 0 ${width} ${height}" width="100%" height="100%">`;
      svg += `<rect x="0" y="0" width="${width}" height="${height}" fill="#e3e7ed" rx="18"/>`;
      svg += `<line x1="${chartX}" y1="${chartY}" x2="${chartX}" y2="${chartY+chartH}" stroke="#888" stroke-width="2"/>`;
      svg += `<line x1="${chartX}" y1="${chartY+chartH}" x2="${chartX+chartW}" y2="${chartY+chartH}" stroke="#888" stroke-width="2"/>`;
      if(type === "line" || type === "both") {
        svg += `<polyline fill="none" stroke="#2196f3" stroke-width="3" points="`;
        for(let i=0;i<N;i++) svg += `${getX(i)},${getY(values[i])} `;
        svg += `"/>`;
      }
      if(type === "bar" || type === "both") {
        const barW = chartW/N*0.7;
        for(let i=0;i<N;i++) {
          svg += `<rect x="${getX(i)-barW/2}" y="${getY(values[i])}" width="${barW}" height="${chartY+chartH-getY(values[i])}" fill="#2196f3" fill-opacity="0.7"/>`;
        }
      }
      svg += `</svg>`;
      document.getElementById('chart').innerHTML = svg;
    }
    // Chart type button logic
    ['btn-line', 'btn-bar', 'btn-both'].forEach(id => {
      document.getElementById(id).onclick = function() {
        ['btn-line', 'btn-bar', 'btn-both'].forEach(i => document.getElementById(i).classList.remove('active'));
        this.classList.add('active');
        renderChart();
      };
    });
    document.getElementById('update-chart').onclick = renderChart;
    document.getElementById('x-offset').oninput =
    document.getElementById('right-offset').oninput =
    document.getElementById('y-offset').oninput = renderChart;
    renderChart();
  </script>
</body>
</html>

Update Snippet (Programmatic Update Example)

If you want to update the chart programmatically (for example, after changing a setting in code), just call the renderChart() function:

<!DOCTYPE html>
<html>
<head>
  <title>Update Chart by Values Demo</title>
  <style>
    .chart-container { background: #fff; border-radius: 18px; box-shadow: 0 4px 24px 0 rgba(0,0,0,0.10); margin: 0 auto; max-width: 700px; padding: 20px; }
  </style>
</head>
<body>
  <div class="chart-container">
    <div id="chart"></div>
  </div>
  <div style="text-align:center; margin-top:20px;">
    <button onclick="updateWithRandomData()">Update with Random Data</button>
    <button onclick="updateWithSineData()">Update with Sine Data</button>
  </div>
  <script>
    // 1. Store your data in a variable
    let N = 30;
    let now = Date.now();
    let data = Array.from({length: N}, (_, i) => [
      now + i * 3600 * 1000,
      10 + 5 * Math.sin(i/10) + (Math.random()-0.5)
    ]);

    function renderChart() {
      const width = 700, height = 300, chartX = 60, chartW = width - 60 - 24, chartY = 40, chartH = height - chartY - 40;
      const values = data.map(d => d[1]);
      const min = Math.min(...values), max = Math.max(...values);
      const getX = i => chartX + (i / (N-1)) * chartW;
      const getY = v => chartY + chartH - ((v - min) / (max - min)) * chartH;
      let svg = `<svg viewBox="0 0 ${width} ${height}" width="100%" height="100%">`;
      svg += `<rect x="0" y="0" width="${width}" height="${height}" fill="#e3e7ed" rx="18"/>`;
      svg += `<polyline fill="none" stroke="#2196f3" stroke-width="3" points="`;
      for(let i=0;i<N;i++) svg += `${getX(i)},${getY(values[i])} `;
      svg += `"/>`;
      svg += `</svg>`;
      document.getElementById('chart').innerHTML = svg;
    }

    // 2. Update the data variable and call renderChart()
    function updateWithRandomData() {
      data = Array.from({length: N}, (_, i) => [
        now + i * 3600 * 1000,
        10 + Math.random()*10
      ]);
      renderChart();
    }
    function updateWithSineData() {
      data = Array.from({length: N}, (_, i) => [
        now + i * 3600 * 1000,
        10 + 5 * Math.sin(i/5)
      ]);
      renderChart();
    }

    renderChart();
  </script>
</body>
</html>

Leave a Reply