konvajs提供了一系列接口,快捷便利得生成canvas图形,大大缩短了手搓canvas的开发工作量。
中文官网指路:文档 | Konva 中文文档 中文API
官网指路:
https://konvajs.org/api/Konva.Stage.html
安装
npm install konva
Konva 的对象是以一颗树的形式保存的,Konva.Stage
是树的根节点,Stage
子节点是用户创建的图层 (Konva.Layer
)。
每一个 layer 有两个 <canvas>
渲染器: 场景渲染器 和 图像命中检测渲染器。场景渲染器输出你所看见的内容,图像命中渲染器在隐藏的 canvas 里用于高性能的检测事件。
图层可以包含图形、嵌套图形的组、嵌套组的组。Stage
(舞台),layers
(图层),groups
(组),和 shapes
(图形) 都是虚拟节点,类似于 HTML 的 DOM 节点。
stage创建
let stage = new konva.Stage({
container: "container",//dom id
});
Layer创建
const mapLayer = useRef<konva.Layer>(); //定义
mapLayer.current = new konva.Layer();//创建实例
stageRef.current.add(mapLayer.current);//加入stage
外部Image引入
//roubotIconRef:承载图片的图层
konva.Image.fromURL(‘图片地址’, (image: konva.Image) => {
image.setAttrs({
width: 30,
height: 30,
});
if (roubotIconRef.current) {
roubotIconRef.current.add(image);
}
});
一些常用方法
1.调整元素参数:setAttrs
对于layer上的元素,可以通过setAttrs调整对应参数
通过该函数可以在元素初始化后操作元素
部分参数:
draggable | 是否可拖动 |
x | 元素对应stage左上角的横坐标 |
y | 元素对应stage左上角的纵坐标 |
scale | {x:0.5,y:0.6}--x:横向缩放;y:纵向缩放 |
width | 元素宽 |
height | 元素高 |
offset | {x:12,y:14} 相对于当前位置的横纵偏移量 |
rotation | 旋转角度 |
strokeWidth | 线的宽度 |
fill | 填充颜色 |
stroke | 线的颜色 |
closed | 线条是否为封闭 |
... |
可修改的参数为当时创建实例时的参数,不同实例有部分参数不同,可参考官网开发
例子:
robotImg.current.setAttrs({
x: 300,
y: 400,
scale: {
x: 0.5 / (stageRef?.current?.scaleX() || 1),
y: 0.5 / (stageRef?.current?.scaleX() || 1),
},
width: 35,
height: 35,
offset: { x: 12, y: 14 },
rotation: (90 / Math.PI) * 180,
});
2. 清空图层 destroyChildren
该函数可清空图层上的一切元素
roubotIconRef.current.destroyChildren();
3.向layer上增加元素add
可单添加也可批量加,后加入的层级高
add(元素1,元素2,元素3......)
roubotIconRef.current.add(image)
4.获取图层上的全部元素 getChildren
返回结果是一个元素数组,我们可以对每个元素进行指定操作
lineLayerRef.current.getChildren().forEach((item: any) => {
item.setAttrs({
strokeWidth: 1 / (stageRef?.current?.scaleX() || 1),
});
});
5.克隆已有实例clone
有时候会重复使用相同的实例,不必每次都去new一个新的,使用clone就可以克隆已有实例
使用场景:
- 重复使用一张图片,可初始就使用konva.Image.fromURL将图片加载好
- 同时生成大量实例,且大部分参数相同
let clone = image.clone({
x: 300,
y: 400,
});
6. imageSmoothingEnabled设置图片缓存
.imageSmoothingEnabled
是 Canvas 2D API 用来设置图片是否平滑的属性,true表示图片平滑(默认值),false表示图片不平滑。当我们获取 imageSmoothingEnabled
属性值时, 它会返回最新设置的值。
以缩放画布为例,这个属性对像素为主的游戏很有用。默认的改变大小的算法会造成图片模糊并且破坏图片原有的像素。 如果那样的话,设置属性值为false。
mapLayer.current.imageSmoothingEnabled(false);
事件监听
Konva 支持的事件类型有 mouseover
, mouseout
, mouseenter
, mouseleave
, mousemove
, mousedown
, mouseup
, wheel
, click
, dblclick
, dragstart
, dragmove
,dragend
。
Konva 支持的移动端事件类型有 touchstart
, touchmove
, touchend
, tap
, dbltap
, dragstart
, dragmove
, dragend
。
加入事件:
两种写法
factorLayerRef.current?.on("tap", function (evt) { });
stageRef.current
?.getContent()
.addEventListener("touchstart", function (evt: any) {
...
});
移除事件
factorLayerRef.current?.off("tap");
stageRef.current
?.getContent()
.removeEventListener("touchstart")
双指缩放
var lastDist = 0;
var lastCenter: any = null;
function getDistance(p1: any, p2: any) {
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}
function getCenter(p1: any, p2: any) {
return {
x: (p1.x + p2.x) / 2,
y: (p1.y + p2.y) / 2,
};
}
stageRef.current
?.getContent()
.addEventListener("touchmove", function (evt: any) {
evt.preventDefault();
var touch1 = evt.touches[0];
var touch2 = evt.touches[1];
if (touch1 && touch2) {
// if the stageRef.current? was under Konva's drag&drop
// we need to stop it, and implement our own pan logic with two pointers
if (stageRef.current?.isDragging()) {
stageRef.current?.stopDrag();
}
var p1 = {
x: touch1.clientX,
y: touch1.clientY,
};
var p2 = {
x: touch2.clientX,
y: touch2.clientY,
};
if (!lastCenter) {
lastCenter = getCenter(p1, p2);
return;
}
var newCenter = getCenter(p1, p2);
var dist = getDistance(p1, p2);
if (!lastDist) {
lastDist = dist;
}
// local coordinates of center point
var pointTo = {
x:
(newCenter.x - stageRef.current!.x()) /
stageRef.current!.scaleX(),
y:
(newCenter.y - stageRef.current!.y()) /
stageRef.current!.scaleX(),
};
var scale = stageRef.current!.scaleX() * (dist / lastDist);
stageRef.current!.scaleX(scale);
stageRef.current!.scaleY(scale);
// calculate new position of the stageRef.current!
var dx = newCenter.x - lastCenter.x;
var dy = newCenter.y - lastCenter.y;
var newPos = {
x: newCenter.x - pointTo.x * scale + dx,
y: newCenter.y - pointTo.y * scale + dy,
};
stageRef.current!.position(newPos);
lastDist = dist;
lastCenter = newCenter;
changeStage();
}
});
stageRef.current
?.getContent()
.addEventListener("touchend", function (evt: any) {
lastDist = 0;
lastCenter = null;
});
鼠标中心点缩放
let StageScale = useRef(1);
// 画布缩放
stageRef.current?.on("wheel", (e: any) => {
e.evt.preventDefault();
const pointerPos = stageRef.current!.getPointerPosition();
if (!pointerPos) return;
const oldScale = StageScale.current;
const delta = e.evt.deltaY > 0 ? -1 : 1;
StageScale.current += delta * 0.1;
StageScale.current = Math.max(0.1, Math.min(5, StageScale.current));
const newPos = {
x: (pointerPos.x - stageRef.current!.x()) / oldScale,
y: (pointerPos.y - stageRef.current!.y()) / oldScale,
};
stageRef.current!.x(-(newPos.x * StageScale.current - pointerPos.x));
stageRef.current!.y(-(newPos.y * StageScale.current - pointerPos.y));
stageRef.current!.scale({ x: StageScale.current, y: StageScale.current });
changeStage();
});