Trend Lines (React)

The TrendLine class extends the regular Series class to provide a calculated series based on the data and parameters you select.

To add trend lines to a FlexChart, follow these steps:

  1. Create one or more TrendLine objects,
  2. Configure the TrendLine objects as you would a regular series, setting their binding, chartType, and style properties for example, and
  3. Set the TrendLine's fitType and order properties to determine the type of trend line you want to create.
  4. Set the TrendLine's minX & maxX property to extend trendline beyond data range by setting custom minX and maxX values

Learn about FlexChart | TrendLine Documentation | FlexChart API Reference

This example uses React.

The demo is being dynamically compiled to support real-time code editing... For quicker access to features, switch to the "JavaScript" tab for a smoother experience! :)
app.jsx
index.html
app.css
data.jsx
Copy to CodeMine
loading...
import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import ReactDOM from 'react-dom/client'; import React, { useEffect, useRef, useState } from 'react'; import useEvent from 'react-use-event-hook'; import * as wjcChart from '@mescius/wijmo.chart'; import * as wjInput from '@mescius/wijmo.react.input'; import * as wjChart from '@mescius/wijmo.react.chart'; import * as wjChartAnalysis from '@mescius/wijmo.react.chart.analytics'; import './app.css'; import { getData, comboData } from './data'; function App() { const [data, setData] = useState(getData()); const [order, setOrder] = useState(2); const [trendlineMinX, setTrendlineMinX] = useState(0); const [trendlineMaxX, setTrendlineMaxX] = useState(0); const theChartRef = useRef(null); const eqEleRef = useRef(null); const orderIptRef = useRef(null); const trendLineRef = useRef(null); const comboRef = useRef(null); useEffect(() => { eqEleRef.current = document.querySelector('#equation'); if (comboRef.current) { reset(comboRef.current.selectedValue); } }, []); const initializeInput = useEvent((flex) => { orderIptRef.current = flex; }); const initializeCombo = useEvent((flex) => { comboRef.current = flex; }); const initializeChart = useEvent((flex) => { theChartRef.current = flex; trendLineRef.current = flex.series[1]; updateTrendLineRange(); }); const orderChanged = useEvent((s) => { if (s.value >= s.min && s.value <= s.max) { setOrder(s.value); updateTrendLineRange(); showEquation(); } }); const showEquation = () => { if (eqEleRef.current) { eqEleRef.current.innerHTML = ''; setTimeout(() => eqEleRef.current.innerHTML = trendLineRef.current.getEquation(), 100); } }; const randomData = () => { setData(getData()); updateTrendLineRange(); showEquation(); }; const textChanged = useEvent((s) => { reset(s.text); }); const updateTrendLineRange = () => { let chart = theChartRef.current; if (!chart) return; let data = chart.itemsSource; let xValues = data.map(item => item.x); let minX = Math.min(...xValues); let maxX = Math.max(...xValues); let range = maxX - minX; setTrendlineMinX(minX - (range * 0.1)); // 10% of range below minX setTrendlineMaxX(maxX + (range * 0.3)); // 30% of range above maxX }; const reset = (text) => { let trendLine = trendLineRef.current; if (!trendLine) { return; } trendLine.name = text; if (text) { // show trendline trendLine.fitType = text; trendLine.visibility = wjcChart.SeriesVisibility.Visible; } else { // hide trendline trendLine.visibility = wjcChart.SeriesVisibility.Hidden; } switch (text) { // enable/disable order input case 'Polynomial': case 'Fourier': orderIptRef.current.isDisabled = false; break; default: orderIptRef.current.isDisabled = true; break; } updateTrendLineRange(); showEquation(); }; return (<div className="container-fluid"> <div className="form-group"> <label htmlFor="fitType">Trendline Type: </label> <wjInput.ComboBox isRequired={false} placeholder="None" itemsSource={comboData} textChanged={textChanged} initialized={initializeCombo}/><br /> <label htmlFor="order">Order: </label> <wjInput.InputNumber step={1} min={1} max={6} value={order} valueChanged={orderChanged} initialized={initializeInput}/> <br /> <label>Equation: </label> <div id="equation" className="equation"></div><br /> <label htmlFor="btnRandomize">Randomize Data</label> <button id="btnRandomize" className="btn btn-default" onClick={randomData}> Go </button> <wjChart.FlexChart itemsSource={data} chartType="Scatter" bindingX="x" initialized={initializeChart}> <wjChart.FlexChartAxis wjProperty="axisY" axisLine={true}/> <wjChart.FlexChartSeries name="Raw Data" binding="y"/> <wjChartAnalysis.FlexChartTrendLine binding="y" minX={trendlineMinX} maxX={trendlineMaxX} order={order} style={{ stroke: 'darkred', strokeWidth: 3 }} visibility={wjcChart.SeriesVisibility.Hidden}/> </wjChart.FlexChart> </div> </div>); } const container = document.getElementById('app'); if (container) { const root = ReactDOM.createRoot(container); root.render(<App />); }
import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import ReactDOM from 'react-dom/client'; import React, { useEffect, useRef, useState } from 'react'; import useEvent from 'react-use-event-hook'; import * as wjcChart from '@mescius/wijmo.chart'; import * as wjInput from '@mescius/wijmo.react.input'; import * as wjChart from '@mescius/wijmo.react.chart'; import * as wjChartAnalysis from '@mescius/wijmo.react.chart.analytics'; import './app.css'; import { getData, comboData } from './data'; function App() { const [data, setData] = useState(getData()); const [order, setOrder] = useState(2); const [trendlineMinX, setTrendlineMinX] = useState(0); const [trendlineMaxX, setTrendlineMaxX] = useState(0); const theChartRef = useRef(null); const eqEleRef = useRef(null); const orderIptRef = useRef(null); const trendLineRef = useRef(null); const comboRef = useRef(null); useEffect(() => { eqEleRef.current = document.querySelector('#equation'); if (comboRef.current) { reset(comboRef.current.selectedValue); } }, []); const initializeInput = useEvent((flex) => { orderIptRef.current = flex; }); const initializeCombo = useEvent((flex) => { comboRef.current = flex; }); const initializeChart = useEvent((flex) => { theChartRef.current = flex; trendLineRef.current = flex.series[1]; updateTrendLineRange(); }); const orderChanged = useEvent((s) => { if (s.value >= s.min && s.value <= s.max) { setOrder(s.value); updateTrendLineRange(); showEquation(); } }); const showEquation = () => { if (eqEleRef.current) { eqEleRef.current.innerHTML = ''; setTimeout(() => eqEleRef.current.innerHTML = trendLineRef.current.getEquation(), 100); } }; const randomData = () => { setData(getData()); updateTrendLineRange(); showEquation(); }; const textChanged = useEvent((s) => { reset(s.text); }); const updateTrendLineRange = () => { let chart = theChartRef.current; if (!chart) return; let data = chart.itemsSource; let xValues = data.map(item => item.x); let minX = Math.min(...xValues); let maxX = Math.max(...xValues); let range = maxX - minX; setTrendlineMinX(minX - (range * 0.1)); // 10% of range below minX setTrendlineMaxX(maxX + (range * 0.3)); // 30% of range above maxX }; const reset = (text) => { let trendLine = trendLineRef.current; if (!trendLine) { return; } trendLine.name = text; if (text) { // show trendline trendLine.fitType = text; trendLine.visibility = wjcChart.SeriesVisibility.Visible; } else { // hide trendline trendLine.visibility = wjcChart.SeriesVisibility.Hidden; } switch (text) { // enable/disable order input case 'Polynomial': case 'Fourier': orderIptRef.current.isDisabled = false; break; default: orderIptRef.current.isDisabled = true; break; } updateTrendLineRange(); showEquation(); }; return (<div className="container-fluid"> <div className="form-group"> <label htmlFor="fitType">Trendline Type: </label> <wjInput.ComboBox isRequired={false} placeholder="None" itemsSource={comboData} textChanged={textChanged} initialized={initializeCombo}/><br /> <label htmlFor="order">Order: </label> <wjInput.InputNumber step={1} min={1} max={6} value={order} valueChanged={orderChanged} initialized={initializeInput}/> <br /> <label>Equation: </label> <div id="equation" className="equation"></div><br /> <label htmlFor="btnRandomize">Randomize Data</label> <button id="btnRandomize" className="btn btn-default" onClick={randomData}> Go </button> <wjChart.FlexChart itemsSource={data} chartType="Scatter" bindingX="x" initialized={initializeChart}> <wjChart.FlexChartAxis wjProperty="axisY" axisLine={true}/> <wjChart.FlexChartSeries name="Raw Data" binding="y"/> <wjChartAnalysis.FlexChartTrendLine binding="y" minX={trendlineMinX} maxX={trendlineMaxX} order={order} style={{ stroke: 'darkred', strokeWidth: 3 }} visibility={wjcChart.SeriesVisibility.Hidden}/> </wjChart.FlexChart> </div> </div>); } const container = document.getElementById('app'); if (container) { const root = ReactDOM.createRoot(container); root.render(<App />); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>MESCIUS Wijmo Wijmo FlexChart Trendlines</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.40/system.src.js" integrity="sha512-G6mEj6h18+m3MvzdviSDfPle/TfH0//cXcB33AKlNR/Rha0yQsKefDZKRTkIZos97HEGq2JMV1RT5ybMoQ3WsQ==" crossorigin="anonymous"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html>
.wj-flexchart { height: 250px; margin: 10px 0; } .wj-control { margin-bottom: 3px; } label { width: 120px; text-align: right; } .equation { display: inline-block; font-style: italic; font-size: 80%; } .wj-combobox, .wj-inputnumber { width: 120px; margin-right: 12px; }
export function getData() { let arr = [], cnt = 50, a = Math.random(), b = Math.random(); // for (let i = 1; i < cnt; i++) { arr.push({ x: i, y: a + i * b + i * Math.random() }); } // return arr; } export const comboData = ['Linear', 'Exponential', 'Logarithmic', 'Power', 'Fourier', 'Polynomial', 'MinX', 'MinY', 'MaxX', 'MaxY', 'AverageX', 'AverageY'];
(function (global) { window.process = { env: { NODE_ENV: "production" } } System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true, react: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'jszip': 'npm:jszip/dist/jszip.js', '@mescius/wijmo': 'npm:@mescius/wijmo/index.js', '@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js', '@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles', '@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures', '@mescius/wijmo.chart': 'npm:@mescius/wijmo.chart/index.js', '@mescius/wijmo.chart.analytics': 'npm:@mescius/wijmo.chart.analytics/index.js', '@mescius/wijmo.chart.animation': 'npm:@mescius/wijmo.chart.animation/index.js', '@mescius/wijmo.chart.annotation': 'npm:@mescius/wijmo.chart.annotation/index.js', '@mescius/wijmo.chart.finance': 'npm:@mescius/wijmo.chart.finance/index.js', '@mescius/wijmo.chart.finance.analytics': 'npm:@mescius/wijmo.chart.finance.analytics/index.js', '@mescius/wijmo.chart.hierarchical': 'npm:@mescius/wijmo.chart.hierarchical/index.js', '@mescius/wijmo.chart.interaction': 'npm:@mescius/wijmo.chart.interaction/index.js', '@mescius/wijmo.chart.radar': 'npm:@mescius/wijmo.chart.radar/index.js', '@mescius/wijmo.chart.render': 'npm:@mescius/wijmo.chart.render/index.js', '@mescius/wijmo.chart.webgl': 'npm:@mescius/wijmo.chart.webgl/index.js', '@mescius/wijmo.chart.map': 'npm:@mescius/wijmo.chart.map/index.js', '@mescius/wijmo.gauge': 'npm:@mescius/wijmo.gauge/index.js', '@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js', '@mescius/wijmo.grid.detail': 'npm:@mescius/wijmo.grid.detail/index.js', '@mescius/wijmo.grid.filter': 'npm:@mescius/wijmo.grid.filter/index.js', '@mescius/wijmo.grid.search': 'npm:@mescius/wijmo.grid.search/index.js', '@mescius/wijmo.grid.style': 'npm:@mescius/wijmo.grid.style/index.js', '@mescius/wijmo.grid.grouppanel': 'npm:@mescius/wijmo.grid.grouppanel/index.js', '@mescius/wijmo.grid.multirow': 'npm:@mescius/wijmo.grid.multirow/index.js', '@mescius/wijmo.grid.transposed': 'npm:@mescius/wijmo.grid.transposed/index.js', '@mescius/wijmo.grid.transposedmultirow': 'npm:@mescius/wijmo.grid.transposedmultirow/index.js', '@mescius/wijmo.grid.pdf': 'npm:@mescius/wijmo.grid.pdf/index.js', '@mescius/wijmo.grid.sheet': 'npm:@mescius/wijmo.grid.sheet/index.js', '@mescius/wijmo.grid.xlsx': 'npm:@mescius/wijmo.grid.xlsx/index.js', '@mescius/wijmo.grid.selector': 'npm:@mescius/wijmo.grid.selector/index.js', '@mescius/wijmo.grid.cellmaker': 'npm:@mescius/wijmo.grid.cellmaker/index.js', '@mescius/wijmo.grid.immutable': 'npm:@mescius/wijmo.grid.immutable/index.js', '@mescius/wijmo.touch': 'npm:@mescius/wijmo.touch/index.js', '@mescius/wijmo.cloud': 'npm:@mescius/wijmo.cloud/index.js', '@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js', '@mescius/wijmo.odata': 'npm:@mescius/wijmo.odata/index.js', '@mescius/wijmo.olap': 'npm:@mescius/wijmo.olap/index.js', '@mescius/wijmo.rest': 'npm:@mescius/wijmo.rest/index.js', '@mescius/wijmo.pdf': 'npm:@mescius/wijmo.pdf/index.js', '@mescius/wijmo.pdf.security': 'npm:@mescius/wijmo.pdf.security/index.js', '@mescius/wijmo.viewer': 'npm:@mescius/wijmo.viewer/index.js', '@mescius/wijmo.xlsx': 'npm:@mescius/wijmo.xlsx/index.js', '@mescius/wijmo.undo': 'npm:@mescius/wijmo.undo/index.js', '@mescius/wijmo.interop.grid': 'npm:@mescius/wijmo.interop.grid/index.js', '@mescius/wijmo.barcode': 'npm:@mescius/wijmo.barcode/index.js', '@mescius/wijmo.barcode.common': 'npm:@mescius/wijmo.barcode.common/index.js', '@mescius/wijmo.barcode.composite': 'npm:@mescius/wijmo.barcode.composite/index.js', '@mescius/wijmo.barcode.specialized': 'npm:@mescius/wijmo.barcode.specialized/index.js', "@mescius/wijmo.react.chart.analytics": "npm:@mescius/wijmo.react.chart.analytics/index.js", "@mescius/wijmo.react.chart.animation": "npm:@mescius/wijmo.react.chart.animation/index.js", "@mescius/wijmo.react.chart.annotation": "npm:@mescius/wijmo.react.chart.annotation/index.js", "@mescius/wijmo.react.chart.finance.analytics": "npm:@mescius/wijmo.react.chart.finance.analytics/index.js", "@mescius/wijmo.react.chart.finance": "npm:@mescius/wijmo.react.chart.finance/index.js", "@mescius/wijmo.react.chart.hierarchical": "npm:@mescius/wijmo.react.chart.hierarchical/index.js", "@mescius/wijmo.react.chart.interaction": "npm:@mescius/wijmo.react.chart.interaction/index.js", "@mescius/wijmo.react.chart.radar": "npm:@mescius/wijmo.react.chart.radar/index.js", "@mescius/wijmo.react.chart": "npm:@mescius/wijmo.react.chart/index.js", "@mescius/wijmo.react.core": "npm:@mescius/wijmo.react.core/index.js", '@mescius/wijmo.react.chart.map': 'npm:@mescius/wijmo.react.chart.map/index.js', "@mescius/wijmo.react.gauge": "npm:@mescius/wijmo.react.gauge/index.js", "@mescius/wijmo.react.grid.detail": "npm:@mescius/wijmo.react.grid.detail/index.js", "@mescius/wijmo.react.grid.filter": "npm:@mescius/wijmo.react.grid.filter/index.js", "@mescius/wijmo.react.grid.grouppanel": "npm:@mescius/wijmo.react.grid.grouppanel/index.js", '@mescius/wijmo.react.grid.search': 'npm:@mescius/wijmo.react.grid.search/index.js', "@mescius/wijmo.react.grid.multirow": "npm:@mescius/wijmo.react.grid.multirow/index.js", "@mescius/wijmo.react.grid.sheet": "npm:@mescius/wijmo.react.grid.sheet/index.js", '@mescius/wijmo.react.grid.transposed': 'npm:@mescius/wijmo.react.grid.transposed/index.js', '@mescius/wijmo.react.grid.transposedmultirow': 'npm:@mescius/wijmo.react.grid.transposedmultirow/index.js', '@mescius/wijmo.react.grid.immutable': 'npm:@mescius/wijmo.react.grid.immutable/index.js', "@mescius/wijmo.react.grid": "npm:@mescius/wijmo.react.grid/index.js", "@mescius/wijmo.react.input": "npm:@mescius/wijmo.react.input/index.js", "@mescius/wijmo.react.olap": "npm:@mescius/wijmo.react.olap/index.js", "@mescius/wijmo.react.viewer": "npm:@mescius/wijmo.react.viewer/index.js", "@mescius/wijmo.react.nav": "npm:@mescius/wijmo.react.nav/index.js", "@mescius/wijmo.react.base": "npm:@mescius/wijmo.react.base/index.js", '@mescius/wijmo.react.barcode.common': 'npm:@mescius/wijmo.react.barcode.common/index.js', '@mescius/wijmo.react.barcode.composite': 'npm:@mescius/wijmo.react.barcode.composite/index.js', '@mescius/wijmo.react.barcode.specialized': 'npm:@mescius/wijmo.react.barcode.specialized/index.js', 'jszip': 'npm:jszip/dist/jszip.js', 'react': 'npm:react/index.js', 'react-dom': 'npm:react-dom/index.js', 'react-dom/client': 'npm:react-dom/client.js', "scheduler": "npm:scheduler/index.js", 'redux': 'npm:redux/dist/redux.min.js', 'react-redux': 'npm:react-redux/dist/react-redux.min.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js', "react-use-event-hook": "npm:react-use-event-hook/dist/esm/useEvent.js", }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'jsx' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);