跳到主要内容

鼠标指针样式

光标样式不是 Konva 属性,而是舞台容器的 CSS。改它要通过 DOM。

用法​

我们可以通过监听事件然后给 Stage container 设置样式来改变鼠标指针的样式。

说明:试试鼠标滑过每个五角星,看看指针的变化。

<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva@10/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Mouse Cursor Styles Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
</style>
</head>

<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;

var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();

var shape1 = new Konva.RegularPolygon({
x: 80,
y: stage.height() / 2,
sides: 5,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true
});

var shape2 = new Konva.RegularPolygon({
x: 220,
y: stage.height() / 2,
sides: 5,
radius: 70,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
draggable: true
});

var shape3 = new Konva.RegularPolygon({
x: 360,
y: stage.height() / 2,
sides: 5,
radius: 70,
fillLinearGradientStartPoint: { x: -50, y: -50 },
fillLinearGradientEndPoint: { x: 50, y: 50 },
fillLinearGradientColorStops: [0, 'red', 1, 'yellow'],
stroke: 'black',
strokeWidth: 4,
draggable: true
});

shape1.on('mouseenter', function() {
stage.container().style.cursor = 'pointer';
});

shape1.on('mouseleave', function() {
stage.container().style.cursor = 'default';
});

shape2.on('mouseenter', function() {
stage.container().style.cursor = 'move';
});

shape2.on('mouseleave', function() {
stage.container().style.cursor = 'default';
});

shape3.on('mouseenter', function() {
stage.container().style.cursor = 'crosshair';
});

shape3.on('mouseleave', function() {
stage.container().style.cursor = 'default';
});

layer.add(shape1);
layer.add(shape2);
layer.add(shape3);
stage.add(layer);
</script>
</body>
</html>

常见问题​

为什么不能直接给图形设 cursor?​

因为图形不是 DOM 元素,浏览器无从得知指针悬停在画布内部的哪个「元素」上——在浏览器眼里整个画布就是一个 <canvas>。

所以做法是:监听 Konva 的悬停事件,然后改容器的 CSS:

shape.on('mouseenter', () => {
stage.container().style.cursor = 'pointer';
});
shape.on('mouseleave', () => {
stage.container().style.cursor = 'default';
});

光标卡在某个样式上不恢复了?​

最常见的原因是 mouseleave 没触发。几种情况会导致这个:

图形在鼠标悬停时被 destroy() 或 visible(false) 了——节点没了,离开事件自然不会派发。

用了 mouseout 而不是 mouseleave,在分组内子图形之间移动时会反复进出, 状态容易错乱(两者的区别见事件绑定)。

稳妥的做法是在销毁或隐藏节点前主动复位一次光标。

拖拽过程中光标为什么不听话?​

拖拽时浏览器会接管光标显示,通常强制为默认或「不可放置」样式。

想在拖拽中显示 grabbing,在 dragstart 时设置、dragend 时复位, 并且要设在 document.body 上而不只是容器上——指针可能已经移出容器范围。

国内环境注意事项​

光标是纯桌面端的概念,触屏设备上完全不存在。这意味着任何只靠悬停传达的信息, 在手机上都会丢失。

国内移动端流量占比通常高于桌面,这个问题比想象中严重。常见的失效模式: 用光标变成 pointer 暗示「这里可以点」,手机用户完全看不到这个提示, 于是不知道哪些元素可交互。

替代方案有几种:给可交互元素加持续可见的视觉标识(描边、图标、微妙的阴影); 首次进入时做一次引导动画;或者用 window.matchMedia('(pointer: coarse)') 判断指针精度,为触屏单独提供一套提示。

按指针精度判断比按屏幕宽度可靠——平板接鼠标时屏幕宽但指针是精确的, 而大屏触控一体机则相反。