前端图文实战:从零开始实现编辑器的图层管理面板和实时缩略图(vue3+vite版)
时间:2023-03-14 17:10:22
科技观察
前言今天继续给大家分享图层管理面板和实时缩略图的实现几何画板。demo显示,根据笔者的写作习惯,这里演示一下实现的效果:我们可以看到,我们可以很方便地通过操作图层面板,切换到某个元素并对元素进行编辑,同时,在每一个之后操作时,右下角缩略图按钮会实时显示画布的最新变化。源码地址:https://gitee.com/lowcode-china/euryd下面让我们按照前面的内容来实现我们的图层管理面板和实时缩略图。技术实现下面我就用大家最熟悉的vue3+ts来实现一下。其他框架的实现原理类似。有兴趣的朋友也可以举一反三,自己实现。图层管理面板的实现图层管理面板主要是为了更方便的对画布中的元素进行管理和操作,比如PhotoShop中的图层管理:或者H5-Dooring页面制作平台的图层面板:我们可以使用这些editors总结图层管理的几个主要功能:定位或切换元素、显示隐藏元素、编辑元素(如删除)、批量操作(如多选、批量删除元素等)、调整元素位置(顺序),所以我们在设计图层面板的时候也可以考虑以上几点,接下来我会建立一个图层面板,实现切换元素和删除指定元素的功能。1.构建图层面板由于图层面板的元素对应着画布的实际元素数据,我们可以直接使用canvasBox来渲染图层列表。这里回顾一下canvasBox的数据结构:typeshapeType="rect"|“圈子”|“线”;接口IBaseShapeProp{类型:shapeType;键:字符串;style:any;}constcanvasBox=ref<{[keyinshapeType]:IBaseShapeProp[]}>({rect:[],circle:[],line:[],});其中每个元素包含以下三个关键属性:唯一的idtype关键元素的元素类型(矩形、圆形、直线等)style元素的style以便我们可以通过key轻松定位元素。如果画布中的元素较多(比如复杂的设计稿),我们还可以在图层面板中加入搜索和分类功能,方便我们更高效的定位元素。一个简单的实现案例如下:.key"class="layerItem">{{item.key}}删除
css样式如下:.layerWrap{position:absolute;左:60px;边距顶部:-20px;填充顶部:10px;填充底部:10px;宽度:160px;背景:#fff;盒子阴影:0010pxrgba(0,0,0,0.1);颜色:#888;.layerItem{&:hover{背景颜色:rgba(110,38,236,0.1);}span:last-child{margin-left:20px;这里分享一下具体的实现效果:由于我们的应用是用vue3的组合函数写的,所以上图中涉及的切换元素和删除元素的方法也很简单,如下:import{ref}from"vue";constcurSelect=ref("");constcanvasBox=ref<{[shapeType中的键]:IBaseShapeProp[]}>({rect:[],circle:[],line:[],});//选择元素缺点thandleSelected=(key:string)=>{curSelect.value=key;};//删除元素consthandleDelItem=(key:string)=>{canvasBox.value.rect=canvasBox.value.rect.filter((v)=>v.key!==键);};所以层次管理的本质是在已有原语的基础上,在数据结构层次上进行操作。当然,你也可以扩展我们的画板应用,支持多选。搜索、排序顺序等功能。实时缩略图的实现我们可能看到有些网站在浏览页面时会显示小缩略图,可以实时显示当前页面的情况。例如:这里有一个简单的实现方案分享给大家。因为我们在canvas中做的每一个操作都会记录在recordManager(recordmanager,也就是上一篇介绍的undo和redo的历史快照集合)中,我们只需要在每次操作之后根据当前dom生成一个image即可operation只是一张图片(如果canvas用canvas实现,miniMap会更容易实现)。所以我们现在的问题就变成了如何根据dom生成图片快照。当然,这里也有解决办法。核心思想是将dom转化为xml结构,然后放到标签中。借助svg处理能力,将dom结构转换为svg标签,然后使用svg标签作为图片的base64地址,最后使用a标签进行下载。但是需要注意以下两个细节:img标签的地址必须是base64字符串,所以我们需要用canvas转成base64通过src中的上述方法,我们可以原生将dom转为图片。当然,市面上也有比较成熟的方案,比如:html2canvasdom2image,所以这里我就用dom2image带大家一起来实现miniMap。首先,我们在vite项目中安装库:yarnadddom-to-image具体实现:constpushRecordFn=(state:{[keyinshapeType]:IBaseShapeProp[]},prevState:{[keyinshapeType]:IBaseShapeProp[]})=>{//生成迷你缩略图domtoimage.toPng(boardDom?.value?.boardDom).then(function(dataUrl:string){miniImg.value=dataUrl;}).catch(function(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;};好了,上面已经实现了我们的miniMap缩略图功能,演示如下: