ImageAnnotator 图像标注
图像 + 钉点标注。点击空白添加、拖动钉点修改坐标、点选弹出删除按钮。坐标用 0–1 归一化以便不同尺寸下复用。
基础用法
annotations 数组用 { id, x, y, label?, tone? },x / y 是图片归一化坐标(0–1)。点击图片空白处自动添加新钉;拖动钉更新坐标;选中后可点 × 删除。
背景 视口
<script setup lang="ts">
import { ref } from 'vue';
import { CfImageAnnotator, type ImageAnnotation } 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"/>' +
'<text x="170" y="350" fill="#99999f" font-family="sans-serif" font-size="14" text-anchor="middle">主板示意图(点击空白添加,拖动钉点)</text>' +
'</svg>';
const imgSrc = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
const annotations = ref<ImageAnnotation[]>([
{ id: 'cpu', x: 0.22, y: 0.34, label: 'CPU 散热片', tone: 'success' },
{ id: 'mem', x: 0.55, y: 0.48, label: '内存条', tone: 'info' },
]);
const selectedId = ref<string | undefined>('cpu');
</script>
<template>
<div class="ia-demo">
<CfImageAnnotator
:src="imgSrc"
alt="主板示意"
:annotations="annotations"
:selected-id="selectedId"
@update:annotations="annotations = $event"
@select="(id) => (selectedId = id)"
/>
<code class="ia-demo__meta">
{{ annotations.length }} 个标记 · selected = {{ selectedId ?? '-' }}
</code>
</div>
</template>
<style scoped>
.ia-demo {
display: grid;
gap: 8px;
}
.ia-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 { CfImageAnnotator } 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"/>' +
'<text x="170" y="350" fill="#99999f" font-family="sans-serif" font-size="14" text-anchor="middle">主板示意图(点击空白添加,拖动钉点)</text>' +
'</svg>';
const imgSrc = `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
const annotations = ref<ImageAnnotation[]>([
{ id: 'cpu', x: 0.22, y: 0.34, label: 'CPU 散热片', tone: 'success' },
{ id: 'mem', x: 0.55, y: 0.48, label: '内存条', tone: 'info' },
]);
const selectedId = ref<string | undefined>('cpu');
</script>
<template>
<div class="ia-demo">
<CfImageAnnotator
:src="imgSrc"
alt="主板示意"
:annotations="annotations"
:selected-id="selectedId"
@update:annotations="annotations = $event"
@select="(id) => (selectedId = id)"
/>
<code class="ia-demo__meta">
{{ annotations.length }} 个标记 · selected = {{ selectedId ?? '-' }}
</code>
</div>
</template>
<style scoped>
.ia-demo {
display: grid;
gap: 8px;
}
.ia-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 { CfImageAnnotator } from '@chufix-design/react';
export default function Demo() {
const [annotations, setAnnotations] = useState([
{ id: 'cpu', x: 0.22, y: 0.34, label: 'CPU 散热片', tone: 'success' },
{ id: 'mem', x: 0.55, y: 0.48, label: '内存条', tone: 'info' },
]);
const [annotations, setAnnotations] = useState([
{ id: 'cpu', x: 0.22, y: 0.34, label: 'CPU 散热片', tone: 'success' },
{ id: 'mem', x: 0.55, y: 0.48, label: '内存条', tone: 'info' },
]);
const [selectedId, setSelectedId] = useState('cpu');
const [selectedId, setSelectedId] = useState('cpu');
const [annotations, setAnnotations] = useState([
{ id: 'cpu', x: 0.22, y: 0.34, label: 'CPU 散热片', tone: 'success' },
]);
const [selectedId, setSelectedId] = useState<string>('cpu');
return (
<>
<CfImageAnnotator
src="/board.png"
annotations={annotations}
selectedId={selectedId}
onChange={setAnnotations}
onSelect={setSelectedId}
/>
</>
);
} import { useState } from 'react';
import { CfImageAnnotator } from '@chufix-design/react';
export default function Demo() {
const [annotations, setAnnotations] = useState([
{ id: 'cpu', x: 0.22, y: 0.34, label: 'CPU 散热片', tone: 'success' },
{ id: 'mem', x: 0.55, y: 0.48, label: '内存条', tone: 'info' },
]);
const [annotations, setAnnotations] = useState([
{ id: 'cpu', x: 0.22, y: 0.34, label: 'CPU 散热片', tone: 'success' },
{ id: 'mem', x: 0.55, y: 0.48, label: '内存条', tone: 'info' },
]);
const [selectedId, setSelectedId] = useState('cpu');
const [selectedId, setSelectedId] = useState('cpu');
const [annotations, setAnnotations] = useState([
{ id: 'cpu', x: 0.22, y: 0.34, label: 'CPU 散热片', tone: 'success' },
]);
const [selectedId, setSelectedId] = useState<string>('cpu');
return (
<>
<CfImageAnnotator
src="/board.png"
annotations={annotations}
selectedId={selectedId}
onChange={setAnnotations}
onSelect={setSelectedId}
/>
</>
);
} API
Props
| 属性 | 类型 | 默认 | 说明 |
|---|---|---|---|
src | string | — | 图片 URL |
alt | string | '' | alt 文本 |
annotations | ImageAnnotation[] | — | 钉点数组 |
readonly | boolean | false | 只读:禁止添加 / 拖动 / 删除 |
addMode | 'click' | 'manual' | 'click' | 点击空白处是否自动加钉 |
selectedId | string | — | 当前选中的钉 id |
defaultTone | AnnotationTone | 'info' | 新加钉默认 tone |
Events
| 事件 | 载荷 | 说明 |
|---|---|---|
update:annotations | ImageAnnotation[] | 列表变更(增 / 改 / 删) |
add | ImageAnnotation | 新加钉 |
update | ImageAnnotation | 钉坐标更新 |
delete | id: string | 删钉 |
select | id: string | 选中 |
反馈与讨论
ImageAnnotator 图像标注 的讨论