Echarts[1]作为前端工程师经常使用的数据可视化库,带来了生动、直观与可交互的数据可视化体验(如图1.1所示),这些功能都离不开强大的前端Canvas库——ZRender[2]带来的底层支撑,该库以API的形式向用户提供画布生成,元素创建、拖拽、裁剪、动画、动态渲染等一系列功能,广泛应用于图形交互、在线文档等大规模前端应用。
图1.1 Echarts可视化图表库
网上对ZRender的介绍非常稀少,本文将从Canvas技术出发,进而展开对ZRender底层原理的介绍,让开发人员逐步掌握ZRender的使用与进一步底层开发方式,达到按需拓展功能的效果。
本文针对的是Canvas2D进行介绍,所有的绘制都来自于getContext("2d")获得的ctx上下文对象(为了便于阅读,下文中我们统一称为Canvas)。
为了帮助初学者快速了解Canvas,本章内容首先介绍Canvas的大致内容,如果您对Canvas有一定了解,那么可以直接跳到第二章开始阅读。
在网页上展示图像的技术有很多,例如<img>标签、Flash、SVG、Canvas等。为了追求数据更好的动态呈现,前端开发中主要使用SVG与Canvas来动态生成图像,SVG以矢量图的形式可实现高保真图像的呈现,但其基于XML文档描述来绘图的形式虽然在编辑与交互上具有一定优势,却无法支持像素处理、大量数据绘制等场景。Canvas虽然不具备SVG矢量图的优势,但在像素处理、动态渲染和大量数据绘制上有更好的支持,且性能表现更优。
Canvas被称为是网页上的一块“画布”,这块画布不仅可以绘制各种图形、图像与文字,还能够操作图形进行变形、渐变、动画等操作。不仅如此,利用Canvas中每一个像素点的可操作性,可以实现图像与视频在前端的动态处理。在Canvas强大功能与稳定性能的加持下,Echarts、腾讯文档在线PPT[3]、Explain Everything(在线白板)[4]等大型应用都是基于Canvas进行实现。
从开发者角度来看,主流应用都是对Canvas原生API进行了元素封装,使其能够提供满足用户在特定使用场景下的具体可视化需求。本章将从原生API的角度介绍Canvas,了解Canvas提供的各种功能的具体实现。
1.1 Canvas元素绘制
Canvas 2D是一种完全以Javascript脚本语言为驱动的图形绘制技术,绘制操作全部发生在Canvas2D上下文对象(ctx)的各种API上,利用Javascript动态输入各项参数,达到动态绘制的效果。
ctx中提供的原生API主要包括以下9类[5]:
(1)直线图形绘制类:包括线段、矩形的绘制,其他多边形都通过线段绘制来实现;
(2)曲线图形绘制类:包括圆形与弧线的绘制;
(3)线条操作类:包括线条宽度、线帽、线条虚实等的操作;
(4)文本操作类:包括文本绘制与格式处理;
(5)图片操作类:包括图片绘制、图片截取、图片平铺与图片切割;
(6)变形操作类:包括图形平移、缩放、旋转、矩阵变换与画布清空;
(7)像素操作类:包括像素获取与像素输入,获取到的像素信息可以通过Javascript进行像素反转、黑白转换、亮度处理、复古风格、颜色蒙版、透明处理等图像处理;
(8)渐变与阴影类:包括线性颜色渐变、径向颜色渐变与阴影生成;
(9)路径类:包括开始绘制路径、关闭当前路径与判断某个点是否存在当前路径中;
1.2 Canvas进阶
当掌握Canvas基础知识后,可以在下面几个方面进一步进阶:
(1)利用Javascript作为高级编程语言的特性,可以设计出各种交互性强的页面与动画效果:
一方面,开发者通过浏览器提供的多个事件监听方法,为用户提供鼠标点击交互、键盘交互等功能,满足用户对图形的交互操作;另一方面,利用window.requestAnimationFrame()可以实现与客户端帧数匹配的动画效果。
(2)若想做出交互性强的动画效果,需要掌握基础的数学与物理知识:
运动离不开数学与物理规律,在设计动画时应该充分利用现有的数学与物理知识。例如:利用数学中的sin、cos、tan、arcsin等三角函数,可以测算出点之间的距离、确定动画的路径、生成波形运动、脉冲效果等等。
(3)Canvas画布作为一块有边界的区域,“边界监测”是动画设计中必不可少的环节:
“边界监测”简单来说,就是给运动物体限定一个范围,从而实现某些动画效果。以圆球ball为例,当圆心x坐标小于半径时,圆球就超出了左边界,圆心x坐标继续变小则圆球左边的部分会超出画布,导致无法显示。边界检测的处理一般包括边界限制、边界环绕、边界生成、边界反弹四种方式。
1.3 Canvas主要应用
Canvas在前端项目中应用十分广泛,主要应用场景有:
(1)视频弹幕[6]:通过将Canvas置于视频组件上方进行遮罩,实现弹幕信息在视频上方的展示,如图1.2所示。
图1.2 Canvas实现视频弹幕效果图
(2)图片与视频前端像素处理[7]:通过将图片与视频导入Cancas进行绘制,使图片和视频每一帧的像素变得可操作,进而实现视频绿幕替换、图像裁切、美化等像素操作,如图1.3所示。
图1.3 Canvas实现图片与视频前端像素处理效果图
(3)前端动画效果[8]:通过js脚本控制图形绘制,实现前端渲染的网页2D动画,如图1.4所示。
图1.4 Canvas实现前端动画效果图
(4)交互式图表与图形[1]:利用Canvas提供的图形与文字绘制功能,架构出图表模板,实现用户提供数据即可自动绘制出整个图表,并且可以进一步通过鼠标与键盘等事件的监听实现交互式的图表与图形,如图1.5所示。
图1.5 Canvas实现交互式图表与图形效果图
(5)2D游戏[9]:Canvas实现的2D游戏是对图形绘制、动画效果、事件交互做的一整套融合,也是前端2D游戏的主流实现方案,如图1.6所示。
图1.6 Canvas实现2D游戏效果图
工欲善其事,必先利其器。1.1节中介绍了Canvas提供的各类原生API,若想利用这些原生API直接开发前端应用,在效率、性能、程序的健壮、后期的维护等方面都将面临很大的挑战。一个合适的“Canvas库”是大型应用开发的关键。国内开源的Canvas库主要有百度开发的ZRender与蚂蚁集团开发的G[10],前者是数据可视化工具Echarts的渲染引擎,后者则是基础图表类库G2[11]的渲染引擎,这些库的“老大哥”则可以追溯到纽约时报一名工程师开源的数据可视化库D3[12],类似的库还有Kinetic.JS[12]和EaselJS[13]。
ZRender相较于其他库,在使用上,采用数据驱动的方式,相对简单易懂;在功能上,ZRender提供完整的事件封装、高效的分层刷新、丰富的图形选项与强大的动画支持;在扩展性上,ZRender支持任意基础图形元素的拓展,并支持SVG和XML的渲染方式。这些特性使得ZRender备受开发者青睐。
为了帮助读者能更快了解ZRender具体做了什么,本章内容将从几个简单例子出发,介绍ZRender的具体用法。
2.1 在项目中引入ZRender
在项目中引入ZRender主要有npm安装与直接下载两种方式。
(1)npm安装:
控制台输入指令‘npm install zrender’进行安装,无需其他操作。安装之后在代码中通过‘import zrender from "zrender"’指令引入zrender模块进行使用
(2)直接下载:
ZRender 项目在 GitHub 上的地址是https://github.com/ecomfe/zrender,下载源文件后,我们可以在源代码中的dist目录找到zrender.js和zrender.min.js,前者是开发版,后者是发布版。
下载之后,需要在 HTML 中加载对应 JavaScript 文件:
<script src="./dist/zrender.js"></script>
在使用 ZRender 前需要初始化实例,具体方式是传入一个 DOM 容器:
var zr = zrender.init(document.getElementById('main'));
该初始化方法会在浏览器DOM中生成Canvas标签并进行基础设置,之后就可在JavaScript代码中使用ZRender,调用相关API,绘制出的所有图形元素都会在DOM容器下的Canvas标签中显示出来。
2.2 通过ZRender添加元素到画布
//创建圆圈元素
var circle = new zrender.Circle({
draggable: true, //可拖拽
shape: {
cx: 150, //圆心x坐标
cy: 50, //圆心y坐标
r: 40 //半径
},
style: {
fill: 'black', //填充色
stroke: '#F00' //边框色
},
textContent: new zrender.Text({ //内部文字
style: {
fill: 'white',
text: 'Circle'
}
}),
textConfig: {
position: 'inside' //文字位置
}
});
//添加至Canvas画布
zr.add(circle);
//通过attr()方法修改元素属性
circle.attr('shape', {
r: 50 //只更新r。cx、cy将保持不变。
});
ZRender不仅对Canvas支持的点、线、圆、文字、图片等元素进行了封装,还封装了20多种图形元素可供用户使用,如:水滴、心形、正多边形等。
2.3 添加事件监听
ZRender支持直接向元素添加事件,还支持对整块画布进行事件添加:
//为圆圈元素添加事件监听
circle.on('click',function(e){
//圆圈元素点击事件处理
console.log(e)
});
//或者直接给zr整块画布添加事件绑定
zr.on('click',function(e){
//画布点击事件处理,e中包含点击的元素,点击位置等信息
console.log(e)
});
ZRender提供元素独立事件监听的支持,可帮助开发者更自如的掌控画布上的元素。
ZRender支持的事件共有:'click'、'mousedown'、'mouseup'、'mousewheel'、 'dblclick'、'contextmenu'。
2.4 添加动画效果
ZRender内部所有元素都支持独立的动画添加。这里说的“动画”可以理解为元素属性的变化,例如元素位置属性发生变化产生位移、大小属性发生变化产生形变、颜色属性发生变化产生颜色渐变……。以下列代码为例,我们可以为上面创建的圆添加一个在屏幕内循环位移的效果。
circle.animate('', true) //第二个参数为true代表循环该动画
.when(1000, { //为动画定义关键帧,此处表示1000ms时的状态
position: [200, 0] //输入元素属性,通过动画从当前属性变换至该属性
})
.when(2000, {
position: [200, 200]
})
.when(3000, {
position: [0, 200]
})
.when(4000, {
position: [0, 0] //最终回到初始位移,保证循环动画的流畅性
})
.start(); //立刻开始该动画
2.5 元素组合
G(https://g.antv.vision/zh/docs/guide/introduce)、Kinetic.JS(http://kineticjs.com/)和EaselJS(http://www.createjs.com/#!/EaselJS)这些Canvas库都提供元素的组合功能,元素组合的作用是:对元素组合的移动,缩放等操作,会映射到每一个子元素上。ZRender同样提供元素组合的底层支持,我们可以通过下列方法添加元素组合:
var g = new zrender.Group();
g.position[0] = 100;
g.position[1] = 100;
g.add(circle);
zr.add(g);
2.6 元素包围盒BoundingRect
ZRender每一个元素都能获得其在画布上的“包围盒”。
var rect = circle.getBoundingRect() //获取元素的包围盒
boundingRect = new zrender.Rect({ //将包围盒以虚线形式展示到画布上
draggable: true,
shape: {
x: rect.x-3,
y: rect.y-3,
width: rect.width+6,
height: rect.height+6
},
style: {
fill: "rgba(255,255,255,0)",
stroke: "rgba(0,0,0,1)",
lineWidth: 1,
lineDash:[3] //虚线间隔
}
});
zr.add(boundingRect);
为了支持文字、各种形状和元素组合的包围盒呈现,且满足经过各种变换后元素包围盒位置的准确,ZRender中为每一个元素维护了一个transition矩阵,通过矩阵进行位置计算保障元素在任何状态下包围盒的准确展示,如图2.1所示。
图2.1 ZRender中Circle元素的包围盒
元素组合与包围盒的引入,让在线文档、在线编辑器、在线白板等应用的开发变得更为方便快捷。
3.1 总体结构
ZRender是一个开源的“轻量级”图形库,这便于开发者理解系统内部运行机制,但是ZRender只公开了使用文档而无开发文档。若想在ZRender基础上进一步开发,需要阶段性掌握其底层的原理。
图3.1 ZRender总体结构图
如图3.1[2]展示了ZRender的总体结构,ZRender采用的是MVC的架构模式,提到MVC模式读者一定不会陌生,ZRender中的M层指的是图形元素数据管理层、V层指的是前端视图绘制层、C层指的是元素事件控制层。此外,图中大圆圈包裹的部分是ZRender内部的核心组成部分,也是我们new一个ZRender实例包含的内容。图中外部的分支是ZRender暴露出来的方法,包括图形元素与工具方法等等,这些方法可供实例调用。
ZRender核心组成部分的介绍如下:
(1)Stroage(M) : shape数据CURD管理。
(2)Painter(V) : Canvas元素生命周期管理、视图渲染、绘画、更新控制。
(3)Handler(C) : 事件交互处理,实现完整dom事件模拟封装。
(4)shape : 图形实体,分而治之的图形策略,可扩展。
(5)tool : 绘画扩展相关使用方法,工具及脚手架。
(6)animation : 动画扩展,提供promise式的动画接口和常用缓动函数。
ZRender源码目录结构介绍如图3.2所示:
图3.2 ZRender源码目录结构
不难看出,ZRender源码对各个模块的封装做的十分完善。我们可以从ZRender的构造函数,看出ZRender的MVC管理机制:
class ZRender {
constructor(id: number, dom: HTMLElement, opts?: ZRenderInitOpt) {
opts = opts || {};
this.dom = dom;
this.id = id;
const storage = new Storage();
let rendererType = opts.renderer || 'canvas';
//...省略其他svg与vml渲染类型的配置代码
const painter = new painterCtors[rendererType](dom, storage, opts, id);
this.storage = storage;//绑定Model数据管理层
this.painter = painter;//绑定View视图层
const handerProxy = (!env.node && !env.worker)
? new HandlerProxy(painter.getViewportRoot(), painter.root)
: null;
this.handler = new Handler(storage, painter, handerProxy, painter.root);//绑定Controller控制层
this.animation = new Animation({
stage: {
update: () => this._flush(true)
}
});
this.animation.start();
}
}
3.2 Model数据管理层(Storage.js)
ZRender用Storage来存放图形元素,我们通过Storage类的构造函数来了解其内部组成:
class Storage {
private _roots: Element[] = [] //所有元素存放列表
private _displayList: Displayable[] = [] //要渲染的元素
private _displayListLen = 0 //要渲染的元素长度
//...省略部分增删改查代码
}
从上面代码可看出,Storage主要管理_roots与_displayList两个列表,这两个列表里面值分别是Element类的对象与Displayable类的对象,Stroage还负责对维护的元素进行增删改查等操作,我们可以将Storage理解成存放图形元素的“数据结构”。
Element类、Displayable类都是元素,他们之间到底有什么关系?要想弄清这个问题,我们只需要了解ZRender中各个元素类的继承关系,该继承关系如图3.3所示:
图3.3 ZRender元素类类图
为了实现元素之间的相互独立与代码复用,ZRender对元素进行了系统性的继承与封装,Element元素基础抽象类继承了Animatable、Transformable、Eventful三个有关动画、变换与事件的元素抽象类,为元素提供相关功能支持,Element类被Displayable类所继承,为元素提供显示相关的功能支持,最后,元素具体图形被封装在其独立的Shape类中。
3.3 V–视图层(Painter.js)
首先我们直接来查看ZRender用来进行Canvas渲染的Painter类:
class CanvasPainter implements PainterBase { //ZRender还支持SVG、VML
storage: Storage //数据元素列表
private _zlevelList: number[] = [] //不同Canvas画布zlevel列表
private _width: number //宽度
private _height: number //高度
private _domRoot: HTMLElement //依赖的dom节点
constructor(root: HTMLElement, storage: Storage, opts: CanvasPainterOption, id: number) {} //构造函数
refresh(paintAll?: boolean) {} //刷新,渲染所有displayable
private _doPaintList(list: Displayable[],prevList: Displayable[],paintAll?: boolean) {} //渲染dispalyable列表
private _doPaintEl(el: Displayable,currentLayer: Layer,useDirtyRect: boolean,repaintRect: BoundingRect,scope: BrushScope,isLast: boolean) {} //渲染单个元素,调用元素内部预定义的brush方法
refreshHover() {} //渲染遮罩层元素
risize() {} //区域大小变化后重绘
clear() {} //清除hover层外所有内容
}
Painter负责ZRender最繁重的部分——“绘图”的实现,从上文的代码可得知,Painter主要承担以下三方面的工作:
(1)负责Canvas及其周边DOM元素的创建与处理。
(2)负责调用各个Shape预定义好的brush方法进行绘制。
(3)提供基本的操作方法,渲染(_doPaintList、_doPaintEl)、刷新(refresh)、尺寸变化(resize)、擦除(clear)等。
绘制的具体过程如图3.4所示:
图3.4 ZRender元素绘制过程
Painter是调用canvas API实现的绘制,这些API包括颜色,渐变色,变换,矩阵变化,绘制图片、文本等。其中IE8使用exCanvas兼容。
3.4 Controller控制层(Handle.js)
Handler负责事件处理,事件类型包括'click', 'dblclick', 'mousewheel', 'mouseout', 'mouseup', 'mousedown', 'mousemove', 'contextmenu'。
class Handler extends Eventful {
storage: Storage
painter: PainterBase
painterRoot: HTMLElement
proxy: HandlerProxyInterface
constructor(storage: Storage,painter: PainterBase,proxy: HandlerProxyInterface,painterRoot: HTMLElement) {} //构造函数
setHandlerProxy(proxy: HandlerProxyInterface) {} //设置代理
dispatch(eventName: HandlerName, eventArgs?: any) //调度事件
dispose() {} //废弃事件
dispatchToElement(targetInfo: {target?: Element,topTarget?: Element}, eventName: ElementEventName, event: ZRRawEvent) {} //事件分发
mousemove(event: ZRRawEvent) {} //mousemove事件处理
mouseout(event: ZRRawEvent) {} //mouseout事件处理
click(event: ZRRawEvent) {} //click事件处理
mousedown(event: ZRRawEvent) {} //mousedown事件处理
mouseup(event: ZRRawEvent) {} //mouseup事件处理
mousewheel(event: ZRRawEvent) {} //mousewheel事件处理
dblclick(event: ZRRawEvent) {} //dblclick事件处理
contextmenu(event: ZRRawEvent) {} //contextmenu事件处理
}
canvas API没有提供监听画布元素的机制,使得用户的单击、拖动等鼠标时间无法判定作用在哪个元素上,这就需要一些处理:
以单击事件为例,处理的主要思路是:通过在Model层管理的元素列表中获取到每个元素的作用范围,判断点击坐标在哪个绘制元素的作用范围中,从而响应相关事件。若多个元素的作用范围都包含点击坐标,则返回level最高的元素。
本人在ZRender2.0版本(基于Javascript,最新版本基于TypeScript)的基础上进行了开发(代码地址:https://github.com/Heisenbilin/Srender),增加了画笔、视频等元素,并支持与前端交互式添加元素、支持元素的撤销、回溯等功能,并提供全部画布信息的结构化存储。
下面以视频元素为例,介绍ZRender中如何按需开发新元素。
4.1 为ZRender引入视频元素
1)Canvas中视频绘制原理
Canvas中可以使用ctx.drawImage(video, x, y,width,height)来对视频当前帧的图像进行绘制,其中video参数就是HTML5中的video标签。故我们可以通过Canvas的动态效果不断获取video当前画面,渲染到Canvas画布上。
在此基础上,我们可以通过改变原生video标签的属性,来对Canvas中绘制出的视频进行事件绑定与视频处理,完成对播放、进度条控制、图像处理等各类操作。
读者可以理解成:在Canvas中新建一个播放器,该播放器视频源是video标签创建,播放器的各种方法最终指向对video标签本身属性和方法的改变。在此基础上继续利用Canvas的强大功能,可以进行图像处理、弹幕加载等操作。
Canvas绘制视频的具体步骤:
第一步:创建video标签:
//创建Video标签
video = document.createElement('Video');
video.src = './video.ogv';
video.controls = true;
//将video标签插入dom结点,用于展示原始video。
//首先应该在body中有一个id为videodiv的div结点。
//在实战中不需要插入dom结点(不用添加下面两行语句)就可以使用上面创建的video对象
var videodiv=document.getElementById('videodiv');
videodiv.appendChild(video);
第二步:Canvas中不断绘制video标签内容:
render()
function render() {
window.requestAnimationFrame(render)
ctx.clearRect(0, 0,canvas.width,canvas.height)
ctx.drawImage(video, 0, 0,width,height) //绘制视频
}
2)ZRender添加视频Shape文件
在ZRender中添加新元素,可以结合3.2节中介绍的Model层原理来改写,该Video对象添加一个继承于Dispalyable抽象类的子类即可继承元素位置管理、变形、动画等共有属性与方法。
ZRender中默认支持Image元素,Video元素与Image元素类似,所以只需要对Image元素进行仿写,对Image元素的brush渲染方法进行修改:
Image元素brush核心渲染代码:
ctx.drawImage(image, x, y, width, height);
修改后的Video渲染代码:
render()
render:function(ctx,video) {
ctx.clearRect(x, y, width, height)
ctx.drawImage(video,x, y, width, height)
setTimeout(this.render.bind(this,ctx,video), 0)
},
在ZRender入口文件暴露出Video后,就可以往画布中添加Video元素:
var video = new zrender.Video({
style:{
videosrc:'./video.ogv',
width:600,
height:480
}
});
zr.add(video);
测试效果,如图4.1所示。
图4.1 通过ZRender绘制出的视频元素
这里添加的Video元素与其他元素一致,同样支持拖拽、放缩、事件添加等操作,将这些事件与Video标签提供的暂停、跳转等方法结合起来,还能实现对视频的播放、声音、图像等的控制。
5.1 ZRender适用场景
基于ZRender对基础元素优雅且功能完备的封装、对DOM事件的一整套代理、对元素动画的高性能支持,开发者能够以舒适的姿态开发小型、中型、甚至大型应用,具体的适用场景如下:
1)小型应用
小型应用专注于利用ZRender进行前端的数据可视化,这部分应用的开发者仅需要知晓ZRender提供的部分API就可以进行前端数据可视化应用的快速开发,开发者不需要了解Canvas的底层渲染原理,只需要掌握基础的坐标系统,再通过元素创建、元素动画等API就可以在前端中创建出想要创建的图形效果,典型应用示例如下图5.1所示。
图5.1 ZRender小型应用示例
2)中型应用
基于ZRender开发的中型应用不仅要提供数据的可视化功能,还关注绘制出的图形与用户的交互,这与开发者在Echarts上开发交互式的前端图表的思路有些类似。ZRender不仅能服务于类似于Echarts这样的图表交互页面的开发,利用其提供的动画功能与交互功能还能用于2D小游戏开发、在线白板、交互式弹幕组件开发(如图10.12所示),交互式卡通模型等中型应用。
图5.2 交互式弹幕组件示例
利用ZRender开发中型应用,不需要对元素位置判定、DOM事件绑定与监听、给元素添加动画等功能进行重复开发,因为ZRender对这些既重要又难度大的工作已经做好了封装,开发者只需要熟练掌握这些API,就能达到“开箱即用、业务专注”的效果。
3)大型应用
基于ZRender开发的大型应用往往不满足于ZRender提供的现有功能,需要在源代码的基础上进行进一步开发以满足更丰富的底层API支持。另一方面,这些大型应用往往从业务角度出发,对底层API进行了进一步封装,为用户提供更便捷、交互性强的交互方案。最后,不同于中小型应用针对具体业务的开发,大型应用中ZRender将服务于整个平台和系统,需要与其他技术融合开发。
ZRender早期是为Echarts而生,Echarts也是ZRender使用最为广泛的大型应用,在Echarts中,开发者将ZRender提供的底层元素封装成了面向用户的图表元素,用户只输入配置就能实现前端数据可视化。但系统角度来看,Echarts的大部分工作也仅仅对ZRender元素进行了业务封装,对其他系统提供的借鉴意义较为一般。
ZRender另一个主流的大型应用开发方向是“在线编辑器”,在线编辑器包括协同白板、协同PPT设计、UI原型设计、图片编辑器等等。这些大型应用不仅需要对ZRender元素进行业务封装,还需要融合WebSocket、缓存、数据库、性能优化等多个技术,并且对于某些ZRender不支持的特定业务(例如白板画笔),开发者还需要对ZRender底层代码进行进一步开发。得益于第三章中介绍的清晰的代码逻辑和优雅的模块封装,利用ZRender开发大型应用比其他Canvas库更容易上手。
对在线编辑器开发感兴趣的读者可以参考Cicada在线流程图(https://github.com/MarkMindCkm/cicada-flowchart)工具与本人参与架构开发的在线图形绘制小工具(https://github.com/Heisenbilin/Srender)。
5.2 ZRender开发注意事项
(1)ZRender对包围盒的功能支持欠缺:在线编辑器中,需要点击元素后显示出元素包围盒,以此判定是否选中该元素,并且该包围盒需要跟随元素进行拖拽与变形。但ZRender中提供的BoundingRect包围盒并不会随着包围盒内部元素的拖拽一起运动。这是因为ECharts中包围盒使用的很少,导致ZRender对包围盒的功能支持较为欠缺。
这个问题主要有两种解决方案,第一种方案是当点击事件触发后将元素与其包围盒封装成group,利用group特性共同拖拽与变形,并在失去焦点时删除包围盒Rect。另一种方案是根据需求重写BoundingRect以适应在线编辑器的包围盒需求。
(2)ZRedner不支持矩形内动态输入文字:在线编辑器中,经常需要进行文字实时录入,录入过程可描述为文本框创建与文字输入两部分,但ZRender并不支持矩形内动态输入文字。
若直接手写支持文字输入的文本框元素,是较大的工作量,且容易造成位置判定失败等冲突,建议用绝对定位的input标签悬停在输入框上方,只需要给文本框添加“创建悬停输入”事件就能实现,输入的数据再回传给画布进行渲染。
(3)ZRender无法胜任大型2D游戏引擎:这是由于ZRender中提供的基础元素适用于图表类应用,而大型2D游戏引擎一般需要更灵活完备的基础元素与更快速的渲染性能,开发2D游戏建议使用Unity等其他引擎。
[1] Echarts官网, https://echarts.apache.org/examples/zh/index.html
[2] ZRender官网, https://ecomfe.github.io/zrender-doc/public/
[3] 腾讯文档在线PPT, https://docs.qq.com/home/product#ppt
[4] ExplanEverything在线白板, https://whiteboard.explaineverything.com/
[5] 《HTML5 Canvas开发详解》, SteveFulton, 2014
[6] 基于Canvas+Vue的视频弹幕组件, https://juejin.cn/post/6844903893848047630
[7] 使用Canvas处理图像与视频, https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Manipulating_video_using_canvas
[8] ZRender绘制动画效果展示, https://ecomfe.github.io/zrender-doc/public/examples/particles.html
[9] Canvas2D过桥小游戏, https://animpen.com/pen/Y07mS6p7
[10] 2D可视化渲染引擎G, https://g.antv.vision/zh/docs/guide/introduce
[12] G2可视化引擎, https://g2.antv.vision/zh
[13] D3数据可视化库, https://d3js.org/
往期推荐
微信公众号
扫码关注我们 | @学而思网校技术团队