如何实现一款轻量级的可视化画布引擎
在很多定制化的可视化场景中,拖拽、缩放、全屏操作必不可少,尤其对于业务复杂的可视化需求,当画布内容足够多,可视区域显示不下时,就需要借助缩略图来一览全局。
本文将详情一款轻量级的画布引擎ReScreen,ReScreen是集缩略图与画布操作为一体的轻量级绘图引擎,为React专门定制,统一封装了画布的操作和缩略图功能,支持对画布的全屏、复位、显示所有、重置、平移缩放等常见功能。
基于ReScreen,我们搭建了一个通用编排场景的demo。
image.png
可以在使用了之后,再阅读下文,体感会更强。
ReScreen 详情
ReScreen具有如下的特性:
- 支持缩放、拖拽等基本功能
- 支持缩略图,提供缩略图的点击聚焦功能、缩略图的位置、大小、间距等样式
- 支持缩略图的自己设置传入,默认使用画布的缩小版
- 支持能否启动鼠标滚动缩放
- 支持缩放的范围设置
- 支持锁定某一方向的拖拽
- 支持控制按钮的自己设置
- 支持拖拽动画
- 画布内容为SVG,或者者原生DOM,目前暂未支持Canvas情况
ReScreen基于React与TypeScript开发,其属性参数为:
class Props { /** 画布内容的类型,默认为SVG */ type?: 'SVG' | 'DOM' | 'CANVAS'; /** 组件整体的尺寸,支持传入百分数 */ height?: number | string; width?: number | string; /** 能否启动鼠标滚动缩放画布,默认为true */ zoomEnabled?: boolean; /** 能否启动聚焦功能,0表示不启动,1表示单击触发,2表示双击触发 */ focusEnabled?: number; /** 缩放范围 */ minZoom?: number; maxZoom?: number; /** 拖拽方向的锁定,默认为ALL */ dragDirection?: 'ALL' | 'HOR' | 'VER'; /** 能否需要缩略图,默认为true */ needMinimap?: boolean; /** 支持自己设置传入缩略图组件 */ Minimap?: React.ReactElement<any>; /** 缩略图位置,默认为RT,右上角;-IN表示在画布的内部 */ mapPosition?: 'RT' | 'RB' | 'LT' | 'LB' | 'RT-IN' | 'RB-IN' | 'LT-IN' | 'LB-IN'; /** 缩略图和原图之间的大小,默认为20 */ mapPadding?: number; /** 缩略图大小,默认为100px */ mapWidth?: number; mapHeight?: number; /** 缩略图矩形的样式,svg语法 */ mapRectStyle?: object; /** 按钮组件,假如不需要就不传 */ Buttons?: React.ReactElement<any>; /** 因为画布元素的变化而引起的视图变化 */ needRefresh?: boolean; /** 通知外层重置needRefresh为false */ resetNeedRefresh?: () => void; /** 画布发生变化时的回调,对外暴露当前的缩放信息 */ onScreenChange?: (transform: ZoomTransform) => void; /** 对外暴露画布操作函数 */ getScreenHandler?: any;}实现原理详解
画布的缩放能力主要借助了d3-zoom,但整体上还是涉及少量数学计算。这里涉及包括画布上可以平移缩放操作,也包括在缩略图上进行反向的操作。缩略图采用的是DOM复制,每次画布操作发生变化时,就重新复制一份。当然也支持自己设置缩略图组件。
基本原理
d3-zoom 里将问题进行了如下的笼统,假设当前画布缩放系数为 k,x 轴平移偏移量在当前缩放系数下为 x ,y 轴平移偏移量在当前缩放系数下为 y,(k,x,y)在点(P0,P1)处进行缩放,需要求得缩放后的(k’,x’,y’),在 SVG 中的体现就是
<svg width="w" height="h"> <g transform="translate(x,y) scale(k)"> </g></svg>- 通过鼠标滚动操作/放大按钮等等,求得缩放系数 k’,在 d3.zoom 内置有缩放系数计算函数:
image.png
- 将(P0,P1)复原到(k,x,y)=(1,0,0)时的坐标:
image.png
- 因为(P0,P1)为缩放原点,它的偏移量是不变,由此求得 x’ 与 y’
image.png
对于拖拽问题,k 值是不变的,拖拽仅改变 x 与 y 值,这块就比较简单了,通过鼠标拖拽的起点与重点,能得到相应变化量,将变化量给予 x 与 y 就可。
实现步骤
整体主要分成如下几个步骤:
根据传入属性,计算(或者者直接获取)画布可视窗口的大小
screenWidth/screenHeight和缩略图的大小mapWidth/mapHeight;计算画布内容一律映射到缩略图中所需要的变化值
screenToMapTransform;监听画布的
zoom事件,用screenTransform记录画布当前的变换;监听缩略图可视矩形的
zoom事件,用minimapTransform记录矩形当前的变换;画布的最终缩放效果
transform = screenTransform * invert(minimapTransform),而缩略图可视矩形的变换为invert(transform);
具体涉及到的计算原理如下图:
image.png
image.png
image.png
image.png
这样就具有了最基本的缩略图缩放功能了。这里涉及到一个问题,当画布内容变化时,缩略图也需要适时地自适应,即重新获取一次screenToMapTransform。
我们再简单详情一下其余功能:
- 复位:将screenTransform和minimapTransform恢复到默认初始值,即设置为单位矩阵zoomIdentity。
- 重置:重置不仅具备复位的功能,还需要通知画布内部一切数据需要恢复到初始状态。
显示一律:这个也比较简单,跟screenToMapTransform的过程相似。
以画布中心缩放:假设P0为画布中心坐标,当前变换为transform,反求出在变换前P0的坐标P1;那么在新的transform1下,P1移到了P2,所以此时想要让画布的中心保持不变,就需要让画布平移P0 – P2的距离。如图所示:
image.png
总结
拖拽在可视化场景中是很常见的交互形式,想做好拖拽的交互其实不容易,社区里也有很多强大的可视化绘图引擎,如AntV的底层G引擎,提供了统一的渲染机制。
本文详情的ReScreen是一种基于React的轻量级绘图引擎方案,目的是借助React生态的能力,更大程度地帮助客户实现业务需求,客户仅仅只关注业务的开发,降低了学习成本,达到开箱即可使用的效果。
ReScreen 是ReGraph体系中的重要一环,ReGraph 是结合基础操作层、渲染交互层、布局算法层三层结构,针对数据领域图表(以数、图为基础数据结构,带有数据业务属性与特征的可视化图表)提供的处理方案。基于ReGraph体系,我们已经支持了多个领域图表场景的开发,比方复杂的DAG场景、ERGraph、服务编排场景等。目前ReGraph正在逐步完善与开放,假如您有兴趣,也欢迎提供宝贵的意见。
最后打一下广告:阿里巴巴数据技术及产品部-体验技术团队招大量高级前台开发工程师/前台技术专家,技术氛围好,大神多,妹纸也多。欢迎投递简历:perkin.pj@alibaba-inc.com
image.png
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 如何实现一款轻量级的可视化画布引擎