做图素材网站哪个好,深圳做品牌网站,手机优化怎么得100分,建立个人网站能干本章主要实现素材的嵌套#xff08;加载阶段#xff09;这意味着可以拖入画布的对象#xff0c;不只是图片素材#xff0c;还可以是嵌套的图片和图形。 请大家动动小手#xff0c;给我一个免费的 Star 吧~ 大家如果发现了 Bug#xff0c;欢迎来提 Issue 哟~ github源码 g…本章主要实现素材的嵌套加载阶段这意味着可以拖入画布的对象不只是图片素材还可以是嵌套的图片和图形。 请大家动动小手给我一个免费的 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)// 生成空白 stagelayerconst 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源码 示例地址