首页 > 其他分享 >前端使用 Konva 实现可视化设计器(1)

前端使用 Konva 实现可视化设计器(1)

时间:2024-04-05 09:33:22浏览次数:27  
标签:const name Konva scaleY cellSize 可视化 前端 stage

使用 konva 实现一个设计器交互,首先考虑实现设计器的画布。

一个基本的画布:

【展示】网格、比例尺

【交互】拖拽、缩放

“拖拽”是无尽的,“缩放”是基于鼠标焦点的。

最终效果:

image

image

image

基本思路:

设计区域 HTML 由两个节点构成,内层挂载一个 Konva.stage 作为画布的开始。

<template>
  <div class="page">
    <header></header>
    <section>
      <header></header>
      <section ref="boardElement">
        <div ref="stageElement"></div>
      </section>
      <footer></footer>
    </section>
    <footer></footer>
  </div>
</template>

image

Konva.stage 暂时先设计3个 Konva.Layer,分别用于绘制背景、所有素材、比例尺。

image

通过 ResizeObserver 使 Konva.stage 的大小与外层 boardElement 保持一致。

为了显示“比例尺” Konva.stage 默认会偏移一些距离,这里定义“比例尺”尺寸为 40px。

    this.stage = new Konva.Stage({
      container: stageEle,
      x: this.rulerSize,
      y: this.rulerSize,
      width: config.width,
      height: config.height
    })

关于“网格背景”,是按照当前设计区域大小、缩放大小、偏移量,计算横向、纵向分别需要绘制多少条 Konva.Line(横向、纵向分别多加1条),同时根据 Konva.stage 的 x,y 进行偏移,用有限的 Konva.Line 模拟无限的网格画布。

      // 格子大小
      const cellSize = this.option.size
      //
      const width = this.stage.width()
      const height = this.stage.height()
      const scaleX = this.stage.scaleX()
      const scaleY = this.stage.scaleY()
      const stageX = this.stage.x()
      const stageY = this.stage.y()

      // 列数
      const lenX = Math.ceil(width / scaleX / cellSize)
      // 行数
      const lenY = Math.ceil(height / scaleY / cellSize)

      const startX = -Math.ceil(stageX / scaleX / cellSize)
      const startY = -Math.ceil(stageY / scaleY / cellSize)

      const group = new Konva.Group()

      group.add(
        new Konva.Rect({
          name: this.constructor.name,
          x: 0,
          y: 0,
          width: width,
          height: height,
          stroke: 'rgba(255,0,0,0.1)',
          strokeWidth: 2 / scaleY,
          listening: false,
          dash: [4, 4]
        })
      )

      // 竖线
      for (let x = startX; x < lenX + startX + 1; x++) {
        group.add(
          new Konva.Line({
            name: this.constructor.name,
            points: _.flatten([
              [cellSize * x, -stageY / scaleY],
              [cellSize * x, (height - stageY) / scaleY]
            ]),
            stroke: '#ddd',
            strokeWidth: 1 / scaleY,
            listening: false
          })
        )
      }

      // 横线
      for (let y = startY; y < lenY + startY + 1; y++) {
        group.add(
          new Konva.Line({
            name: this.constructor.name,
            points: _.flatten([
              [-stageX / scaleX, cellSize * y],
              [(width - stageX) / scaleX, cellSize * y]
            ]),
            stroke: '#ddd',
            strokeWidth: 1 / scaleX,
            listening: false
          })
        )
      }

      this.group.add(group)

关于“比例尺”,与“网格背景”思路差不多,在绘制“刻度”和“数值”的时候相对麻烦一些,例如绘制“数值”的时候,需要动态判断应该使用多大的字体。

              let fontSize = fontSizeMax

              const text = new Konva.Text({
                name: this.constructor.name,
                y: this.option.size / scaleY / 2 - fontSize / scaleY,
                text: (x * cellSize).toString(),
                fontSize: fontSize / scaleY,
                fill: '#999',
                align: 'center',
                verticalAlign: 'bottom',
                lineHeight: 1.6
              })

              while (text.width() / scaleY > (cellSize / scaleY) * 4.6) {
                fontSize -= 1
                text.fontSize(fontSize / scaleY)
                text.y(this.option.size / scaleY / 2 - fontSize / scaleY)
              }
              text.x(nx - text.width() / 2)

关于“拖拽”,这里设计的是通过鼠标右键拖拽画布,通过记录 mousedown 时 Konva.stage 起始位置、鼠标位置,mousemove 时将鼠标位置偏移与Konva.stage 起始位置计算最新的 Konva.stage 的位置即可。

      mousedown: (e: Konva.KonvaEventObject<GlobalEventHandlersEventMap['mousedown']>) => {
        if (e.evt.button === Types.MouseButton.右键) {
          // 鼠标右键
          this.mousedownRight = true

          this.mousedownPosition = { x: this.render.stage.x(), y: this.render.stage.y() }
          const pos = this.render.stage.getPointerPosition()
          if (pos) {
            this.mousedownPointerPosition = { x: pos.x, y: pos.y }
          }

          document.body.style.cursor = 'pointer'
        }
      },
      mouseup: () => {
        this.mousedownRight = false

        document.body.style.cursor = 'default'
      },
      mousemove: () => {
        if (this.mousedownRight) {
          // 鼠标右键拖动
          const pos = this.render.stage.getPointerPosition()
          if (pos) {
            const offsetX = pos.x - this.mousedownPointerPosition.x
            const offsetY = pos.y - this.mousedownPointerPosition.y
            this.render.stage.position({
              x: this.mousedownPosition.x + offsetX,
              y: this.mousedownPosition.y + offsetY
            })

            // 更新背景
            this.render.draws[Draws.BgDraw.name].draw()
            // 更新比例尺
            this.render.draws[Draws.RulerDraw.name].draw()
          }
        }
      }

关于“缩放”,可以参考 konva 官网的缩放示例,思路是差不多的,只是根据实际情况调整了逻辑。

接下来,计划增加下面功能:

  • 坐标参考线
  • 从左侧图片素材拖入节点
  • 鼠标、键盘移动节点
  • 鼠标、键盘单选、多选节点
  • 键盘复制、粘贴
  • 节点层次单个、批量调整
  • 等等。。。

如果 github Star 能超过 20 个,将很快更新下一篇章。

源码在这,望多多支持

标签:const,name,Konva,scaleY,cellSize,可视化,前端,stage
From: https://www.cnblogs.com/xachary/p/18115479

相关文章

  • 59.html+css网页设计实例/“美食”主题中华美食介绍/web前端期末大作业/
    一、前言  本实例以“美食”角色为主题设计,应用div+css布局、代码简单,为学生初等水平,是个不错的模板,供大家参考。【关注作者|获取更多源码(2000+个Web案例源码)|优质文章】;您的支持是我创作的动力!【点赞收藏博文】,Web开发、课程设计、毕业设计有兴趣的联系我交流分享,3Q!二......
  • 网页前端之html表单相关属性
                      表单input标签和表单相关属性        学习过HTML的朋友都会了解到,想要制作一个表单,我们首先要有一个最外层的容器来容纳我们用HTML所写的编程语句,所以今天我们所学的第一个HTML标签就是<form>标签。  ......
  • Python数据分析与可视化笔记 九 分类问题
    分类        分类是找出数据库中一组数据对象的共同特点,并按照分类模式将其划分为不同的类,其目的是通过分类模型,将数据库中的数据项映射到某个给定的类别。        分类学习是一类监督学习的问题,训练数据会包含其分类结果,根据分类结果分为以下几种问题。1.......
  • javascript常见100问|前端基础知识|问ajax-fetch-axios-区别请用 XMLHttpRequestfetch
    00-开始前端基础知识HTMLCSSJSHTTP等基础知识是前端面试的第一步,基础知识不过关将直接被拒。本章将通过多个面试题,讲解前端常考的基础知识面试题,同时复习一些重要的知识点。为何要考察扎实的前端基础知识,是作为前端工程师的根本。基础知识能保证最基本的使用,即招聘......
  • 前端学习<四>JavaScript基础——03-常量和变量
    常量(字面量):数字和字符串常量也称之为“字面量”,是固定值,不可改变。看见什么,它就是什么。常量有下面这几种:数字常量(数值常量)字符串常量布尔常量自定义常量数字常量数字常量非常简单,直接写数字就行,不需要任何其他的符号。既可以是整数,也可以是浮点数。例如: //不......
  • Python快速入门系列-8(Python数据分析与可视化)
    第八章:Python数据分析与可视化8.1数据处理与清洗8.1.1数据加载与查看8.1.2数据清洗与处理8.1.3数据转换与整理8.2数据可视化工具介绍8.2.1Matplotlib8.2.2Seaborn8.2.3Plotly8.3数据挖掘与机器学习简介8.3.1Scikit-learn8.3.2TensorFl......
  • 基于python的豆瓣电影数据的可视化与分析
    1项目背景意义介绍    电影是一种具有极高娱乐性和文化价值的艺术形式,自从电影产业诞生以来,已经成为了人们生活中的重要组成部分。电影产业在全球范围内都有着广泛的影响力,对经济、文化、社会等多个方面都起到了积极的作用。因此,对电影产业进行数据分析和可视化,可以帮......
  • (某网站)评论爬虫+wordcloud可视化
    目录一、序二、没变化的三、没怎么变的四、全牛魔变了的五、全代码六、后记,但没完全后记七,词云图一、序打正大杯的时候,需要面向女性群体的信息收集,当时想到爬xhs相关笔记评论的数据本着面向csdn编程的心态,蒟蒻在csdn上狂搜各类“某网站爬虫”,什么“某网站 爬虫”,......
  • 前端学习<四>JavaScript基础——01-编程语言和JavaScript简介
    计算机语言概念计算机语言:人与计算机之间通信的语言。它是人与计算机之间传递信息的媒介,它通过特定的语法规则和语义约定,将人类可理解的指令转化为计算机可以执行的机器指令。计算机程序:就是计算机所执行的一系列的指令集合,程序全部都是用我们所掌握的语言来编写的,如果我们......
  • 前端学习<四>JavaScript基础——02-JavaScript入门:hello world
    开始写第一行JavaScript:helloworldJS代码的书写位置在哪里呢?这个问题,也可以理解成:引入JS代码,有哪几种方式?有三种方式:(和CSS的引入方式类似)行内式:写在HTML标签内部。内嵌式(内联式):写在script标签中。外链式:引入外部JS文件。方式1:行内式代码举例: <inputty......