前端图形学实战: 从零实现编辑器的图层管理面板和实时缩略图(vue3 + vite版)

简介: 前端图形学实战: 从零实现编辑器的图层管理面板和实时缩略图(vue3 + vite版)

前言

本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究

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

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

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

demo演示

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

image.png

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

源码地址: gitee.com/lowcode-chi…

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

技术实现

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

image.png

图层管理面板的实现

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

image.png

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

image.png

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

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

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

1. 构建图层面板

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

type shapeType = "rect" | "circle" | "line";
interface IBaseShapeProp {
  type: shapeType;
  key: string;
  style: any;
}
const canvasBox = ref<{ [key in shapeType]: IBaseShapeProp[] }>({
  rect: [],
  circle: [],
  line: [],
});

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

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

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

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

<div v-show="layerVisible" class="layerWrap">
  <h3>图层管理</h3>
  <div v-for="item in canvasBox.rect" :key="item.key" class="layerItem">
    <span @click.stop="handleSelected(item.key)">{{ item.key }}</span>
    <span @click="handleDelItem(item.key)"> 删除 </span>
  </div>
</div>

css样式如下:

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

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

image.png

由于我们应用是用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转换成base64
  • canvas标签直接转成xml是无法显示的, 所以我们需要将canvas转换成base64,再放入图片的src内

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

  • html2canvas
  • dom2image

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

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

yarn add dom-to-image

具体实现:

const pushRecordFn = (
  state: { [key in shapeType]: IBaseShapeProp[] },
  prevState: { [key in shapeType]: 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 缩略图功能, 演示如下:

image.png

后期规划

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

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


目录
相关文章
|
18天前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
|
8天前
|
前端开发 JavaScript 开发工具
Vite 4.0 发布,下一代的前端工具链
【10月更文挑战第21天】Vite 4.0 的发布标志着前端开发领域的又一次重要进步。它为开发者带来了更高效、更智能、更具创新性的开发体验,正逐渐成为下一代前端工具链的引领者。
|
30天前
|
前端开发 JavaScript 关系型数据库
前端的全栈之路:基于 Vue3 + Nest.js 全栈开发的后台应用
这篇文章介绍了一个名为Vue3Admin的全栈后台应用,前端基于SoybeanAdmin二次开发,后端基于Nest.js。主要使用了Vue3.5、AntDesignVue、UnoCSS、Pinia等前端技术栈,以及Nest.js、PostgreSQL、Prisma等后端技术栈。文章详细描述了系统的功能设计,包括动态国际化语言配置、登录用户操作日志、用户和角色权限映射、动态路由菜单、消息公告发布及前端业务功能等。同时,也提供了项目运行所需的环境和依赖,以及如何拉取代码、安装依赖和启动项目的方法。最后,文章展示了项目的演示图,并对项目进行了总结,指出项目未经严格测试,仅供学习交流使用。
前端的全栈之路:基于 Vue3 + Nest.js 全栈开发的后台应用
|
11天前
|
监控 JavaScript 前端开发
前端的混合之路Meteor篇(六):发布订阅示例代码及如何将Meteor的响应数据映射到vue3的reactive系统
本文介绍了 Meteor 3.0 中的发布-订阅模型,详细讲解了如何在服务器端通过 `Meteor.publish` 发布数据,包括简单发布和自定义发布。客户端则通过 `Meteor.subscribe` 订阅数据,并使用 MiniMongo 实现实时数据同步。此外,还展示了如何在 Vue 3 中将 MiniMongo 的 `cursor` 转化为响应式数组,实现数据的自动更新。
|
19天前
|
前端开发 JavaScript
Vite 多种前端框架的构建
Vite 多种前端框架的构建
|
2月前
|
JavaScript 前端开发 API
vue3 v-md-editor markdown编辑器(VMdEditor)和预览组件(VMdPreview )的使用
本文介绍了如何在Vue 3项目中使用v-md-editor组件库来创建markdown编辑器和预览组件。文章提供了安装步骤、如何在main.js中进行全局配置、以及如何在页面中使用VMdEditor和VMdPreview组件的示例代码。此外,还提供了一个完整示例的链接,包括编辑器和预览组件的使用效果和代码。
vue3 v-md-editor markdown编辑器(VMdEditor)和预览组件(VMdPreview )的使用
|
17天前
|
资源调度 前端开发 JavaScript
Vite:新一代前端构建工具的革命性体验
【10月更文挑战第13天】Vite:新一代前端构建工具的革命性体验
|
17天前
|
前端开发 JavaScript 中间件
Vite:下一代前端构建工具的崛起
【10月更文挑战第13天】Vite:下一代前端构建工具的崛起
|
17天前
|
JSON 前端开发 JavaScript
Vite:新一代前端构建工具的崛起
【10月更文挑战第13天】Vite:新一代前端构建工具的崛起
|
3月前
|
前端开发 JavaScript
在 Vue3 + ElementPlus 项目中使用 computed 实现前端静态分页
本文介绍了在Vue3 + ElementPlus项目中使用`computed`属性实现前端静态分页的方法,并提供了详细的示例代码和运行效果。
152 1
在 Vue3 + ElementPlus 项目中使用 computed 实现前端静态分页