首页 > 其他分享 >前端使用 Konva 实现可视化设计器(7)- 导入导出、上一步、下一步

前端使用 Konva 实现可视化设计器(7)- 导入导出、上一步、下一步

时间:2024-04-24 22:35:11浏览次数:26  
标签:node layer const 一步 Konva 可视化 image copy 节点

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

这一章实现导入导出为JSON文件、另存为图片、上一步、下一步。

github源码

gitee源码

示例地址

image

导出为JSON文件

提取需要导出的内容

  getView() {
    // 复制画布
    const copy = this.render.stage.clone()
    // 提取 main layer 备用
    const main = copy.find('#main')[0] as Konva.Layer
    // 暂时清空所有 layer
    copy.removeChildren()

    // 提取节点
    let nodes = main.getChildren((node) => {
      return !this.render.ignore(node) && !this.render.ignoreDraw(node)
    })

    // 重新装载节点
    const layer = new Konva.Layer()
    layer.add(...nodes)
    nodes = layer.getChildren()

    // 计算节点占用的区域
    let minX = 0
    let maxX = copy.width()
    let minY = 0
    let maxY = copy.height()
    for (const node of nodes) {
      const x = node.x()
      const y = node.y()
      const width = node.width()
      const height = node.height()

      if (x < minX) {
        minX = x
      }
      if (x + width > maxX) {
        maxX = x + width
      }
      if (y < minY) {
        minY = y
      }
      if (y + height > maxY) {
        maxY = y + height
      }

      if (node.attrs.nodeMousedownPos) {
        // 修正正在选中的节点透明度
        node.setAttrs({
          opacity: copy.attrs.lastOpacity ?? 1
        })
      }
    }

    // 重新装载 layer
    copy.add(layer)

    // 节点占用的区域
    copy.setAttrs({
      x: -minX,
      y: -minY,
      scale: { x: 1, y: 1 },
      width: maxX - minX,
      height: maxY - minY
    })

    // 返回可视节点和 layer
    return copy
  }

1、首先复制一份画布
2、取出 main layer
3、筛选目标节点
4、计算目标节点占用区域
5、调整拷贝画布的位置和大小

导出 JSON

使用 stage 的 toJSON 即可。

  // 保存
  save() {
    const copy = this.getView()

    // 通过 stage api 导出 json
    return copy.toJSON()
  }

导入 JSON,恢复画布

相比导出,过程会比较复杂一些。

恢复节点结构

  // 恢复
  async restore(json: string, silent = false) {
    try {
      // 清空选择
      this.render.selectionTool.selectingClear()

      // 清空 main layer 节点
      this.render.layer.removeChildren()

      // 加载 json,提取节点
      const container = document.createElement('div')
      const stage = Konva.Node.create(json, container)
      const main = stage.getChildren()[0]
      const nodes = main.getChildren()

      // 恢复节点图片素材
      await this.restoreImage(nodes)

      // 往 main layer 插入新节点
      this.render.layer.add(...nodes)

      // 上一步、下一步 无需更新 history 记录
      if (!silent) {
        // 更新历史
        this.render.updateHistory()
      }
    } catch (e) {
      console.error(e)
    }
  }

1、清空选择
2、清空 main layer 节点
3、创建临时 stage
4、通过 Konva.Node.create 恢复 JSON 定义的节点结构
5、恢复图片素材(关键)

恢复图片素材

  // 加载 image(用于导入)
  loadImage(src: string) {
    return new Promise<HTMLImageElement | null>((resolve) => {
      const img = new Image()
      img.onload = () => {
        // 返回加载完成的图片 element
        resolve(img)
      }
      img.onerror = () => {
        resolve(null)
      }
      img.src = src
    })
  }
  // 恢复图片(用于导入)
  async restoreImage(nodes: Konva.Node[] = []) {
    for (const node of nodes) {
      if (node instanceof Konva.Group) {
        // 递归
        await this.restoreImage(node.getChildren())
      } else if (node instanceof Konva.Image) {
        // 处理图片
        if (node.attrs.svgXML) {
          // svg 素材
          const blob = new Blob([node.attrs.svgXML], { type: 'image/svg+xml' })
          // dataurl
          const url = URL.createObjectURL(blob)
          // 加载为图片 element
          const image = await this.loadImage(url)
          if (image) {
            // 设置图片
            node.image(image)
          }
        } else if (node.attrs.gif) {
          // gif 素材
          const imageNode = await this.render.assetTool.loadGif(node.attrs.gif)
          if (imageNode) {
            // 设置图片
            node.image(imageNode.image())
          }
        } else if (node.attrs.src) {
          // 其他图片素材
          const image = await this.loadImage(node.attrs.src)
          if (image) {
            // 设置图片
            node.image(image)
          }
        }
      }
    }
  }

关于恢复 svg,关键在于拖入 svg 的时候,记录了完整的 svg xml 在属性 svgXML 中。

关于恢复 gif、其他图片,拖入的时候记录其 src 地址,就可以恢复到节点中。

上一步、下一步

其实就是需要记录历史记录

历史记录

  history: string[] = []
  historyIndex = -1

  updateHistory() {
    this.history.splice(this.historyIndex + 1)
    this.history.push(this.importExportTool.save())
    this.historyIndex = this.history.length - 1
    // 历史变化事件
    this.config.on?.historyChange?.(_.clone(this.history), this.historyIndex)
  }

1、从当前历史位置,舍弃后面的记录
2、从当前历史位置,覆盖最新的 JSON 记录
3、更新位置
4、暴露事件(用于外部判断历史状态,以此禁用、启用上一步、下一步)

更新历史记录

一切会产生变动的位置都执行 updateHistory,如拖入素材、移动节点、改变节点位置、改变节点大小、复制粘贴节点、删除节点、改变节点的层次。具体代码就不贴了,只是在影响的地方执行一句:

this.render.updateHistory()

上一步、下一步方法

  prevHistory() {
    const record = this.history[this.historyIndex - 1]
    if (record) {
      this.importExportTool.restore(record, true)
      this.historyIndex--
      // 历史变化事件
      this.config.on?.historyChange?.(_.clone(this.history), this.historyIndex)
    }
  }

  nextHistory() {
    const record = this.history[this.historyIndex + 1]
    if (record) {
      this.importExportTool.restore(record, true)
      this.historyIndex++
      // 历史变化事件
      this.config.on?.historyChange?.(_.clone(this.history), this.historyIndex)
    }
  }

另存为图片

  // 获取图片
  getImage(pixelRatio = 1, bgColor?: string) {
    // 获取可视节点和 layer
    const copy = this.getView()

    // 背景层
    const bgLayer = new Konva.Layer()

    // 背景矩形
    const bg = new Konva.Rect({
      listening: false
    })
    bg.setAttrs({
      x: -copy.x(),
      y: -copy.y(),
      width: copy.width(),
      height: copy.height(),
      fill: bgColor
    })

    // 添加背景
    bgLayer.add(bg)

    // 插入背景
    const children = copy.getChildren()
    copy.removeChildren()
    copy.add(bgLayer)
    copy.add(children[0], ...children.slice(1))

    // 通过 stage api 导出图片
    return copy.toDataURL({ pixelRatio })
  }

主要关注有2点:
1、插入背景层
2、设置导出图片的尺寸

导出的时候,其实就是把当前矢量、非矢量素材统一输出为非矢量的图片,设置导出图片的尺寸越大,可以保留更多的矢量素材细节。

接下来,计划实现下面这些功能:

  • 实时预览窗
  • 对齐效果
  • 连接线
  • 等等。。。

是不是值得更多的 Star 呢?勾勾手指~

源码

gitee源码

示例地址

标签:node,layer,const,一步,Konva,可视化,image,copy,节点
From: https://www.cnblogs.com/xachary/p/18156521

相关文章

  • R语言建立和可视化混合效应模型mixed effect model|附代码数据
    全文下载链接:http://tecdat.cn/?p=20631最近我们被客户要求撰写关于混合效应模型的研究报告,包括一些图形和统计输出我们已经学习了如何处理混合效应模型。本文的重点是如何建立和_可视化_ 混合效应模型的结果设置本文使用数据集,用于探索草食动物种群对珊瑚覆盖的影响。 ......
  • 沉浸式观赛新风尚:足球场体育馆三维可视化技术引领潮流
    在数字化浪潮席卷全球的今天,三维可视化技术正以其独特的魅力引领着体育场馆建设的革新潮流。这一技术的出现,不仅为观众带来了前所未有的视觉享受,更在体育产业的发展中,开启了一扇通往未来的大门。 足球场体育馆三维可视化,就是将足球场体育馆的实体结构通过三维建模技术,以数字化......
  • R语言随机森林RandomForest、逻辑回归Logisitc预测心脏病数据和可视化分析|附代码数据
    全文链接:http://tecdat.cn/?p=22596最近我们被客户要求撰写关于预测心脏病的研究报告,包括一些图形和统计输出。本报告是对心脏研究的机器学习/数据科学调查分析。更具体地说,我们的目标是在心脏研究的数据集上建立一些预测模型,并建立探索性和建模方法。但什么是心脏研究?研究大纲......
  • 【视频】N-Gram、逻辑回归反欺诈模型文本分析招聘网站欺诈可视化|附数据代码
    原文链接:https://tecdat.cn/?p=36028原文出处:拓端数据部落公众号随着互联网的快速发展,招聘网站已成为求职者与雇主之间的重要桥梁。然而,随之而来的欺诈行为也日益猖獗,给求职者带来了极大的困扰和风险。因此,如何帮助客户有效地识别和防范招聘网站上的欺诈行为,已成为一个亟待解决......
  • 与开源数据可视化平台深度融合,进入流程办公新时代!
    进入新时代,需要有新的软件平台实现创新智造。开源数据可视化平台是流行于各中小型企业中的快速框架软件平台,够灵活、易操作、好维护、可视化操作界面等多个优势特点,在降本增效、减少成本支出、实现流程化办公等方面具有事半功倍的应用价值和效果。流辰信息作为专业的服务商,将不遗......
  • 【rust】《Rust深度学习[3]-数据可视化(Plotters)》
    什么是Plotters?Plotters是一个用纯Rust开发的图形库,用于中渲染图形、图表和数据可视化。它支持静态图片渲染和实时渲染,并支持多种后端,包括:位图格式(png、bmp、gif等)、矢量图(svg)、窗口和HTML5Canvas。Plotters对不同后端使用统一的高级API,并允许开发者自定义坐标系。在Plotters......
  • 领略未来办公魅力:办公楼3D可视化技术解析
    在数字化浪潮的推动下,办公楼的设计和管理也在经历着前所未有的变革。 想象一下,你站在一个虚拟的办公楼前,手指轻轻一点,就能深入其中,看到每一层、每一间办公室的布局,甚至每一个设备的运行状态。这不仅仅是一个简单的画面展示,更是一种全新的办公体验和管理方式的革新。 3D可视......
  • NET6 Hangfire 可视化配置
    Nuget<PackageReferenceInclude="Hangfire"Version="1.8.5"/><PackageReferenceInclude="Hangfire.AspNetCore"Version="1.8.5"/><PackageReferenceInclude="Hangfire.Console"Version="1......
  • 解锁化学密码:元素周期表可视化的魅力之旅
    你是否曾经被复杂的元素周期表所困扰,想要一窥化学世界的奥秘却又无从下手?如今,随着科技的进步,我们有了更直观、更生动的方式来探索这个神秘的领域。 元素周期表可视化,就是将传统的元素周期表以图形化的方式呈现出来。它不再是一张单调乏味的表格,而是一个充满活力、色彩斑斓的世......
  • YOLO可视化界面
    此版本的可视化界面比较麻烦,需要安装gpu版的torch和torchvision包(1)将可视化代码下载到本地文件夹中并解压;(2)在终端激活yolov8的运行环境,可通过anaconda终端激活或pycharm终端进入。任意一种方式进入终端后,依次运行以下三条命令进行依赖库安装pipinstall-rrequirements.txt......