前端使用 Konva 实现可视化设计器(18)- 素材嵌套 - 加载阶段
本章主要实现素材的嵌套(加载阶段)这意味着可以拖入画布的对象,不只是图片素材,还可以是嵌套的图片和图形。
请大家动动小手,给我一个免费的 Star 吧~
大家如果发现了 Bug,欢迎来提 Issue 哟~
github源码
gitee源码
示例地址
在原来的 drop 处理基础上,增加一个 json 类型素材的处理入口:
// src/Render/handlers/DragOutsideHandlers.tsdrop: (e: GlobalEventHandlersEventMap['drop']) => {// 略this.render.assetTool[type === 'svg'? `loadSvg`: type === 'gif'? 'loadGif': type === 'json'? 'loadJson' // 新增,处理 json 类型素材: 'loadImg'](src).then((target: Konva.Image | Konva.Group) => {// 图片素材if (target instanceof Konva.Image) {// 略} else {// json 素材target.id(nanoid())target.name('asset')group = targetthis.render.linkTool.groupIdCover(group)}})// 略
}
drop 原逻辑基本不变,关键逻辑在 loadJson 中:
// src/Render/tools/AssetTool.ts// 加载节点 jsonasync loadJson(src: string) {try {// 读取 json内容const json = JSON.parse(await (await fetch(src)).text())// 子素材const assets = json.children// 刷新idthis.render.linkTool.jsonIdCover(assets)// 生成空白 stage+layerconst stageEmpty = new Konva.Stage({container: document.createElement('div')})const layerEmpty = new Konva.Layer()stageEmpty.add(layerEmpty)// 空白 json 根const jsonRoot = JSON.parse(stageEmpty.toJSON())jsonRoot.children[0].children = [json]// 重新加载 stageconst stageReload = Konva.Node.create(JSON.stringify(jsonRoot), document.createElement('div'))// 目标 group(即 json 转化后的节点)const groupTarget = stageReload.children[0].children[0] as Konva.Group// 释放内存stageEmpty.destroy()groupTarget.remove()stageReload.destroy()// 深度遍历加载子素材const nodes: {target: Konva.Stage | Konva.Layer | Konva.Group | Konva.Nodeparent?: Konva.Stage | Konva.Layer | Konva.Group | Konva.Node}[] = [{ target: groupTarget }]while (nodes.length > 0) {const item = nodes.shift()if (item) {const node = item.targetif (node instanceof Konva.Image) {if (node.attrs.svgXML) {const n = await this.loadSvgXML(node.attrs.svgXML)n.listening(false)node.parent?.add(n)node.remove()} else if (node.attrs.gif) {const n = await this.loadGif(node.attrs.gif)n.listening(false)node.parent?.add(n)node.remove()} else if (node.attrs.src) {const n = await this.loadImg(node.attrs.src)n.listening(false)node.parent?.add(n)node.remove()}}if (node instanceof Konva.Stage ||node instanceof Konva.Layer ||node instanceof Konva.Group) {nodes.push(...node.getChildren().map((o) => ({target: o,parent: node})))}}}// 作用:点击空白区域可选择const clickMask = new Konva.Rect({id: 'click-mask',width: groupTarget.width(),height: groupTarget.height()})groupTarget.add(clickMask)clickMask.zIndex(1)return groupTarget} catch (e) {console.error(e)return new Konva.Group()}}
loadJson,关键逻辑说明:
1、jsonIdCover 把加载到的 json 内部的 id 们刷新一遍
2、借一个空 stage 得到一个 空 stage 的 json 结构(由于素材 json 只包含素材自身结构,需要补充上层 json 结构)
3、加载拼接好的 json,得到一个新 stage
4、从 3 的 stage 中提取目标素材 group
5、加载该 group 内部的图片素材
6、插入一个透明 Rect,使其点击 sub-asset 们之间的空白,也能选中整个 asset
最后,进行一次 linkTool.groupIdCover 处理:
// src/Render/tools/LinkTool.ts// 把深层 group 的 id 统一为顶层 group 的 idgroupIdCover(group: Konva.Group) {const groupId = group.id()const subGroups = group.find('.sub-asset') as Konva.Group[]while (subGroups.length > 0) {const subGroup = subGroups.shift() as Konva.Group | undefinedif (subGroup) {const points = subGroup.attrs.pointsif (Array.isArray(points)) {for (const point of points) {point.rawGroupId = point.groupIdpoint.groupId = groupIdfor (const pair of point.pairs) {pair.from.rawGroupId = pair.from.groupIdpair.from.groupId = groupIdpair.to.rawGroupId = pair.to.groupIdpair.to.groupId = groupId}}}subGroups.push(...(subGroup.find('.sub-asset') as Konva.Group[]))}}}
这里的逻辑就是把 顶层 asset 的新id,通过广度优先遍历,下发到下面所有的 point 和 pair 上,并保留原来的 groupId(上面的 rawGroupId)为日后备用。groupId 更新之后,在连接线算法执行的时候,会忽略同个 asset 下不同 sub-asset 的 pair 关系,即不会重复绘制内部不同 sub-asset 之间实时连接线(连接线在另存为素材 json 的时候,已经直接固化成 Line 实例了,往后将跟随 根 asset 行动,特别是 transform 变换)。
接着,因为这次的实现,内部属于各 sub-asset 的 point 依旧有效,首先,调整一下 pointsVisible,使其在 hover 根 asset 的时候,内部所有 point 都会显现:
// src/Render/tools/LinkTool.tspointsVisible(visible: boolean, group?: Konva.Group) {const start = group ?? this.render.layer// 查找深层 pointsfor (const asset of [...(['asset', 'sub-asset'].includes(start.name()) ? [start] : []),...start.find('.asset'),...start.find('.sub-asset')]) {const points = asset.getAttr('points') ?? []asset.setAttrs({points: points.map((o: any) => ({ ...o, visible }))})}// 重绘this.render.redraw()}
然后,关键要调整 LinkDraw:
// src/Render/draws/LinkDraw.tsoverride draw() {// 略// 所有层级的素材const groups = [...(this.render.layer.find('.asset') as Konva.Group[]),...(this.render.layer.find('.sub-asset') as Konva.Group[])]// 略const pairs = points.reduce((ps, point) => {return ps.concat(point.pairs ? point.pairs.filter((o) => !o.disabled) : [])}, [] as LinkDrawPair[])// 略// 连接线for (const pair of pairs) {// 多层素材,需要排除内部 pair 对// pair 也不能为 disabledif (pair.from.groupId !== pair.to.groupId && !pair.disabled) {// 略}}
}
1、groups 查询要增加包含 sub-asset
2、过滤掉 disabled 的 pair 纪录
3、过滤掉同 asset 的 pair 纪录
其他逻辑,基本不变。
至此,关于“素材嵌套”的逻辑基本已实现。
整体代码对比上个功能版本,改变的并不多,对之前的代码影响不大。
More Stars please!勾勾手指~
源码
gitee源码
示例地址
相关文章:
前端使用 Konva 实现可视化设计器(18)- 素材嵌套 - 加载阶段
本章主要实现素材的嵌套(加载阶段)这意味着可以拖入画布的对象,不只是图片素材,还可以是嵌套的图片和图形。 请大家动动小手,给我一个免费的 Star 吧~ 大家如果发现了 Bug,欢迎来提 Issue 哟~ github源码 g…...
vue3 -layui项目-左侧导航菜单栏
1.创建目录结构 进入cmd,先cd到项目目录(项目vue3-project) cd vue3-project mkdir -p src\\views\\home\\components\\menubar 2.创建组件文件 3.编辑menu-item-content.vue <template><template v-if"item.icon"><lay-ic…...
Spring AOP(1)
目录 一、AOP 概述 什么是Spring AOP? 二、Spring AOP 快速入门 1、引入AOP依赖 2、编写AOP程序 三、Spring AOP 详解 1、Spring AOP的核心概念 (1)切点(Pointcut) (2)连接点ÿ…...
第1关 -- Linux 基础知识
闯关任务 完成SSH连接与端口映射并运行hello_world.py ssh -p 37367 rootssh.intern-ai.org.cn -CNg -L 7860:127.0.0.1:7860 -o StrictHostKeyCheckingno可选任务 1 将Linux基础命令在开发机上完成一遍 可选任务 2 使用 VSCODE 远程连接开发机并创建一个conda环境 …...
tensorflow keras Model.fit returning: ValueError: Unrecognized data type
题意:TensorFlow Keras 的 Model.fit 方法返回了一个 ValueError,提示数据类型无法识别 问题背景: Im trying to train a keras model with 2 inputs: an image part thats a tf.data.Dataset and a nor mal part represented by a pd.DataF…...
虚拟机固定配置IP
在Hyper-V中,vEthernet (Default Switch) 是Hyper-V自带的默认虚拟交换机,它允许虚拟机直接连接到宿主机网络或外部网络。这个虚拟交换机可以通过Hyper-V管理器或PowerShell等工具进行管理和配置。以下是具体的操作步骤: 一、通过Hyper-V管理…...
【Pytorch实用教程】pytorch中random_split用法的详细介绍
在 PyTorch 中,torch.utils.data.random_split 是一个非常有用的函数,用于将数据集随机分割成多个子集。这在机器学习和深度学习中非常常见,特别是当你需要将数据集分割成训练集和测试集或验证集时。这里是 random_split 的详细用法介绍: 功能 random_split 用于随机地将…...
第二讲:NJ网络配置
Ethernet/IP网络拓扑结构 一. NJ EtherNet/IP 1、网络端口位置 NJ的CPU上面有两个RJ45的网络接口,其中一个是EtherNet/IP网络端口(另一个是EtherCAT的网络端口) 2、网络作用 如图所示,EtherNet/IP网络既可以做控制器与控制器之间的通信,也可以实现与上位机系统的对接通…...
pytorch中常见的模型3种组织方式 nn.Sequential(OrderedDict)
在nn.Sequential中嵌套OrderedDict组织网络,以对层进行命名 import torch import torch.nn as nn from collections import OrderedDictclass OrderedDictCNN(nn.Module):def __init__(self):super(OrderedDictCNN, self).__init__()# 使用 OrderedDict 定义网络层self.model …...
达梦数据库DM8-索引篇
目录 一、前景二、名词三、语法1、命令方式创建索引1.1 创建索引空间1.2.1 创建普通索引并指定索引数据空间1.2.2 另一种没验证,官方写法1.3 复合索引1.4 唯一索引1.5 位图索引1.6 函数索引 2、创建表时候创建索引3、可视化方式创建索引3.1 打开DM管理工具3.2 找到要…...
【中项】系统集成项目管理工程师-第4章 信息系统架构-4.5技术架构
前言:系统集成项目管理工程师专业,现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试,全称为“全国计算机与软件专业技术资格(水平)考试”&…...
随机梯度下降 (Stochastic Gradient Descent, SGD)
SGD 是梯度下降法的一种变体。与批量梯度下降法不同,SGD 在每次迭代中仅使用一个样本(或一个小批量样本)的梯度来更新参数。它能更快地更新参数,并且可以更容易地跳出局部最优解。 原理 SGD 的基本思想是通过在每次迭代中使用不…...
TDengine 3.3.2.0 发布:新增 UDT 及 Oracle、SQL Server 数据接入
经过数月的开发和完善,TDengine 3.3.2.0 版本终于问世了。这一版本中既有针对开源社区的功能优化,也有从企业级用户需求出发做出的功能调整。在开源版本中,我们增强了系统的灵活性和兼容性;而在企业级版本中,新增了关键…...
Ubuntu 24.04 LTS 无法打开Chrome浏览器
解决办法: 删除本地配置文件,再次点击Chrome图标,即可打开。 rm ~/.config/google-chrome/ -rf ref: Google chrome not opening in Ubuntu 22.04 LTS - Ask Ubuntu...
linux中RocketMQ安装(单机版)及springboot中的使用
文章目录 一、安装1.1、下载RocketMQ1.2、将下载包上传到linux中,然后解压1.3、修改runserver.sh的jvm参数大小(根据自己服务器配置来修改)1.4、启动mqnamesrv (类似于注册中心)1.5、修改runbroker.sh的jvm参数大小&am…...
亚信安全终端一体化解决方案入选应用创新典型案例
近日,由工业和信息化部信息中心主办的2024信息技术应用创新发展大会暨解决方案应用推广大会成功落幕,会上集中发布了一系列技术水平先进、应用效果突出、产业带动性强的信息技术创新工作成果。其中,亚信安全“终端一体化安全运营解决方案”在…...
Django视图与URLs路由详解
在Django Web框架中,视图(Views)和URLs路由(URL routing)是Web应用开发的核心概念。它们共同负责将用户的请求映射到相应的Python函数,并返回适当的响应。本篇博客将深入探讨Django的视图和URLs路由系统&am…...
怎么关闭 Windows 安全中心,手动关闭 Windows Defender 教程
Windows 安全中心(也称为 Windows Defender Security Center)是微软 Windows 操作系统内置的安全管理工具,用于监控和控制病毒防护、防火墙、应用和浏览器保护等安全功能。然而,在某些情况下,用户可能需要关闭 Windows…...
洛谷看不了别人主页怎么办
首先,我们先点进去 可以看到,看不了一点 那我们看向上方,就可以发现,我们那有个URL,选中 把光标插到n和/中间 把.cn删了,变成国际服 我们就可以看了 但是国际服还没搭建完,跳转的时候可能503&a…...
邮件安全篇:企业电子邮件安全涉及哪些方面?
1. 邮件安全概述 企业邮件安全涉及多个方面,旨在保护电子邮件通信的机密性、完整性和可用性,防止数据泄露、欺诈、滥用及其他安全威胁。本文从身份验证与防伪、数据加密、反垃圾邮件和反恶意软件防护、邮件内容过滤与审计、访问控制与权限管理、邮件存储…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...
算法打卡第18天
从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。 示例 1: 输入:inorder [9,3,15,20,7…...
aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...
