react-native-graph-plus
Version:
📈 Beautiful, high-performance Graphs and Charts for React Native +
137 lines (121 loc) • 5.93 kB
JavaScript
import { Skia } from '@shopify/react-native-skia';
const PIXEL_RATIO = 2;
export function getGraphPathRange(points, range) {
var _ref, _range$x$min, _range$x, _points$, _ref2, _range$x$max, _range$x2, _points, _range$y$min, _range$y, _range$y$max, _range$y2;
const minValueX = (_ref = (_range$x$min = range === null || range === void 0 ? void 0 : (_range$x = range.x) === null || _range$x === void 0 ? void 0 : _range$x.min) !== null && _range$x$min !== void 0 ? _range$x$min : (_points$ = points[0]) === null || _points$ === void 0 ? void 0 : _points$.date) !== null && _ref !== void 0 ? _ref : new Date();
const maxValueX = (_ref2 = (_range$x$max = range === null || range === void 0 ? void 0 : (_range$x2 = range.x) === null || _range$x2 === void 0 ? void 0 : _range$x2.max) !== null && _range$x$max !== void 0 ? _range$x$max : (_points = points[points.length - 1]) === null || _points === void 0 ? void 0 : _points.date) !== null && _ref2 !== void 0 ? _ref2 : new Date();
const minValueY = (_range$y$min = range === null || range === void 0 ? void 0 : (_range$y = range.y) === null || _range$y === void 0 ? void 0 : _range$y.min) !== null && _range$y$min !== void 0 ? _range$y$min : points.reduce((prev, curr) => curr.value < prev ? curr.value : prev, Number.MAX_SAFE_INTEGER);
const maxValueY = (_range$y$max = range === null || range === void 0 ? void 0 : (_range$y2 = range.y) === null || _range$y2 === void 0 ? void 0 : _range$y2.max) !== null && _range$y$max !== void 0 ? _range$y$max : points.reduce((prev, curr) => curr.value > prev ? curr.value : prev, Number.MIN_SAFE_INTEGER);
return {
x: {
min: minValueX,
max: maxValueX
},
y: {
min: minValueY,
max: maxValueY
}
};
}
export const getXPositionInRange = (date, xRange) => {
const diff = xRange.max.getTime() - xRange.min.getTime();
const x = date.getTime();
return (x - xRange.min.getTime()) / diff;
};
export const getXInRange = (width, date, xRange) => {
return Math.floor(width * getXPositionInRange(date, xRange));
};
export const getYPositionInRange = (value, yRange) => {
const diff = yRange.max - yRange.min;
const y = value;
return (y - yRange.min) / diff;
};
export const getYInRange = (height, value, yRange) => {
return Math.floor(height * getYPositionInRange(value, yRange));
};
export const getPointsInRange = (allPoints, range) => {
return allPoints.filter(point => {
const portionFactorX = getXPositionInRange(point.date, range.x);
return portionFactorX <= 1 && portionFactorX >= 0;
});
};
function createGraphPathBase(_ref3) {
let {
pointsInRange: graphData,
range,
horizontalPadding,
verticalPadding,
canvasHeight: height,
canvasWidth: width,
shouldFillGradient
} = _ref3;
const path = Skia.Path.Make(); // Canvas width substracted by the horizontal padding => Actual drawing width
const drawingWidth = width - 2 * horizontalPadding; // Canvas height substracted by the vertical padding => Actual drawing height
const drawingHeight = height - 2 * verticalPadding;
if (graphData[0] == null) return path;
const points = [];
const startX = getXInRange(drawingWidth, graphData[0].date, range.x) + horizontalPadding;
const endX = getXInRange(drawingWidth, graphData[graphData.length - 1].date, range.x) + horizontalPadding;
const getGraphDataIndex = pixel => Math.round((pixel - startX) / (endX - startX) * (graphData.length - 1));
const getNextPixelValue = pixel => {
if (pixel === endX || pixel + PIXEL_RATIO < endX) return pixel + PIXEL_RATIO;
return endX;
};
for (let pixel = startX; startX <= pixel && pixel <= endX; pixel = getNextPixelValue(pixel)) {
const index = getGraphDataIndex(pixel); // Draw first point only on the very first pixel
if (index === 0 && pixel !== startX) continue; // Draw last point only on the very last pixel
if (index === graphData.length - 1 && pixel !== endX) continue;
if (index !== 0 && index !== graphData.length - 1) {
// Only draw point, when the point is exact
const exactPointX = getXInRange(drawingWidth, graphData[index].date, range.x) + horizontalPadding;
const isExactPointInsidePixelRatio = Array(PIXEL_RATIO).fill(0).some((_value, additionalPixel) => {
return pixel + additionalPixel === exactPointX;
});
if (!isExactPointInsidePixelRatio) continue;
}
const value = graphData[index].value;
const y = drawingHeight - getYInRange(drawingHeight, value, range.y) + verticalPadding;
points.push({
x: pixel,
y: y
});
}
for (let i = 0; i < points.length; i++) {
const point = points[i]; // first point needs to start the path
if (i === 0) path.moveTo(point.x, point.y);
const prev = points[i - 1];
const prevPrev = points[i - 2];
if (prev == null) continue;
const p0 = prevPrev !== null && prevPrev !== void 0 ? prevPrev : prev;
const p1 = prev;
const cp1x = (2 * p0.x + p1.x) / 3;
const cp1y = (2 * p0.y + p1.y) / 3;
const cp2x = (p0.x + 2 * p1.x) / 3;
const cp2y = (p0.y + 2 * p1.y) / 3;
const cp3x = (p0.x + 4 * p1.x + point.x) / 6;
const cp3y = (p0.y + 4 * p1.y + point.y) / 6;
path.cubicTo(cp1x, cp1y, cp2x, cp2y, cp3x, cp3y);
if (i === points.length - 1) {
path.cubicTo(point.x, point.y, point.x, point.y, point.x, point.y);
}
}
if (!shouldFillGradient) return path;
const gradientPath = path.copy();
gradientPath.lineTo(endX, height + verticalPadding);
gradientPath.lineTo(0 + horizontalPadding, height + verticalPadding);
return {
path: path,
gradientPath: gradientPath
};
}
export function createGraphPath(props) {
return createGraphPathBase({ ...props,
shouldFillGradient: false
});
}
export function createGraphPathWithGradient(props) {
return createGraphPathBase({ ...props,
shouldFillGradient: true
});
}
//# sourceMappingURL=CreateGraphPath.js.map