HotspotImage 图像热区
图像 + 预定义可点击区域(矩形 / 圆形)。SVG overlay 渲染、归一化坐标、悬停高亮 + label tooltip。
基础用法
hotspots 数组定义可点击区域;shape: 'rect' 配 rect: { x, y, w, h },shape: 'circle' 配 circle: { cx, cy, r },全部归一化到图像 0–1 坐标。设 show-outlines 强制描边可见,否则仅 hover 时显示。
背景 视口
<script setup lang="ts">
import { ref } from 'vue';
import { CfHotspotImage, type HotspotItem } from '@chufix-design/vue';
const svg =
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 360">' +
'<rect width="100%" height="100%" fill="#26272a"/>' +
'<rect x="80" y="80" width="180" height="180" fill="#404048" stroke="#656571" stroke-width="2" rx="8"/>' +
'<rect x="300" y="150" width="220" height="50" fill="#404048" stroke="#656571" stroke-width="2" rx="4"/>' +
'<circle cx="450" cy="80" r="48" fill="#404048" stroke="#656571" stroke-width="2"/>' +
'</svg>';
const imgSrc = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
const hotspots: HotspotItem[] = [
{
id: 'cpu',
shape: 'rect',
rect: { x: 0.13, y: 0.22, w: 0.3, h: 0.5 },
label: 'CPU 区',
tone: 'success',
},
{
id: 'pcie',
shape: 'rect',
rect: { x: 0.5, y: 0.42, w: 0.36, h: 0.14 },
label: 'PCIe 插槽',
tone: 'info',
},
{
id: 'fan',
shape: 'circle',
circle: { cx: 0.75, cy: 0.22, r: 0.08 },
label: '机箱风扇',
tone: 'warning',
},
];
const last = ref('点击区域查看坐标');
function onClick(h: HotspotItem) {
last.value = `clicked: ${h.id} (${h.label ?? '-'})`;
}
</script>
<template>
<div class="hs-demo">
<CfHotspotImage
:src="imgSrc"
alt="hardware"
:hotspots="hotspots"
show-outlines
@hotspot-click="onClick"
/>
<code class="hs-demo__meta">{{ last }}</code>
</div>
</template>
<style scoped>
.hs-demo {
display: grid;
gap: 8px;
}
.hs-demo__meta {
padding: 4px 8px;
background: var(--bg-inset);
border-radius: var(--r-2);
font-size: var(--t-12);
color: var(--fg-2);
}
</style> <script setup>
import { ref } from 'vue';
import { CfHotspotImage } from '@chufix-design/vue';
const svg =
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 360">' +
'<rect width="100%" height="100%" fill="#26272a"/>' +
'<rect x="80" y="80" width="180" height="180" fill="#404048" stroke="#656571" stroke-width="2" rx="8"/>' +
'<rect x="300" y="150" width="220" height="50" fill="#404048" stroke="#656571" stroke-width="2" rx="4"/>' +
'<circle cx="450" cy="80" r="48" fill="#404048" stroke="#656571" stroke-width="2"/>' +
'</svg>';
const imgSrc = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
const hotspots= [
{
id: 'cpu',
shape: 'rect',
rect: { x: 0.13, y: 0.22, w: 0.3, h: 0.5 },
label: 'CPU 区',
tone: 'success',
},
{
id: 'pcie',
shape: 'rect',
rect: { x: 0.5, y: 0.42, w: 0.36, h: 0.14 },
label: 'PCIe 插槽',
tone: 'info',
},
{
id: 'fan',
shape: 'circle',
circle: { cx: 0.75, cy: 0.22, r: 0.08 },
label: '机箱风扇',
tone: 'warning',
},
];
const last = ref('点击区域查看坐标');
function onClick(h) {
last.value = `clicked: ${h.id} (${h.label ?? '-'})`;
}
</script>
<template>
<div class="hs-demo">
<CfHotspotImage
:src="imgSrc"
alt="hardware"
:hotspots="hotspots"
show-outlines
@hotspot-click="onClick"
/>
<code class="hs-demo__meta">{{ last }}</code>
</div>
</template>
<style scoped>
.hs-demo {
display: grid;
gap: 8px;
}
.hs-demo__meta {
padding: 4px 8px;
background: var(--bg-inset);
border-radius: var(--r-2);
font-size: var(--t-12);
color: var(--fg-2);
}
</style> import { useState } from 'react';
import { CfHotspotImage } from '@chufix-design/react';
export default function Demo() {
const svg =
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 360">' +
'<rect width="100%" height="100%" fill="#26272a"/>' +
'<rect x="80" y="80" width="180" height="180" fill="#404048" stroke="#656571" stroke-width="2" rx="8"/>' +
'<rect x="300" y="150" width="220" height="50" fill="#404048" stroke="#656571" stroke-width="2" rx="4"/>' +
'<circle cx="450" cy="80" r="48" fill="#404048" stroke="#656571" stroke-width="2"/>' +
'</svg>';
const imgSrc = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
const hotspots: HotspotItem[] = [
{
id: 'cpu',
shape: 'rect',
rect: { x: 0.13, y: 0.22, w: 0.3, h: 0.5 },
label: 'CPU 区',
tone: 'success',
},
{
id: 'pcie',
shape: 'rect',
rect: { x: 0.5, y: 0.42, w: 0.36, h: 0.14 },
label: 'PCIe 插槽',
tone: 'info',
},
{
id: 'fan',
shape: 'circle',
circle: { cx: 0.75, cy: 0.22, r: 0.08 },
label: '机箱风扇',
tone: 'warning',
},
];
const [last, setLast] = useState('点击区域查看坐标');
function onClick(h: HotspotItem) {
setLast(`clicked: ${h.id} (${h.label ?? '-'})`);
}
return (
<>
<div className="hs-demo">
<CfHotspotImage src={imgSrc} alt="hardware" hotspots={hotspots} showOutlines onHotspotClick={onClick} />
<code className="hs-demo__meta">{last}</code>
</div>
</>
);
} import { useState } from 'react';
import { CfHotspotImage } from '@chufix-design/react';
export default function Demo() {
const svg =
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 360">' +
'<rect width="100%" height="100%" fill="#26272a"/>' +
'<rect x="80" y="80" width="180" height="180" fill="#404048" stroke="#656571" stroke-width="2" rx="8"/>' +
'<rect x="300" y="150" width="220" height="50" fill="#404048" stroke="#656571" stroke-width="2" rx="4"/>' +
'<circle cx="450" cy="80" r="48" fill="#404048" stroke="#656571" stroke-width="2"/>' +
'</svg>';
const imgSrc = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
const hotspots= [
{
id: 'cpu',
shape: 'rect',
rect: { x: 0.13, y: 0.22, w: 0.3, h: 0.5 },
label: 'CPU 区',
tone: 'success',
},
{
id: 'pcie',
shape: 'rect',
rect: { x: 0.5, y: 0.42, w: 0.36, h: 0.14 },
label: 'PCIe 插槽',
tone: 'info',
},
{
id: 'fan',
shape: 'circle',
circle: { cx: 0.75, cy: 0.22, r: 0.08 },
label: '机箱风扇',
tone: 'warning',
},
];
const [last, setLast] = useState('点击区域查看坐标');
function onClick(h) {
setLast(`clicked: ${h.id} (${h.label ?? '-'})`);
}
return (
<>
<div className="hs-demo">
<CfHotspotImage src={imgSrc} alt="hardware" hotspots={hotspots} showOutlines onHotspotClick={onClick} />
<code className="hs-demo__meta">{last}</code>
</div>
</>
);
} API
Props
| 属性 | 类型 | 默认 | 说明 |
|---|---|---|---|
src | string | — | 图片 URL |
alt | string | '' | alt |
hotspots | HotspotItem[] | — | 区域定义 |
showOutlines | boolean | false | 静态显示描边 |
Events
| 事件 | 载荷 | 说明 |
|---|---|---|
hotspot-click | (hotspot, ev) | 点击区域 |
hotspot-hover | hotspot | null | 悬停 |
反馈与讨论
HotspotImage 图像热区 的讨论