likes
comments
collection
share

释放KonvaJS的创造力:从画布到杰作

作者站长头像
站长
· 阅读数 7

1. Konva

konva是什么?根据官方文档描述:

Konva 是一个HTML5 Canvas JavaScript 框架,它通过对 2d context 的扩展实现了在桌面端和移动端的可交互性。Konva 提供了高性能的动画,补间,节点嵌套,布局,滤镜,缓存,事件绑定(桌面/移动端)等等功能。你可以使用 Konva 在舞台上绘制图形,给图形添加事件,移动、缩放和旋转图形并且支持高性能的动画即使包含数千个图形。 基础 | Konva 中文文档 中文API (bluehymn.com)

也就是说,konva是一个canvas库,方便我们在项目中快速构建canvas图形,以实现高质量的图形效果。下面我将演示konva的基础用法。

2. Konva工作原理

Konva 的对象是以一颗树的形式保存的,Konva.Stage 是树的根节点,Stage 子节点是用户创建的图层 (Konva.Layer)。

每一个 layer 有两个 <canvas> 渲染器: 场景渲染器 和 图像命中检测渲染器。场景渲染器输出你所看见的内容,图像命中渲染器在隐藏的 canvas 里用于高性能的检测事件。

图层可以包含图形、嵌套图形的组、嵌套组的组。Stage(舞台),layers(图层),groups(组),和 shapes(图形) 都是虚拟节点,类似于 HTML 的 DOM 节点。

节点结构图1-1:

              Stage
                |
         +------+------+
         |             |
       Layer         Layer
         |             |
   +-----+-----+     Shape
   |           |
 Group       Group
   |           |
   +       +---+---+
   |       |       |
Shape   Group    Shape
           |
           +
           |
         Shape

3. 创建stage

// (1)创建舞台stage[作为一个容器,承载图层layer]
var stage = new Konva.Stage({
    container: 'container',   // id of container <div>
    width: 500,
    height: 500
});

// (2)创建图层layer,用来盛放group&shape
var layer = new Konva.Layer();

// (3)创建组group,group可以盛放shape作为一个整体操作
var group = new Konva.Group({
    x: 0,
    y: 0,
    draggable: true
})

// (4)创建图形shape,图形作为konva中的最基本的单位
//    [如 rect/circle/ellipse/line/image/text等]
var circle = new Konva.Circle({
    x: stage.width() / 2,
    y: stage.height() / 2,
    radius: 70,
    fill: 'red',
    stroke: 'black',
    strokeWidth: 4
});

//(5)
// 添加circle到group中
layer.add(circle);

// 添加group到layer中
layer.add(circle);

// 最后添加layer到stage中
stage.add(layer);

// 执行绘制
layer.draw();

[注*]图层layer、组group、和图形shape可以有多个,可以不创建组,把图形直接添加到图层中,也可以有多个组,组和组可以是并列关系,也可以是嵌套关系。

4. 拖拽功能

托拽功能是本人最近项目使用konva的主要原因,我们项目想实现一个类似与华容道游戏的拖拽功能。

// (1)以rect为例,添加draggable属性并设置为true即可实现元素的拖拽,默认值false
// (2)然后给box[rect]添加事件监听,这样在触发拖拽、悬停等事件是可以监听到,并执行相应的回调
var box = new Konva.Rect({
    x: rectX,
    y: rectY,
    width: 100,
    height: 50,
    fill: '#00D2FF',
    draggable: true
});

// 鼠标悬停修改鼠标样式
box.on('mouseover', function() {
    document.body.style.cursor = 'pointer';
});
box.on('mouseout', function() {
    document.body.style.cursor = 'default';
});

可设置拖拽属性的元素有很多,比如图形、组、图层、甚至舞台都可以设置拖拽属性。合同拽相关的属性,还有一个拖拽区域,通常是限制在父级元素一定范围内拖动(也就是设置边界)。根据我的实践,图形的拖拽区域可以设置dragBoundFunc来实现,但是,组group是无法通过设置dragBoundFunc来实现拖拽区域限制的(虽然我看文档里面是支持的),通常是dragBoundFunc添加上去了,但是没有效果,因此我用另外的方式限制组的拖拽区域————事件绑定(dragmove/dragend)。

// (1)以下示例演示,text元素可以在水平区域拖拽
var vbox = new Konva.Text({
    x: 150,
    y: 70,
    draggable: true,
    fontSize: 24,
    fontFamily: 'Calibri',
    text: 'horizontal',
    fill: 'black',
    padding: 15,
    dragBoundFunc: function(pos) {
          return {
                x: pos.x,
                y: this.absolutePosition().y
          };
    }
 });
     
     
// (2)group的拖拽区域限制   
var group = new Konva.Group({
    x: 0,
    y: 0,
    draggable: true
})

// 根据group和其父元素的坐标、宽高,分别限制其对应的上下左右边界,
// 以达到限制区域的目的
group.on('dragmove', function (e) {
    var target = e.target
    var targetRect = e.target.getClientRect()
    
    // 限制左边界&上边界
    targetRect.x < 0 && target.setAttr('x', 0);
    targetRect.y < 0 && target.setAttr('y', 0);
    
    // 右边界&下边界
    // ...
})

!!!

划重点,关于拖拽重叠判断:

Konva对重叠的判断仅限与基础图形shape,并且通过shape.getClientRect()方法获取元素的外轮廓,该外轮廓会被简化成长方形(reat),即便原来的图形是圆形、椭圆、多边形。同理,group判断重叠也会被简化成长方体,因此导致重叠判断不准确。有待konva进一步完善,或者项目引入其他插件的帮忙,实现想要的效果(粗略知道有几种插件,还有待继续探索)。

// 启用元素拖拽 
rect.draggable(true); 

// 监听元素拖拽事件 
rect.on('dragmove', () => { 
    console.log('Rect is being dragged'); 
}); 

// 判断元素是否重叠 
const isOverlap = Konva.Util.haveIntersection(rect.getClientRect(), circle.getClientRect()); 
console.log('Rect and Circle are overlapping:', isOverlap);
  

5. 写在最后

Konva这小东西挺有意思,在一些特殊业务需求下显得十分好用,同时上手也不难,文档十分丰富,希望看懂本文的朋友们能激起一些学习兴趣,或者帮助你解决一些小问题,那便是这篇分享文的意义。

最后最后:我是菜鸟,轻点喷我,最好不喷,友好交流哈~同时以上内容除官方文档外都是本人短时间内浅显的理解,有不足之处欢迎朋友指出,必会第一时间改正。

彩蛋

chatGpt帮我写博客 ×

chatGpt帮我博客取标题 √

释放KonvaJS的创造力:从画布到杰作