前沿
今天给大家分享基于echarts的带流动光效的折线图, 首先看一下要做的效果如下图:
刚开始以为很容易实现,但是echart只支持非平滑曲线,效果实现代码可查看juejin.cn/post/712918…
实现原理
实现原理其实也很简单,我们基于上面非平滑曲线,通过算法处理图表数据,增加图表数据的同时让线条呈现曲线。下面是算法源码
/**
* 图表增加采样
*/
// 二次贝塞尔曲线
function bezier2(p0, p1, p2, t) {
const t2 = t * t
const m = 1 - t
const m2 = m * (1 - t)
return p0 * m2 + 2 * p1 * t * m + p2 * t2
}
// 三次贝塞尔曲线
function bezier3(p0, p1, p2, p3, t) {
const t2 = t * t
const t3 = t * t2
const m = 1 - t
const m2 = m * (1 - t)
const m3 = m2 * (1 - t)
return p0 * m3 + 3 * p1 * t * m2 + 3 * p2 * t2 * m + p3 * t3
}
function getMidPoint(p0, p1) {
const x0 = p0[0]
const y0 = p0[1]
const x1 = p1[0]
const y1 = p1[1]
return [(x0 + x1) / 2, (y0 + y1) / 2]
}
function movePoint(p, v) {
const px = p[0]
const py = p[1]
const vx = v[0]
const vy = v[1]
return [px + vx, py + vy]
}
function getVector(p0, p1) {
const x0 = p0[0]
const y0 = p0[1]
const x1 = p1[0]
const y1 = p1[1]
return [x1 - x0, y1 - y0]
}
export function smoothLine(points, smooth = 50) {
const len = points.length
if (len < 3) {
return points
}
const newPoints = []
const controls = []
for (let i = 0; i < len - 2; i++) {
const p0 = points[i]
const p1 = points[i + 1]
const p2 = points[i + 2]
const m1 = getMidPoint(p0, p1)
const m2 = getMidPoint(p1, p2)
const m = getMidPoint(m1, m2)
const v = getVector(m, p1)
const _m1 = movePoint(m1, v)
const _m2 = movePoint(m2, v)
// 起点
if (i === 0) {
for (let j = 0; j < smooth; j++) {
const x = bezier2(p0[0], _m1[0], p1[0], j / smooth)
const y = bezier2(p0[1], _m1[1], p1[1], j / smooth)
newPoints.push([x, y, ...p0])
}
controls.push(_m1)
controls.push(_m2)
} else {
for (let j = 0; j < smooth; j++) {
const prev = controls[controls.length - 1]
const x = bezier3(p0[0], prev[0], _m1[0], p1[0], j / smooth)
let y = bezier3(p0[1], prev[1], _m1[1], p1[1], j / smooth)
if (y < 0) {
y = 0
}
newPoints.push([x, y, ...p0])
}
controls.push(_m1)
controls.push(_m2)
// 结束
if (i === len - 3) {
for (let j = 0; j <= smooth; j++) {
const x = bezier2(p1[0], _m2[0], p2[0], j / smooth)
const y = bezier2(p1[1], _m2[1], p2[1], j / smooth)
newPoints.push([x, y, ...p0])
}
}
}
}
return newPoints
}
处理数据
接下来我们使用算法处理原始的图表数据
// 原始图表数据
const respData = [
{
"STATUS":0,
"COUNT":"4",
"NAME":"[0,1)"
},
{
"STATUS":0,
"COUNT":"0",
"NAME":"[2,4)"
},
{
"STATUS":1,
"COUNT":"4",
"NAME":"[1,1)"
},
{
"STATUS":1,
"COUNT":"0",
"NAME":"[1,4)"
},
{
"STATUS":2,
"COUNT":"5",
"NAME":"[2,1)"
},
{
"STATUS":2,
"COUNT":"0",
"NAME":"[2,4)"
},
{
"STATUS":3,
"COUNT":"5",
"NAME":"[3,1)"
},
{
"STATUS":3,
"COUNT":"0",
"NAME":"[3,4)"
}
],
let smoothLinesCoordsData = [
{
coords: [],
},
]
// 处理折线数据
const smoothLineData = smoothLine(respData)
// 再通过折线数据获取光线数据
smoothLinesCoordsData[0].coords = smoothLineData.map((item) => [
item[0],
item[1],
])
处理X轴
我们增加数据采样后,会导致数据量很大,如果不处理X轴的话X轴会密密麻麻一大堆。 所以我们修改下x轴interval属性,让他有一个间隔值,可根据自己实际情况来设置
xAxis: {
type: 'value',
name: '',
min: 1,
interval: 1,
axisTick: {
show: false,
},
axisLabel: {
rotate: 35,
formatter: (params) => {
return lineArrData[params - 1] ? lineArrData[params - 1][2] : ''
},
},
position: 'bottom',
},
以上就是所有步骤,如果你是用react, 已经有相应的插件了。可查看juejin.cn/post/709056…