前段时间遇到一个需求,须要画一个 7日年化利率 的曲线图。前端
UI 提了几个需求,说实话,一开始看到时都懵逼了,而后我回了句 “你说,我不必定实现。”git
一开始看了网上的一些开源图表,看是能实现,可是又以为比较重,不必为了一个图表而引入整个库。然后下定决心本身撸一个。github
固然了一开始并不打算写一个 npm 库,只是在项目中写一个 js 来实现图表绘制。在效果实现以后以为须要整理一番,因而在五一期间进行了重构,将结构整清楚,提升扩展性,以便应对往后不一样的需求。web
先来看看 demonpm
固然了 lw 不是我名字的缩写,lw 是指 lightweight 轻量的意思,我但愿这 lw-chart 可以真正作到轻量化,当我只使用其中某一个类型的图表时,那么只须要引入这个类型的图表,其余的不相关的代码不要来增长项目的体积。canvas
「lw-chart 是如何作的呢?」api
lw-chart 内部经过类继承的方式提升代码的复用性,同时在编译打包时,输出多个文件,当实际项目中使用了其中某一个类型的图表,那么能够再项目中只 import
一个文件,避免把全部类型的图表都都入到项目中而不使用的状况。数组
具体的使用方式能够查看文档,这里就不在赘述。markdown
在开始编码前必定要先考虑好布局,否则在写代码时会很混乱,不知道坐标该加仍是该减。函数
第一次写的时候,由于参考了网上的代码,致使没有清晰的布局,虽然能绘制出一些东西,可是遇到要计算坐标时,就会很混乱,致使就都没法达到预期的效果。
最后本身画了一个布局的图,再开始编码。下面是个人一个布局结构
我分了两个类来实现这个布局,一个是基类 LWChart
,定义了 canvas
,titleBar
,chart
。
而 x坐标 和 y坐标 则定义在 Axis
坐标轴类中,由于有些图表可能不须要坐标轴,因此经过 Axis
继承 LWChart
达到更灵活的配置。
获取屏幕 dpi,尺寸及位置参数在绘制时乘以 dpi
。
dpi
的计算方式以下:
let n = (ctx.backingStorePixelRatio ||
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
1);
let a = (window.devicePixelRatio || 1) / n;
if (a < 1) {
a = 1;
}
const dpi = a / n;
复制代码
可能大部分都是使用现成的图表库,网上比较少关于贝塞尔曲线控制点计算的文章。
/** 给原始坐标点添加三次贝塞尔曲线控制点 */
export const getCurveList = function (pointList: IPos[]): ICurvePoint[] {
if (pointList.length <= 0) return [];
// 长度比例系数
const lenParam = 1 / 3;
const len = pointList.length;
return pointList.map((curPoint, index) => {
const nextPoint = index === len - 1 ? curPoint : pointList[index + 1];
const curX = curPoint.x;
const curY = curPoint.y;
const nextX = nextPoint.x;
const nextY = nextPoint.y;
const deltaX = Math.abs(nextX - curX) * lenParam;
return {
start: curPoint,
end: nextPoint,
control1: {
x: curX + deltaX,
y: curY
},
control2: {
x: nextX - deltaX,
y: nextY
}
};
});
};
复制代码
以上是个人计算方式,主要经过先后两个点的 x坐标 进行加减生成两个控制点,使得贝塞尔曲线相对平滑。
动画主要经过 requestAnimationFrame
api 进行实现。
将须要绘制的数据存入一个数组中,将数组中的前 n - 1 个数据使用 bezierCurveTo
绘制,最后一段曲线使用贝塞尔曲线函数进行绘制
// 动画执行中,使用贝塞尔函数绘制最后一段曲线
for (let t = 0; t <= percent / 100; t += 0.1) {
lastX = curveWithTime(lastPoint.start.x, lastPoint.control1.x, lastPoint.control2.x, lastPoint.end.x, t);
lastY = curveWithTime(lastPoint.start.y, lastPoint.control1.y, lastPoint.control2.y, lastPoint.end.y, t);
this.ctx.lineTo(lastX, lastY);
}
复制代码
使用 ctx.lineTo
绘制出来的为直线,因此须要小间隙绘制多个点才能使得曲线相对平滑。
首先经过计算每段曲线应该执行的时间, 结合 requestAnimationFrame
和 performance.now();
获取每一帧的时间差,经过时间差与每一段曲线的单位时间进行对比,计算出百分比,再经过上面的 for
循环进行绘制。
目前 lw-chart 中实现的图表绘制的只有 Area
,能够经过参数配置控制显示线条或区域。可是这也许仍是仍是不够好用,也不可能知足到各个需求。
因此各位开发者能够在 lw-chart 的基础上进行定制开发,经过继承 LWChart
或者 Axis
这个类。
关于如何开发,后期整理后会在文档上更新。
欢迎各位大佬 pull request,一同开发。
若是以为 lw-chart 不错的话,不妨下载体验一番,顺便到 Github 点个 Star。
若是以为内容还不错的话,但愿小伙伴能够帮忙点赞转发,给更多的同窗看到,感谢感谢!
若是你喜欢个人文章,还能够关注个人公众号【前端develop】