本章主要实现素材的嵌套(加载阶段)这意味着可以拖入画布的对象,不只是图片素材,还可以是嵌套的图片和图形。

请大家动动小手,给我一个免费的 Star 吧~

大家如果发现了 Bug,欢迎来提 Issue 哟~

github源码

gitee源码

示例地址

在原来的 drop 处理基础上,增加一个 json 类型素材的处理入口:

// src/Render/handlers/DragOutsideHandlers.ts

drop: (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 = target
this.render.linkTool.groupIdCover(group)
}
})
// 略
}

drop 原逻辑基本不变,关键逻辑在 loadJson 中:

// src/Render/tools/AssetTool.ts

  // 加载节点 json
async loadJson(src: string) {
try {
// 读取 json内容
const json = JSON.parse(await (await fetch(src)).text()) // 子素材
const assets = json.children // 刷新id
this.render.linkTool.jsonIdCover(assets) // 生成空白 stage+layer
const 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] // 重新加载 stage
const 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.Node
parent?: Konva.Stage | Konva.Layer | Konva.Group | Konva.Node
}[] = [{ target: groupTarget }] while (nodes.length > 0) {
const item = nodes.shift()
if (item) {
const node = item.target
if (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 的 id
groupIdCover(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 | undefined
if (subGroup) {
const points = subGroup.attrs.points
if (Array.isArray(points)) {
for (const point of points) {
point.rawGroupId = point.groupId
point.groupId = groupId
for (const pair of point.pairs) {
pair.from.rawGroupId = pair.from.groupId
pair.from.groupId = groupId
pair.to.rawGroupId = pair.to.groupId
pair.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.ts

  pointsVisible(visible: boolean, group?: Konva.Group) {
const start = group ?? this.render.layer // 查找深层 points
for (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.ts

override 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 也不能为 disabled
if (pair.from.groupId !== pair.to.groupId && !pair.disabled) {
// 略
}
}
}

1、groups 查询要增加包含 sub-asset

2、过滤掉 disabled 的 pair 纪录

3、过滤掉同 asset 的 pair 纪录

其他逻辑,基本不变。

至此,关于“素材嵌套”的逻辑基本已实现。

整体代码对比上个功能版本,改变的并不多,对之前的代码影响不大。

More Stars please!勾勾手指~

源码

gitee源码

示例地址

前端使用 Konva 实现可视化设计器(18)- 素材嵌套 - 加载阶段的更多相关文章

  1. python数据可视化-matplotlib入门(7)-从网络加载数据及数据可视化的小总结

    除了从文件加载数据,另一个数据源是互联网,互联网每天产生各种不同的数据,可以用各种各样的方式从互联网加载数据. 一.了解 Web API Web 应用编程接口(API)自动请求网站的特定信息,再对这些 ...

  2. 惊闻企业Web应用生成平台 活字格 V4.0 免费了,不单可视化设计器免费,服务器也免费!

    官网消息: 针对活字格开发者,新版本完全免费!您可下载活字格 Web 应用生成平台 V4.0 Updated 1,方便的创建各类 Web 应用系统,任意部署,永不过期. 我之前学习过活字格,也曾经向用 ...

  3. 前端性能优化成神之路--vue组件懒加载(Vue Lazy Component )

    ---恢复内容开始--- 使用组件懒加载的原因 我们先来看看这样的一个页面,页面由大量模块组成,所有模块是同时进行加载,模块中图片内容较多,每个模块的依赖资源较多(包括js文件.接口文件.css文件等 ...

  4. .NET创建宿主设计器--DesignHost、DesignSurface.

    一个窗口在运行时,是这样的: 但是,在设计时,却远比这复杂的多,它需要一个设计器对象:它仅存在于设计时,并连接到运行时存在的对象.   宿主容器 我们可以看到每个窗体和按钮均有与之相关的设计器.这两个 ...

  5. XAML 设计器已意外退出。(退出代码: e0434352)

    一.前言 开门见山,这个问题我遇到过两次,第一次因为项目刚开始不长时间,我查了很长时间都没解决,然后就直接重写了,几乎一样的写法,但问题没复现了,但程序员思维告诉我,一定还是有比较关键的地方出现了问题 ...

  6. (原创)【B4A】一步一步入门02:可视化界面设计器、控件的使用

    一.前言 上篇 (原创)[B4A]一步一步入门01:简介.开发环境搭建.HelloWorld 中我们创建了默认的项目,现在我们来看一下B4A项目的构成,以及如何所见即所得的设计界面,并添加和使用自带的 ...

  7. Windows Phone 十二、设计器同步

    在设计阶段为页面添加数据源 Blend或者VS的可视化设计器会跑我们的代码,然后来显示出来,当我们Build之后,设计器会进入页面的构造函数,调用InitializeComponent();方法来将U ...

  8. WinForms项目升级.Net Core 3.0之后,没有WinForm设计器?

    目录 .NET Conf 2019 Window Forms 设计器 .NET Conf 2019 2019 9.23-9.25召开了 .NET Conf 2019 大会,大会宣布了 .Net Cor ...

  9. 阿里无线前端性能优化指南 (Pt.1 加载优化)

    前言 阿里无线前端团队在过去一年对所负责业务进行了全面的性能优化.以下是我们根据实际经验总结的优化指南,希望对大家有所帮助. 第一部分仅包括数据加载期优化. 图片控制 对于网页特别是电商类页面来说,图 ...

  10. ActiveReports 9 新功能:可视化查询设计器(VQD)介绍

    在最新发布的ActiveReports 9报表控件中添加了多项新功能,以帮助你在更短的时间里创建外观绚丽.功能强大的报表系统,本文将重点介绍可视化数据查询设计器,无需手动编写任何SQL语句,主要内容如 ...

随机推荐

  1. 数据结构(C++)--学习单链表时发现的一些小坑

    基于类的链表类无相应构造函数报错 #include<bits/stdc++.h> using namespace std; const int MaxSize = 10; template ...

  2. 鸿蒙极速入门(一)-HarmonyOS简介

    1.华为官网介绍 2.OpenHarmony开源项目 3.技术架构 内核层 内核子系统:采用多内核(Linux内核或者LiteOS)设计,支持针对不同资源受限设备选用适合的OS内核 驱动子系统:驱动框 ...

  3. Deepin15.11+WIN10 双系统安装过程与遇到的问题(一)

    一.deepin安装流程 1.下载 下载深度系统最新版本官网https://www.deepin.org/zh/download/下载深度系统专用U盘启动盘制作工具https://www.deepin ...

  4. monaco-editor 实现SQL编辑器

    原文链接:https://www.yuque.com/sxd_panda/antv/editor 安装 yarn add monaco-editor 或 npm install monaco-edit ...

  5. 机器学习策略篇:详解进行误差分析(Carrying out error analysis)

    从一个例子开始讲吧. 假设正在调试猫分类器,然后取得了90%准确率,相当于10%错误,,开发集上做到这样,这离希望的目标还有很远.也许的队员看了一下算法分类出错的例子,注意到算法将一些狗分类为猫,看看 ...

  6. C#.NET 逐行读取TXT文本

    C#.NET 逐行读取TXT文本 using System; using System.IO; class Program { static void Main() { string filePath ...

  7. ASP.NET MVC 查询加分页

    使用了LinqKit.PagedList.Mvc.EntityFramework 等DLL 直接使用nuget安装即可. 1.表模型: using System.ComponentModel.Data ...

  8. Do not access Object.prototype method 'hasOwnProperty' from target object

    hasOwnProperty 判断对象是否为空 在使用 hasOwnProperty 判断对象是否为空时遇到了一下问题,总结一下 // Do not access Object.prototype m ...

  9. kettle从入门到精通 第七十课 ETL之kettle kettle数据校验,脏数据清洗轻松拿捏

    场景:输入在指定的错误(错误应涵盖数据类型不匹配的情况)行数内,trans不报错,但通过错误处理步骤捕捉,并记入文件,整个数据管线正常完成直至处理完最后一个输入行. 解决方案:使用步骤[数据检验]进行 ...

  10. FFmpeg如何将一个gif嵌入视频指定位置并指定显示时间

    背景 很简单的需求:我需要将一个gif嵌入到视频里面的指定位置,并要指定时间播放: 环境 windows11 64位专业版 ffmpeg version 2022-04-07-git-607ecc27 ...