跳到主要内容

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

交互式楼层平面图和建筑地图可用于房地产、设施管理和寻路应用。Konva.js 可绘制复杂的多边形图形,并为每个区域添加悬停检测、工具提示和自定义样式。

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

import Konva from 'konva';

function getData() {
return {
'1st Floor': {
color: 'blue',
points: [366, 298, 500, 284, 499, 204, 352, 183, 72, 228, 74, 274],
},
'2nd Floor': {
color: 'red',
points: [72, 228, 73, 193, 340, 96, 498, 154, 498, 191, 341, 171],
},
'3rd Floor': {
color: 'yellow',
points: [73, 192, 73, 160, 340, 23, 500, 109, 499, 139, 342, 93],
},
Gym: {
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,
});

// add background image first
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);

// get areas data
const areas = getData();

// draw areas
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',
// custom attr
key: key,
});

shapesLayer.add(shape);
}

// add layers in correct order
stage.add(shapesLayer);
stage.add(tooltipLayer);

stage.on('mouseover', (evt) => {
const shape = evt.target;
if (shape && shape.name() === 'area') { // only change opacity if it's an area shape
shape.opacity(0.5);
}
});

stage.on('mouseout', (evt) => {
const shape = evt.target;
if (shape && shape.name() === 'area') { // only change opacity if it's an area shape
shape.opacity(0);
tooltip.hide();
}
});

stage.on('mousemove', (evt) => {
const shape = evt.target;
if (shape && shape.name() === 'area') { // only change opacity if it's an area shape
const mousePos = stage.getPointerPosition();
const x = mousePos.x;
const y = mousePos.y - 5;
updateTooltip(tooltip, x, y, shape.getAttr('key'));
}
});