Skip to content

Commit

Permalink
Closes #1565 map: add zIndex hotspot option
Browse files Browse the repository at this point in the history
  • Loading branch information
mistic100 committed Feb 22, 2025
1 parent 124aa54 commit 154a316
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 48 deletions.
5 changes: 5 additions & 0 deletions docs/plugins/map.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ The hotspots are represented by a circle with configurable size and color, but c
* @default 'rgba(255, 255, 255, 0.6)'
*/
hoverBorderColor?: string;
/**
* Stacking position of the hotpost, defaults to declaration order
* @default null
*/
zIndex?: number;
}
```

Expand Down
6 changes: 6 additions & 0 deletions examples/plugin-map.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@
yaw: '-45deg',
distance: 80, // pixels
tooltip: 'Hotspot',
zIndex: 10,
},
{
yaw: '-30deg',
distance: 90,
color: 'lightgrey',
},
],
spotStyle: {
Expand Down
106 changes: 58 additions & 48 deletions packages/map-plugin/src/components/MapComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,61 +536,71 @@ export class MapComponent extends AbstractComponent {
context.restore();

// draw the hotspots
[...this.config.hotspots, ...this.state.markers].forEach((hotspot: MapHotspot) => {
const isHover = this.state.hotspotId === hotspot.id;

const style = getStyle(this.config.spotStyle, hotspot, isHover);
const image = this.__loadImage(style.image);

const hotspotPos = { ...offset };
if ('yaw' in hotspot && 'distance' in hotspot) {
const angle = utils.parseAngle(hotspot.yaw) + rotation;
hotspotPos.x += Math.sin(-angle) * hotspot.distance * this.state.imgScale;
hotspotPos.y += Math.cos(-angle) * hotspot.distance * this.state.imgScale;
} else if ('x' in hotspot && 'y' in hotspot) {
hotspotPos.x += center.x - hotspot.x * this.state.imgScale;
hotspotPos.y += center.y - hotspot.y * this.state.imgScale;
} else {
utils.logWarn(`Hotspot ${hotspot['id']} is missing position (yaw+distance or x+y)`);
return;
}
[...this.config.hotspots, ...this.state.markers]
.sort((a, b) => {
if (this.state.hotspotId === a.id) {
return 1;
}
if (this.state.hotspotId === b.id) {
return -1;
}
return (a.zIndex ?? 0) - (b.zIndex ?? 0);
})
.forEach((hotspot: MapHotspot) => {
const isHover = this.state.hotspotId === hotspot.id;

const style = getStyle(this.config.spotStyle, hotspot, isHover);
const image = this.__loadImage(style.image);

const hotspotPos = { ...offset };
if ('yaw' in hotspot && 'distance' in hotspot) {
const angle = utils.parseAngle(hotspot.yaw) + rotation;
hotspotPos.x += Math.sin(-angle) * hotspot.distance * this.state.imgScale;
hotspotPos.y += Math.cos(-angle) * hotspot.distance * this.state.imgScale;
} else if ('x' in hotspot && 'y' in hotspot) {
hotspotPos.x += center.x - hotspot.x * this.state.imgScale;
hotspotPos.y += center.y - hotspot.y * this.state.imgScale;
} else {
utils.logWarn(`Hotspot ${hotspot['id']} is missing position (yaw+distance or x+y)`);
return;
}

const spotPos = projectPoint(hotspotPos, yawAndRotation, zoom);
const spotPos = projectPoint(hotspotPos, yawAndRotation, zoom);

// TODO filter out not visible
// TODO filter out not visible

const x = canvasVirtualCenterX - spotPos.x;
const y = canvasVirtualCenterY - spotPos.y;
const x = canvasVirtualCenterX - spotPos.x;
const y = canvasVirtualCenterY - spotPos.y;

// save absolute position on the viewer
this.state.hotspotPos[hotspot.id] = {
x: x + canvasPos.x,
y: y + canvasPos.y,
s: style.size,
};
// save absolute position on the viewer
this.state.hotspotPos[hotspot.id] = {
x: x + canvasPos.x,
y: y + canvasPos.y,
s: style.size,
};

context.save();
context.translate(x * SYSTEM.pixelRatio, y * SYSTEM.pixelRatio);
canvasShadow(context, PIN_SHADOW_OFFSET, PIN_SHADOW_OFFSET, PIN_SHADOW_BLUR);
if (image) {
drawImageCentered(context, image, style.size);
} else {
context.fillStyle = style.color;
context.beginPath();
context.arc(0, 0, (style.size * SYSTEM.pixelRatio) / 2, 0, 2 * Math.PI);
context.fill();

if (style.borderColor && style.borderSize) {
context.shadowColor = 'transparent';
context.strokeStyle = style.borderColor;
context.lineWidth = style.borderSize;
context.save();
context.translate(x * SYSTEM.pixelRatio, y * SYSTEM.pixelRatio);
canvasShadow(context, PIN_SHADOW_OFFSET, PIN_SHADOW_OFFSET, PIN_SHADOW_BLUR);
if (image) {
drawImageCentered(context, image, style.size);
} else {
context.fillStyle = style.color;
context.beginPath();
context.arc(0, 0, ((style.size + style.borderSize) * SYSTEM.pixelRatio) / 2, 0, 2 * Math.PI);
context.stroke();
context.arc(0, 0, (style.size * SYSTEM.pixelRatio) / 2, 0, 2 * Math.PI);
context.fill();

if (style.borderColor && style.borderSize) {
context.shadowColor = 'transparent';
context.strokeStyle = style.borderColor;
context.lineWidth = style.borderSize;
context.beginPath();
context.arc(0, 0, ((style.size + style.borderSize) * SYSTEM.pixelRatio) / 2, 0, 2 * Math.PI);
context.stroke();
}
}
}
context.restore();
});
context.restore();
});

const pinImage = this.__loadImage(this.config.pinImage);
if (pinImage || (this.config.coneColor && this.config.coneSize)) {
Expand Down
5 changes: 5 additions & 0 deletions packages/map-plugin/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ export type MapHotspotStyle = {
* @default 'rgba(255, 255, 255, 0.6)'
*/
hoverBorderColor?: string;
/**
* Stacking position of the hotpost, defaults to declaration order
* @default null
*/
zIndex?: number;
};

export type MapHotspot = (Point | { yaw: number | string; distance: number }) & MapHotspotStyle & {
Expand Down

0 comments on commit 154a316

Please sign in to comment.