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

一个基本的画布:

【展示】网格、比例尺

【交互】拖拽、缩放

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

最终效果:

基本思路:

设计区域 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>

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

通过 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 个,将很快更新下一篇章。

源码在这,望多多支持

前端使用 Konva 实现可视化设计器(1)的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. VS2015 android 设计器不能可视化问题解决。

    近期安装了VS2015,体验了一下android 的开发,按模板创建执行了个,试下效果非常不错.也能够可视化设计.但昨天再次打开或创建一个android程序后,设计界面直接不能显示,显示错误:(可能是 ...

  7. 可视化流程设计——流程设计器演示(基于Silverlight)

    上一篇文章<通用流程设计>对鄙人写的通用流程做了一定的介绍,并奉上了相关源码.但一个好的流程设计必少不了流程设计器的支持,本文将针对<通用流程设计>中的流程的设计器做一个简单的 ...

  8. Type Script 在流程设计器的落地实践

    流程设计器项目介绍 从事过BPM行业的大佬必然对流程建模工具非常熟悉,做为WFMC三大体系结构模型中的核心模块,它是工作流的能力模型,其他模块都围绕工作流定义来构建. 成熟的建模工具通过可视化的操作界 ...

  9. .net erp(办公oa)开发平台架构概要说明之表单设计器

    背景:搭建一个适合公司erp业务的开发平台.   架构概要图: 表单设计开发部署示例图    表单设计开发部署示例说明1)每个开发人员可以自己部署表单设计至本地一份(当然也可以共用一套开发环境,但是如 ...

  10. 解析大型.NET ERP系统核心组件 查询设计器 报表设计器 窗体设计器 工作流设计器 任务计划设计器

    企业管理软件包含一些公共的组件,这些基础的组件在每个新项目立项阶段就必须考虑.核心的稳定不变功能,方便系统开发与维护,也为系统二次开发提供了诸多便利.比如通用权限管理系统,通用附件管理,通用查询等组件 ...

随机推荐

  1. win32-使用FillRect绘制具有渐变颜色的客户区域背景

    void OnEraseBkGnd(HWND hwnd) { /* Vars */ HDC dc; /* Standard Device Context; used to do the paintin ...

  2. win32 - 写入安全日志(AuthzRegisterSecurityEventSource和AuthzReportSecurityEvent)

    微软文档介绍说, 安全日志在其他两个重要方面与其他日志不同.首先,在默认配置中,它受到强大的访问控制列表(ACL)和特权检查的保护,这将可以读取其内容的个人的范围限制为本地系统,管理员和安全特权的持有 ...

  3. Qt三方库开发技术:QXlsx介绍、编译和使用

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  4. 尝试通过uniapp仿微信页面

    最近一直想弄一个app,然后刚好看到Uniapp这个技术,然后最近就用几个晚上琢磨了下: 先看下成果: 1.通讯页面,这个是通过插件uni-indexed-list 索引列表 进行改造过后:改造过程还 ...

  5. 【Azure 环境】使用 az ad group create 时候遇见 Insufficient privileges to complete the operation

    问题描述 使用China Azure,通过Azure CLI 创建AAD组报错,提示权限不足 Insufficient privileges to complete the operation # 使 ...

  6. 【Azure 应用服务】登录App Service 高级工具 Kudu站点的 Basic Auth 方式

    问题描述 从Azure App Service的页面中,直接跳转到高级管理工具Kudu站点(https://<your app service name>.scm.chinacloudsi ...

  7. C++ //常用算法 adjacent_find //查找相邻的重复元素

    1 //常用算法 adjacent_find 2 //查找相邻的重复元素 3 #include<iostream> 4 #include<string> 5 #include& ...

  8. 基于stm32的spi接口dma 数据收发实例解析

    一 前记 SPI接口平时用的比较少,再加上对CUBEMX不是很熟悉,这里踩了不少坑才把问题解决.针对遇到了不少问题,是要值得梳理一下了. 二 源码解析 1 SPI的DMA发送端配置: 2 主函数源码: ...

  9. 50HZ陷波器的原理和实物开发设计

    原理     陷波滤波器指的是一种可以在某一个频率点迅速衰减输入信号,以达到阻碍此频率信号通过的滤波效果的滤波器.陷波滤波器属于带阻滤波器的一种,只是它的阻带非常狭窄,起阶数必须是二阶(含二阶)以上. ...

  10. 一个简单的RTMP服务器实现 --- RTMP实现要点

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...