Skip to main content

如何使用 JavaScript Canvas 构建交互式平面图和地图

交互式平面图和建筑地图常用于房地产、设施管理和寻路应用。Konva.js 让绘制复杂的多边形形状变得简单,支持悬停检测、工具提示以及每个区域的自定义样式。

说明:将鼠标悬停在建筑的各个区域上以查看其描述。

import Konva from 'konva';

function getData() {
return {
'一楼': {
color: 'blue',
points: [366, 298, 500, 284, 499, 204, 352, 183, 72, 228, 74, 274],
},
'二楼': {
color: 'red',
points: [72, 228, 73, 193, 340, 96, 498, 154, 498, 191, 341, 171],
},
'三楼': {
color: 'yellow',
points: [73, 192, 73, 160, 340, 23, 500, 109, 499, 139, 342, 93],
},
健身房:{
color: 'green',
points: [498, 283, 503, 146, 560, 136, 576, 144, 576, 278, 500, 283],
},
};
}

function updateTooltip(tooltip, x, y, text) {
tooltip.getText().text(text);
tooltip.position({
x: x,
y: y,
});
tooltip.show();
}

const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});

// 先添加背景图像
const imageLayer = new Konva.Layer();
stage.add(imageLayer);

Konva.Image.fromURL('https://konvajs.org/assets/line-building.png', function (bgImage) {
bgImage.setAttrs({
x: 1,
y: 0,
});
imageLayer.add(bgImage);
});

const shapesLayer = new Konva.Layer();
const tooltipLayer = new Konva.Layer();

const tooltip = new Konva.Label({
opacity: 0.75,
visible: false,
listening: false,
});

tooltip.add(
new Konva.Tag({
fill: 'black',
pointerDirection: 'down',
pointerWidth: 10,
pointerHeight: 10,
lineJoin: 'round',
shadowColor: 'black',
shadowBlur: 10,
shadowOffsetX: 10,
shadowOffsetY: 10,
shadowOpacity: 0.5,
})
);

tooltip.add(
new Konva.Text({
text: '',
fontFamily: 'Calibri',
fontSize: 18,
padding: 5,
fill: 'white',
})
);

tooltipLayer.add(tooltip);

// 获取区域数据
const areas = getData();

// 绘制区域
for (const key in areas) {
const area = areas[key];
const points = area.points;

const shape = new Konva.Line({
points: points,
fill: area.color,
opacity: 0,
closed: true,
name: 'area',
// 自定义属性
key: key,
});

shapesLayer.add(shape);
}

// 按正确顺序添加层
stage.add(shapesLayer);
stage.add(tooltipLayer);

stage.on('mouseover', (evt) => {
const shape = evt.target;
if (shape && shape.name() === 'area') { // 仅在是区域形状时改变不透明度
shape.opacity(0.5);
}
});

stage.on('mouseout', (evt) => {
const shape = evt.target;
if (shape && shape.name() === 'area') { // 仅在是区域形状时改变不透明度
shape.opacity(0);
tooltip.hide();
}
});

stage.on('mousemove', (evt) => {
const shape = evt.target;
if (shape && shape.name() === 'area') { // 仅在是区域形状时改变不透明度
const mousePos = stage.getPointerPosition();
const x = mousePos.x;
const y = mousePos.y - 5;
updateTooltip(tooltip, x, y, shape.getAttr('key'));
}
});