【前端】【Echarts】【zrender】从入门到多路径信号流动动画实战
效果
好的!下面这篇文章会引人入胜、通俗易懂地介绍 zrender,讲清它的核心配置与函数,最后结合之前的“多路径信号流动动画示例”做详细教学,帮助你快速上手并灵活运用。
全面解析 zrender — 从入门到多路径信号流动动画实战
目录
- 什么是 zrender?
- zrender 核心概念和主要配置
- 常用图形与接口介绍
- zrender 事件与动画机制
- 实战:基于 zrender 实现多路径信号流动动画
- 拓展与思考
1. 什么是 zrender?
zrender 是百度团队开源的一个轻量级 2D 渲染库,专注于高性能图形绘制。它是 ECharts 的底层渲染引擎,提供了丰富的图形类型、灵活的事件处理、动画支持,兼容 Canvas 和 SVG。
-
为什么用 zrender?
- 轻量、灵活,适合需要自定义图形和动画的场景
- 兼容性好,支持主流浏览器
- 支持丰富图形:矩形、圆形、线条、多边形、路径、文本等
- 事件系统完善,支持拖拽、点击等交互
- 支持图形分组与层级管理,方便复杂图形构建
简言之,zrender 是前端可视化的利器,适合做仪表盘、流程图、动画演示、交互设计等。
2. zrender 核心概念和主要配置
2.1 zrender 实例
调用:
const zr = zrender.init(domElement);
用于初始化渲染上下文,所有绘制都基于这个实例。
2.2 图形元素(Shape)
zrender 里的绘制单位是 图形元素,如 Rect
、Circle
、Polyline
、Path
等。图形通过构造函数创建:
const circle = new zrender.Circle({shape: { cx: 100, cy: 100, r: 50 },style: { fill: 'red', stroke: 'black', lineWidth: 2 }
});
shape
:定义图形的几何参数,如位置、尺寸style
:定义图形的渲染样式,如填充颜色、边框颜色、线宽、阴影等
2.3 图形类型及常用属性示例
图形类型 | 主要 shape 属性 | 备注 |
---|---|---|
Rect | x, y, width, height | 矩形 |
Circle | cx, cy, r | 圆形 |
Line | x1, y1, x2, y2 | 直线 |
Polyline | points: [[x1,y1], [x2,y2], …] | 折线 |
Polygon | points: [[x1,y1], [x2,y2], …] | 多边形(闭合折线) |
Path | pathData: SVG 路径字符串 | 任意复杂路径 |
Text | text, x, y, font | 文本 |
2.4 常用 style 属性
属性 | 作用 |
---|---|
fill | 填充颜色 |
stroke | 边框颜色 |
lineWidth | 边框线宽 |
opacity | 透明度 |
shadowColor | 阴影颜色 |
shadowBlur | 阴影模糊半径 |
font | 文字字体与大小 |
textAlign | 文字对齐 |
3. 常用接口和函数介绍
3.1 添加和删除图形
zr.add(shape); // 添加图形
zr.remove(shape); // 删除图形
3.2 设置图形属性
- 修改形状参数
shape.attr('shape', { cx: 200, cy: 200 });
- 修改样式
shape.setStyle({ fill: 'blue' });
3.3 事件绑定
支持常见事件:
shape.on('click', () => { console.log('点击了图形'); });
shape.on('mousemove', () => { /* 鼠标移动 */ });
shape.on('drag', () => { /* 拖拽 */ });
开启拖拽:
shape.draggable = true;
3.4 动画支持
shape.animateTo({ style: { fill: 'red' } }, {duration: 1000,easing: 'cubicOut'
});
3.5 线性渐变和径向渐变
- 线性渐变
new zrender.LinearGradient(x0, y0, x1, y1, [{ offset: 0, color: 'red' },{ offset: 1, color: 'blue' }
])
- 径向渐变
new zrender.RadialGradient(x, y, r, [{ offset: 0, color: 'white' },{ offset: 1, color: 'blue' }
])
4. zrender 事件与动画机制
zrender 通过内部动画帧驱动,支持全局动画事件监听:
zr.animation.on('frame', () => {// 每一帧执行,适合做流动动画、物体跟踪等
});
5. 实战教学:多路径信号流动动画
5.1 需求回顾
- 多条路径
- 每条路径独立小球流动
- 小球带尾巴尾迹(拖尾渐变)
- 可控制速度、颜色、半径、尾巴长度
5.2 代码结构及配置说明
示例把所有可修改配置放最前面,方便调整:
const configList = [{points: [[100, 200], [300, 250], [600, 180]],color: '#00FFFF',speed: 2,radius: 8,tailLength: 0.1},// 更多路径...
];
points
:路径点数组,确定路径形状color
:路径和球颜色speed
:小球运动速度,单位像素/帧radius
:小球半径tailLength
:尾巴长度,按当前路径段比例计算
5.3 核心逻辑讲解
5.3.1 初始化每条路径
- 画主线(低透明度)
- 创建尾巴折线,使用线性渐变让尾巴逐渐消失
- 创建带径向渐变的信号球
5.3.2 计算路径长度与分段长度
为了实现流动,先计算每条路径分段长度及总长度,方便定位小球位置。
5.3.3 帧动画更新
在 zr.animation.on('frame')
中,根据速度递增「进度」,再根据进度确定当前在哪条段及相对比例 t
,然后计算当前小球的坐标。
尾巴位置计算同理,是当前进度向后(或向前)一定比例的点。
5.4 完整代码参考
详见我给你的代码示例【配置集中管理版】
6. 拓展思考
- 结合拖拽事件,实时调整路径
- 多点并行流动,模拟复杂管网
- 节点状态高亮,添加交互信息弹窗
- 融入数据驱动,实时更新信号状态
源码
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8" /><title>zrender 多路径信号流动(可配置)</title><style>html, body, #main {margin: 0;width: 100vw;height: 100vh;background: #111;}</style>
</head>
<body>
<div id="main"></div><script src="https://cdn.jsdelivr.net/npm/zrender@5.4.4/dist/zrender.min.js"></script>
<script>const zr = zrender.init(document.getElementById('main'));// 🛠️ === 全部配置集中在这里修改 ===const configList = [{points: [[100, 200], [300, 250], [600, 180]],color: '#00FFFF',speed: 2,radius: 8,tailLength: 0.1},{points: [[200, 500], [400, 400], [700, 480]],color: '#FF69B4',speed: .2,radius: 10,tailLength: 0.15},{points: [[150, 100], [250, 150], [350, 100], [500, 180]],color: '#FFD700',speed: 1.6,radius: 6,tailLength: 0.08}];const runners = [];for (const conf of configList) {const { points, color, speed, radius, tailLength } = conf;// 背景主路径const baseLine = new zrender.Polyline({shape: { points },style: {stroke: color,lineWidth: 2,opacity: 0.2}});zr.add(baseLine);// 尾巴线段const tailLine = new zrender.Polyline({shape: { points: [points[0], points[0]] },style: {lineWidth: 3,stroke: new zrender.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: color },{ offset: 1, color: 'rgba(0,0,0,0)' }])},z: 10});zr.add(tailLine);// 信号球体const ball = new zrender.Circle({shape: { cx: points[0][0], cy: points[0][1], r: radius },style: {fill: new zrender.RadialGradient(0.5, 0.5, 0.5, [{ offset: 0, color: '#fff' },{ offset: 1, color: color }]),shadowColor: color,shadowBlur: 12},z: 20});zr.add(ball);// 路径段长度计算const segmentLengths = [];let totalLength = 0;for (let i = 0; i < points.length - 1; i++) {const dx = points[i + 1][0] - points[i][0];const dy = points[i + 1][1] - points[i][1];const len = Math.sqrt(dx * dx + dy * dy);segmentLengths.push(len);totalLength += len;}runners.push({points,segmentLengths,totalLength,progress: 0,speed,ball,tailLine,tailLength});}// 帧动画:更新小球位置 & 尾巴拖影zr.animation.on('frame', () => {for (const runner of runners) {const { points, segmentLengths, totalLength, ball, tailLine, speed, tailLength } = runner;runner.progress += speed;if (runner.progress > totalLength) runner.progress = 0;let traveled = 0;for (let i = 0; i < segmentLengths.length; i++) {const len = segmentLengths[i];if (traveled + len >= runner.progress) {const t = (runner.progress - traveled) / len;const [x1, y1] = points[i];const [x2, y2] = points[i + 1];const x = x1 + (x2 - x1) * t;const y = y1 + (y2 - y1) * t;ball.attr('shape', { cx: x, cy: y });// 尾巴位置const tailT = Math.max(t - tailLength, 0);const tx = x1 + (x2 - x1) * tailT;const ty = y1 + (y2 - y1) * tailT;tailLine.attr('shape', { points: [[tx, ty], [x, y]] });break;}traveled += len;}}});
</script>
</body>
</html>