在web开发中数据可视化在企业中后台是很重要的一部分,常见的图表有折线图、柱状图、饼图...等等。开发者为了效率和效果会使用第三方图表库,例如:echarts、antV、D3,这些图表库的底层实现包括 canvas、SVG、webGL,接下来我会使用 svg 来实现一个简单的折线图。
创建折线图组件
创建一个名为 <LineChart />
的新组件并将文件命名为 line-chart.js。
组件接受一个名为 data 的 props。
import React, { useRef, useState } from 'react';
import PropTypes from 'prop-types';
const LineChart = ({ data }) => {
return null;
};
LineChart.propTypes = {
/** The shape of the data */
data: PropTypes.arrayOf(
PropTypes.shape({
total: PropTypes.number.isRequired,
date: PropTypes.string.isRequired
})
).isRequired
};
export default LineChart;
设置这线图
这步设置折线图不同部分的变量
import React from 'react';
import PropTypes from 'prop-types';
const LineChart = ({ data }) => {
const chartWidth = 1200; // SVG 视图框的宽度
const chartHeight = 600; // SVG 视图框的高度
const offsetY = 40; // 用于定位刻度
const paddingX = 50; // 折线图周围的左右内边距
const paddingY = 90; // 折线图周围的上下内边距
const maxY = Math.max(...data.map((item) => item.total)); // 数据数组中的最大总值
const guides = [...Array(16).keys()]; // 一个空数组,用于确定指南的数量
return null;
};
LineChart.propTypes = {
/** The shape of the data */
data: PropTypes.arrayOf(
PropTypes.shape({
total: PropTypes.number.isRequired,
date: PropTypes.string.isRequired
})
).isRequired
};
export default LineChart;
properties 是使用来自 data prop 的值和上一步中定义的变量的组合创建的。每个返回值都用于折线图的不同部分。
const LineChart = ({ data }) => {
// ...
const properties = data.map((property, index) => {
const { total, date } = property;
const x = (index / data.length) * (chartWidth - paddingX) paddingX / 2;
const y = chartHeight - offsetY - (total / maxY) * (chartHeight - (paddingY + offsetY)) - paddingY + offsetY;
return {
total: total,
date: date,
x: x,
y: y
};
});
return null;
};
这里要查看的两个重要值是 x 和 y。这些变量是通过组合前面定义的一些变量和 .map 中的索引值来创建的。
创建 X 坐标
x 坐标用于创建标记的位置、值和折线点的 x 值。
const x = (index / data.length) * (chartWidth - paddingX) + paddingX / 2;
x 坐标是通过使用 .map 中的索引值并将其除以在 data prop 上传递的数据的长度来创建的。将其乘以 chartWidth 并减去 paddingX 值可确保 x 坐标值永远不会超过图表宽度的范围。
创建 Y 坐标
y 坐标用于创建标记的位置、值和折线点的 y 值。
const y = chartHeight - offsetY - (total / maxY) * (chartHeight - (paddingY + offsetY)) - paddingY + offsetY;
y 坐标是通过将总数除以 maxY 值再乘以 chartHeight 减去 paddingY 值来创建的。作为一个额外的步骤,减去 paddingY 加上 offsetY 值,这为图表底部的刻度创建了一些额外的空间。
创建点数组
SVG Polyline 元素可用于创建由点连接的线。要为折线创建点,您可以使用属性数组中的 x 和 y 值,并将它们作为 x、y 位置的数组返回。
const points = properties.map((point) => {
const { x, y } = point;
return `${x},${y}`;
});
点数组将返回类似于下面的内容。数组中的每个索引都包含 x 和 y 坐标。
[
'25,25', '47,232', '69,40', '91,235' ...
]
创建 SVG
创建一个新的 元素,定义 viewBox 属性并将角色设置为presentation。
const LineChart = ({ data }) => {
// ...
return (
<svg viewBox={`0 0 ${chartWidth} ${chartHeight}`} role="presentation">
</svg>
);
};
创建折线
创建一个新的 <polyline />
元素,将填充设置为 none 并添加相关 class来设置样式。还可以使用属性定义strokeWidth。使用 points 属性,可以传递 points 数组返回的值。
<polyline fill="none" className="stroke-gray-400" strokeWidth={2} points={points} />
创建标记和值
使用 properties 数组的返回值,现在可以添加和定位 svg 元素以显示数据数组中的总数。
return (
<svg viewBox={`0 0 ${chartWidth} ${chartHeight}`} role="presentation">
<polyline fill="none" className="stroke-gray-400" strokeWidth={2} points={points} />
{properties.map((property, index) => {
const { total, date, x, y } = property;
return (
<g key={index}>
<circle className="stroke-gray-400 fill-white" cx={x} cy={y} r={12} strokeWidth={2} />
<text x={x} y={y + 2.8} textAnchor="middle" fontSize={8} className="font-bold fill-gray-800 select-none">
{total}
</text>
</g>
);
})}
</svg>
);
元素使用 x 和 y 属性使用 cx 和 cy 属性定位,并使用 r 属性指定半径为 12。您可以添加自己的类名和 strokeWidth 的值以实现所需的外观。
元素也使用 x 和 y 属性使用 x 和 y 属性定位。 元素接受子元素,因此添加 total 属性以显示它。我在 y 中添加了一个额外的 2.8,以确保文本在 中垂直居中。可以添加自己的类名或属性来获得所需的外观。
创建刻度
return (
<g key={index}>
...
<g transform={`translate(${x} ${chartHeight - (paddingY - offsetY)})`}>
<text transform="rotate(45)" textAnchor="start" transformorigin="50% 50%" fontSize={10} className="fill-gray-800 select-none">
{new Date(date).toLocaleDateString(undefined, { year: '2-digit', month: 'numeric', day: 'numeric' })}
</text>
</g>
</g>
);
刻度的创建方式略有不同, 元素被包裹在 中。
这是为了创建一个新的坐标系,以便在对文本应用旋转时,其顶部/左侧位置是根据 而不是 元素的顶部和左侧位置计算的。
元素不支持 x 或 y 属性,因此您将使用 transform 属性并为 x 和 y 位置提供平移值。我还减去了 paddingY 和 offsetY 值来正确定位刻度。
创建指南
指南是最后添加的元素;但是这次你将迭代guides数组,而不是遍历properties数组。
这只是一个空数组,索引用于为 React 提供键。 y 坐标的创建方式与以前类似,其中创建了一个比率以确保 y 位置保持在边界内并且不与刻度重叠。
const LineChart = ({ data }) => {
return (
<svg viewBox={`0 0 ${chartWidth} ${chartHeight}`} role="presentation">
...
{guides.map((_, index) => {
const ratio = index / guides.length;
const y = chartHeight - paddingY - chartHeight * ratio;
return <polyline key={index} className="stroke-gray-200" fill="none" strokeWidth={1} points={`${paddingX / 2},${y} ${chartWidth - paddingX / 2},${y}`} />;
})}
...
</svg>
);
};
使用从属性数组返回的值,可以添加任意数量的不同 SVG 元素,以帮助显示来自不同类型数据的不同值。
x 和 y 属性应该是您所需要的。虽然创建它们有点棘手,但它们为您想添加到图表中的任何新元素创建了边界。
Top comments (0)