diff --git a/.eslintrc.json b/.eslintrc.json index c066e75..bd82cc3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -14,7 +14,7 @@ ], "linebreak-style": [ "error", - "unix" + "windows" ], "semi": [ "error", diff --git a/.gitignore b/.gitignore index 1127623..bb6312e 100644 --- a/.gitignore +++ b/.gitignore @@ -60,4 +60,8 @@ typings/ # next.js build output .next -.DS_Store \ No newline at end of file +.DS_Store + +package-lock.json +dist/ +docs/assets/js/ diff --git a/src/js/charts/AxisChart.js b/src/js/charts/AxisChart.js index 9ae40f3..39a2b8b 100644 --- a/src/js/charts/AxisChart.js +++ b/src/js/charts/AxisChart.js @@ -298,6 +298,7 @@ export default class AxisChart extends BaseChart { svgDefs: this.svgDefs, heatline: this.lineOptions.heatline, regionFill: this.lineOptions.regionFill, + spline: this.lineOptions.spline, hideDots: this.lineOptions.hideDots, hideLine: this.lineOptions.hideLine, diff --git a/src/js/objects/ChartComponents.js b/src/js/objects/ChartComponents.js index ab4a982..571bd82 100644 --- a/src/js/objects/ChartComponents.js +++ b/src/js/objects/ChartComponents.js @@ -368,7 +368,8 @@ let componentConfigs = { c.color, { heatline: c.heatline, - regionFill: c.regionFill + regionFill: c.regionFill, + spline: c.spline }, { svgDefs: c.svgDefs, @@ -419,7 +420,7 @@ let componentConfigs = { if(Object.keys(this.paths).length) { animateElements = animateElements.concat(animatePath( - this.paths, newXPos, newYPos, newData.zeroLine)); + this.paths, newXPos, newYPos, newData.zeroLine, this.constants.spline)); } if(this.units.length) { diff --git a/src/js/utils/animate.js b/src/js/utils/animate.js index 283b7b5..fd1d8c0 100644 --- a/src/js/utils/animate.js +++ b/src/js/utils/animate.js @@ -1,4 +1,4 @@ -import { getBarHeightAndYAttr } from './draw-utils'; +import { getBarHeightAndYAttr, createSplineCurve } from './draw-utils'; export const UNIT_ANIM_DUR = 350; export const PATH_ANIM_DUR = 350; @@ -74,13 +74,14 @@ export function animateDot(dot, x, y) { // dot.animate({cy: yTop}, UNIT_ANIM_DUR, mina.easein); } -export function animatePath(paths, newXList, newYList, zeroLine) { +export function animatePath(paths, newXList, newYList, zeroLine, spline) { let pathComponents = []; + let pointsStr = newYList.map((y, i) => (newXList[i] + ',' + y)).join("L"); + + if (spline) + pointsStr = createSplineCurve(newXList, newYList); - let pointsStr = newYList.map((y, i) => (newXList[i] + ',' + y)); - let pathStr = pointsStr.join("L"); - - const animPath = [paths.path, {d:"M"+pathStr}, PATH_ANIM_DUR, STD_EASING]; + const animPath = [paths.path, {d:"M" + pointsStr}, PATH_ANIM_DUR, STD_EASING]; pathComponents.push(animPath); if(paths.region) { @@ -89,7 +90,7 @@ export function animatePath(paths, newXList, newYList, zeroLine) { const animRegion = [ paths.region, - {d:"M" + regStartPt + pathStr + regEndPt}, + {d:"M" + regStartPt + pointsStr + regEndPt}, PATH_ANIM_DUR, STD_EASING ]; @@ -101,4 +102,4 @@ export function animatePath(paths, newXList, newYList, zeroLine) { export function animatePathStr(oldPath, pathStr) { return [oldPath, {d: pathStr}, UNIT_ANIM_DUR, STD_EASING]; -} +} \ No newline at end of file diff --git a/src/js/utils/draw-utils.js b/src/js/utils/draw-utils.js index 00a1bbf..e781c0d 100644 --- a/src/js/utils/draw-utils.js +++ b/src/js/utils/draw-utils.js @@ -52,4 +52,48 @@ export function shortenLargeNumber(label) { // Correct for floating point error upto 2 decimal places return Math.round(shortened*100)/100 + ' ' + ['', 'K', 'M', 'B', 'T'][l]; +} + +// cubic bezier curve calculation (from example by François Romain) +export function createSplineCurve(xList, yList) { + + let points=[]; + for(let i=0;i { + let lengthX = pointB[0] - pointA[0]; + let lengthY = pointB[1] - pointA[1]; + return { + length: Math.sqrt(Math.pow(lengthX, 2) + Math.pow(lengthY, 2)), + angle: Math.atan2(lengthY, lengthX) + }; + }; + + let controlPoint = (current, previous, next, reverse) => { + let p = previous || current; + let n = next || current; + let o = line(p, n); + let angle = o.angle + (reverse ? Math.PI : 0); + let length = o.length * smoothing; + let x = current[0] + Math.cos(angle) * length; + let y = current[1] + Math.sin(angle) * length; + return [x, y]; + }; + + let bezierCommand = (point, i, a) => { + let cps = controlPoint(a[i - 1], a[i - 2], point); + let cpe = controlPoint(point, a[i - 1], a[i + 1], true); + return `C ${cps[0]},${cps[1]} ${cpe[0]},${cpe[1]} ${point[0]},${point[1]}`; + }; + + let pointStr = (points, command) => { + return points.reduce((acc, point, i, a) => i === 0 + ? `${point[0]},${point[1]}` + : `${acc} ${command(point, i, a)}`, ''); + }; + + return pointStr(points, bezierCommand); } \ No newline at end of file diff --git a/src/js/utils/draw.js b/src/js/utils/draw.js index 506f3cf..c65790d 100644 --- a/src/js/utils/draw.js +++ b/src/js/utils/draw.js @@ -1,4 +1,4 @@ -import { getBarHeightAndYAttr, truncateString, shortenLargeNumber } from './draw-utils'; +import { getBarHeightAndYAttr, truncateString, shortenLargeNumber, createSplineCurve } from './draw-utils'; import { getStringWidth } from './helpers'; import { DOT_OVERLAY_SIZE_INCR, PERCENTAGE_BAR_DEFAULT_DEPTH } from './constants'; import { lightenDarkenColor } from './colors'; @@ -577,6 +577,11 @@ export function datasetDot(x, y, radius, color, label='', index=0) { export function getPaths(xList, yList, color, options={}, meta={}) { let pointsList = yList.map((y, i) => (xList[i] + ',' + y)); let pointsStr = pointsList.join("L"); + + // Spline + if (options.spline) + pointsStr = createSplineCurve(xList, yList); + let path = makePath("M"+pointsStr, 'line-graph-path', color); // HeatLine