前言

hello, 大家好, 我是徐小夕, 今天又到了我们的博学时间。

本文是 100+前端几何学应用案例 专栏的第四篇文章, 之前和大家分享了如何从零实现几何画板以及几何画板的撤销重做功能:


(资料图片仅供参考)

几何学在前端边界计算中的应用和原理分析前端图形学实战: 从零开发几何画板(vue3 + vite版)前端图形学实战: 100行代码实现几何画板的撤销重做等功能(vue3 + vite版)

今天继续和大家分享一下几何画板的图层管理和实时缩略图的实现。

demo演示

按照笔者的写作习惯, 这里先和大家演示一下实现的效果:

2022-11-14 12.35.12.gif

可以看到通过操作图层面板我们可以轻松的切换到某一个元素并对元素进行编辑, 同时在每次操作之后右下角的缩略图会实时展示画布最新的变动。

源码地址: https://gitee.com/lowcode-china/euryd

接下来就让我们接着之前的内容, 来实现我们的图层管理面板和实时缩略图。

技术实现

接下来我还是用大家最最熟悉的 vue3 + ts 来实现, 其他框架实现原理类似, 感兴趣的朋友也可以举一反三, 自行实现。

image.png
图层管理面板的实现

图层管理面板主要是为了更方便管理和操作画布中的元素, 比如 PhotoShop 里的图层管理:

image.png

或者 H5-Dooring 页面制作平台的图层面板:

image.png

我们可以从这些编辑器中总结出图层管理的几个主要功能:

定位或切换元素显示隐藏元素编辑元素(如删除)批量操作(如多选批量删除元素等)调整元素位置(顺序)

所以说我们在设计图层面板的时候也可以考虑以上几个点, 接下来我就来构建一下图层面板, 并实现切换元素,删除指定元素 的功能。

1. 构建图层面板

由于图层面板的元素和画布实际的元素数据是一一对应的, 所以我们可以直接用 canvasBox 来渲染图层列表, 这里回顾一下 canvasBox 的数据结构:

typeshapeType="rect"|"circle"|"line";interfaceIBaseShapeProp{type:shapeType;key:string;style:any;}constcanvasBox=ref<{[keyinshapeType]:IBaseShapeProp[]}>({rect:[],circle:[],line:[],});

其中每个元素都包含如下三个关键属性:

key 元素的唯一idtype 元素的类型(矩形, 圆形, 线等)style 元素的样式

这样我们就可以利用 key 来轻松的定位元素, 如果画布中元素很多(比如复杂的设计稿), 我们还可以给图层面板添加搜索和分类功能, 方便我们更高效的定位元素。

一个简单实现的案例如下:

图层管理

{{ item.key }} 删除

css样式如下:

.layerWrap{position:absolute;left:60px;margin-top:-20px;padding-top:10px;padding-bottom:10px;width:160px;background:#fff;box-shadow:0010pxrgba(0,0,0,0.1);color:#888;.layerItem{&:hover{background-color:rgba(110,38,236,0.1);}span:last-child{margin-left:20px;}}}

这里分享一下具体实现效果:

2022-11-14 14.25.31.gif

由于我们应用是用vue3的组合式函数写的, 上图中涉及到的切换元素和删除元素的方法也很简单, 具体如下:

import { ref } from "vue";const curSelect = ref("");const canvasBox = ref<{ [key in shapeType]: IBaseShapeProp[] }>({  rect: [],  circle: [],  line: [],});// 选择元素const handleSelected = (key: string) => {  curSelect.value = key;};// 删除元素const handleDelItem = (key: string) => {  canvasBox.value.rect = canvasBox.value.rect.filter((v) => v.key !== key);};

所以说图层管理的本质是基于已有的图元进行数据结构层面的操作。

当然大家也可以扩展我们的画板应用, 让它支持多选, 搜索, 排列顺序等功能。

实时缩略图的实现

我们之前也许看过一些网站在浏览页面的时候会出现小的缩略图, 可以实时展示当前页面的情况, 比如:

image.png

这里就简单和大家分享一下实现方案。

image.png

因为我们在画布中的每一次操作都会被记录在 recordManager (记录管理器, 也就是上篇文章介绍的撤销重做的历史快照集合)中, 我们只需要在每次操作后基于当前 dom 生成一张图片即可(画布如果是canvas实现的, miniMap实现起来会更简单)。

所以说我们现在的问题就变成了如何基于 dom 生成图片快照的问题了, 当然这里也有解决方案, 核心思路就是将 dom 转换成 xml 结构,然后放在标签内,借助 svg 的处理能力将 dom 结构转换成 svg 标签,然后将svg标签作为图片的 base64 地址,最后用 a 标签实现下载。不过需要注意以下两个细节:

img标签的地址必须是base64字符串, 所以我们需要用canvas转换成base64canvas标签直接转成xml是无法显示的, 所以我们需要将canvas转换成base64,再放入图片的src内

通过以上方式我们就可以原生实现将 dom 转换为图片。当然市面上也有比较成熟的方案, 比如:

html2canvasdom2image

那这里我就用 dom2image 带大家一起实现一下 miniMap。

首先我们在vite 工程中安装该库:

yarnadddom-to-image

具体实现:

constpushRecordFn=(state:{[keyinshapeType]:IBaseShapeProp[]},prevState:{[keyinshapeType]:IBaseShapeProp[]})=>{//生成mini缩略图片domtoimage.toPng(boardDom?.value?.boardDom).then(function(dataUrl:string){miniImg.value=dataUrl;}).catch(function(error:Error){console.error("脚本错误!",error);});const{snapshots,maxLimit,curIndex}=recordManager.value;//如果两个状态相同,则不推入历史记录if(!diff(state,snapshots[curIndex])){return;}//如果在撤销的过程中重新执行了新的操作,则覆盖上一个状态if(snapshots.length-1!==curIndex){snapshots.splice(curIndex+1,snapshots.length);}//超过了最大限制记录if(snapshots.length>=maxLimit){snapshots.shift();}recordManager.value.snapshots.push(cloneDeep(state));recordManager.value.curIndex=recordManager.value.snapshots.length-1;};

pushRecordFn 函数就是我们之前在实现撤销重做功能的快照记录函数, 如果大家对撤销重做功能感兴趣的可以参考我的文章:

前端图形学实战: 100行代码实现几何画板的撤销重做等功能(vue3 + vite版)

好了, 以上就实现了我们的miniMap 缩略图功能, 演示如下:

2022-11-14 15.15.04.gif
后期规划

后面会继续围绕图形可视化来实现更多有意思的应用, 比如滑动验证码, 图形编辑器, 可视化图表等, 如果大家感兴趣, 可以参考我的github: https://gitee.com/lowcode-china/euryd

如果文章对你有帮助, 欢迎点赞评论, 让我们一起探索真正的前端技术。

如果想学习更多H5游戏,webpack,node,gulp,css3,javascript,nodeJS,canvas数据可视化等前端知识和实战,欢迎在《趣谈前端》加入我们的技术群一起学习讨论,共同探索前端的边界。

从零搭建全栈可视化大屏制作平台V6.Dooring

从零设计可视化大屏搭建引擎

Dooring可视化搭建平台数据源设计剖析

可视化搭建的一些思考和实践

基于Koa + React + TS从零开发全栈文档编辑器(进阶实战

点个在看你最好看

推荐内容