前端使用 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. 邮件安全概述 企业邮件安全涉及多个方面,旨在保护电子邮件通信的机密性、完整性和可用性,防止数据泄露、欺诈、滥用及其他安全威胁。本文从身份验证与防伪、数据加密、反垃圾邮件和反恶意软件防护、邮件内容过滤与审计、访问控制与权限管理、邮件存储…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
