开发一个html渲染引擎,数据可视化初探-从零开始开发一个渲染引擎概述

相信大家在项目开发中都或多或少的用过一些图表,让我们再看一下一个简单的图表是什么样的。

e56adb4d4c85d811c2587c89b9fe6511.png

截图自EChart Demo

一个简单的条形图表都是由哪些基础图形组成的了?没错!正如看到的那样主要由线段和文字以及矩形组成。这三个基础图形组成了一个简单图表。然后对这三个基础图形进行划分,线段和文字为一组构成了坐标轴,所有矩形为一组构成了图表的主体。然后将坐标轴和主体放入一个画板中并绘制出来即形成了这个图表。 依据上面的描述并参考Photoshop绘图的理念可以提取一个简单的关系,如图:

1b081cb090c1dfa59f1172e50b3351cb.png

以上便是一个总体的结构设计划分,接下来只需实现相应的类即可。

Canvas类

作为所有图形表现的媒介,Canvas类必须有一个可供图形呈现到网页中的元素,故初始化需传入一个 html 元素来确定图表的渲染位置,并需要提供相应的配置来确定画布的大小。此外作为绘图的入口还需要负责绘画的统一调度,以及对相应事件的转发和模拟,否则作为HTMLCanvasElement中的一个图像到目前为止是不能原生响应相应的事件的,事件的订阅和派发放到下面单独介绍。

Canvas类具体配置和常用方法如下

配置

container: 画板的容器,canvas渲染到html中的位置

width: 画布宽度

height: 画布高度

style: 绘画的各种样式,具体设置项同原生API

主要方法

draw() 绘制图形。

addLayer(options) 添加图层

addShape(type, options) 添加图形

remove(element) 移除图形或图层

clear() 清除所有图层

update() 更新画布

例子

const canvas = new ZE.Canvas('container', {

width: 800,

height: 600,

style: {

fillStyle: 'red'

}

})

复制代码

EventBus类

为了让canvas中的元素可以获得单独事件响应的能力,故而需要实现了此类,它是Canvas的基类。这个类主要提供了订阅和分发事件的方法,还会记录每种事件都由哪些元素订阅,从而实现事件的冒泡机制。

EventBus类的主要方法

addEventListener(type, func, [element, once])添加订阅事件 (alias: on)

removeEventListener(type, [element, func])移除订阅事件 (alias: off)

once(type, func, [element])添加只执行一次的事件

trigger(type, [element, ...data))触发事件(alias: emit)

例子

canvas.on('test', (str) => { console.log(str) });

canvas.emit('test', '您好');

复制代码

Element类

何为Element?其实Layer和Shape都可以看成是一个元素,他们有许多相似之处,Layer只是包含了若干个Shape的元素,所以提取出了Element这个类,它也是整个引擎最核心的类。我们需要在这个类完成元素的属性设置、动画等相关方法。

Element类的属性和方法

方法

setAttrs(attr)设置属性(具体属性视具体shape而定)

setStyle(style)设置样式

animate(options)设置动画 (默认设置后自动播放)

play() 播放动画

stop() 停止动画

update() 更新画面,更改属性或样式后需要update才会更新画布内容

show() 显示图形

hide() 隐藏图形

destroy() 从画板中移除自己

getCanvas() 获取画板容器

getContext() 获取画笔

getStatus() 返回图形当前状态

属性

attrs 图形相关配置

style 图形相关样式

type 图形类型

container 图形的容器

zIndex 位置层级

computed 根据图形样式和属性计算后得到的一些值

例子

const ball = canvas.addShape('circle', {

attrs: {

x: 50, // 圆心x坐标

y: 50, // 圆心y坐标

r: 50 // 半径

},

style: {

fillStyle: 'red'

},

zIndex: 1, // 层级 默认 0

event: { // 可以直接再初始化时配置相应的事件

click (e) { console.log(e); }

}

});

复制代码

Layer类

Layer是继承Element的,经过Element的封装后需要在Layer里面完成的事情就不多了。Layer作为一个容器,主要当然要提供对元素的添加和删除了,此外个人为每一个Layer设计了一个图形的缓存,即每个Layer都拥有一个自己的缓存画板,当Canvas调用draw来进行绘画时,可以直接将缓存的图像绘制到显示的绘画板中,这样的初衷是为了提高绘画的效率(待验证),但会增加内存的使用。

Layer类的主要方法和属性

addLayer() 添加图层

addShape() 添加图形

remove() 移除一个子元素

clear() 清除子元素

palette 缓存的画板

brush 缓存的画笔

attrs 画板的属性

例子

const layer = canvas.addLayer({

x: 10,

y: 20

});

layer.addShape('text', {

attrs: {

text: 'hello'

}

});

复制代码

Shape类

Shape其实是具体图形的基类,它只是将每个图形都会用到方法提取到了这个公用的地方,比如图形样式的设置和绘制。

绘制其实分两个步骤:1.创建图形路径。2.调用画笔绘制到画板。Shape类只负责后者。

由于每种图形的实现各不相同,并且有的需要很多复杂的数学知识在此就不一一描述了,具体可以查看源码或者搜索查看这些基本图形的实现方法或者另写相应的帖子再介绍。总之每种图形最主要做的两件事是1:实现图形的路径绘制。2:判断一个坐标点是否包含在图形内。

一个初步的渲染引擎基本就差不多了,当然每个人对于各个类的划分可能不尽相同,并且对于各种机制的设计也千差万别,最后再次附上本项目的GitHub地址,欢迎大家fork。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值