fix: pin editor
This commit is contained in:
parent
9a858918fe
commit
f1a55bf83e
|
|
@ -22,7 +22,48 @@ function generateLabel(index: number): string {
|
|||
return labels.join('');
|
||||
}
|
||||
|
||||
customElement("md-pin-editor", {}, (props, { element }) => {
|
||||
// 解析 pins 字符串 "A:30,40 B:10,30" -> Pin[]
|
||||
function parsePins(pinsStr: string): Pin[] {
|
||||
if (!pinsStr) return [];
|
||||
|
||||
const pins: Pin[] = [];
|
||||
const regex = /([A-Z]+):(\d+),(\d+)/g;
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(pinsStr)) !== null) {
|
||||
pins.push({
|
||||
label: match[1],
|
||||
x: parseInt(match[2]),
|
||||
y: parseInt(match[3])
|
||||
});
|
||||
}
|
||||
|
||||
return pins;
|
||||
}
|
||||
|
||||
// 格式化 pins 为字符串 "A:30,40 B:10,30"
|
||||
function formatPins(pins: Pin[]): string {
|
||||
return pins.map(pin => `${pin.label}:${pin.x},${pin.y}`).join(' ');
|
||||
}
|
||||
|
||||
// 找到最早未使用的标签
|
||||
function findNextUnusedLabel(pins: Pin[]): string {
|
||||
const usedLabels = new Set(pins.map(p => p.label));
|
||||
|
||||
let index = 0;
|
||||
while (true) {
|
||||
const label = generateLabel(index);
|
||||
if (!usedLabels.has(label)) {
|
||||
return label;
|
||||
}
|
||||
index++;
|
||||
if (index > 10000) break; // 安全限制
|
||||
}
|
||||
|
||||
return generateLabel(pins.length);
|
||||
}
|
||||
|
||||
customElement("md-pin-editor", { pins: "", fixed: false }, (props, { element }) => {
|
||||
noShadowDOM();
|
||||
|
||||
const [pins, setPins] = createSignal<Pin[]>([]);
|
||||
|
|
@ -57,18 +98,30 @@ customElement("md-pin-editor", {}, (props, { element }) => {
|
|||
const [image] = createResource(resolvedSrc, loadImage);
|
||||
const visible = createMemo(() => !image.loading && !!image());
|
||||
|
||||
// 从 props.pins 初始化 pins
|
||||
onMount(() => {
|
||||
if (props.pins) {
|
||||
const parsed = parsePins(props.pins);
|
||||
if (parsed.length > 0) {
|
||||
setPins(parsed);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 添加 pin
|
||||
const addPin = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (isFixed()) return;
|
||||
|
||||
const imgRect = (e.target as Element).getBoundingClientRect();
|
||||
const clickX = ((e.clientX - imgRect.left) / imgRect.width) * 100;
|
||||
const clickY = ((e.clientY - imgRect.top) / imgRect.height) * 100;
|
||||
|
||||
const x = Math.round(clickX);
|
||||
const y = Math.round(clickY);
|
||||
const label = generateLabel(pins().length);
|
||||
const label = findNextUnusedLabel(pins());
|
||||
|
||||
setPins([...pins(), { x, y, label }]);
|
||||
};
|
||||
|
|
@ -77,13 +130,16 @@ customElement("md-pin-editor", {}, (props, { element }) => {
|
|||
const removePin = (index: number, e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (isFixed()) return;
|
||||
|
||||
setPins(pins().filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
// 复制所有 pin 为 md-pin 文本
|
||||
// 复制所有 pin 为 :md-editor-pin 格式
|
||||
const copyPins = () => {
|
||||
const pinTexts = pins().map(pin => `:md-pin[${pin.label}]{x=${pin.x} y=${pin.y}}`);
|
||||
const text = pinTexts.join('\n');
|
||||
const pinsStr = formatPins(pins());
|
||||
const text = `:md-pin-editor[${rawSrc}]{pins="${pinsStr}" fixed}`;
|
||||
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
setShowToast(true);
|
||||
|
|
@ -93,18 +149,26 @@ customElement("md-pin-editor", {}, (props, { element }) => {
|
|||
});
|
||||
};
|
||||
|
||||
const isFixed = () => props.fixed;
|
||||
|
||||
return (
|
||||
<div ref={editorContainer}>
|
||||
<Show when={visible()}>
|
||||
<Show when={visible() && image()}>
|
||||
{/* 图片容器 */}
|
||||
<div class="relative" onClick={addPin}>
|
||||
{/* 显示图片 */}
|
||||
<img src={resolvedSrc} alt="" class="inset-0" />
|
||||
|
||||
{/* 透明遮罩层 */}
|
||||
<Show when={!isFixed()}>
|
||||
<div class="absolute inset-0 bg-transparent hover:bg-black/10 transition-colors cursor-crosshair" />
|
||||
</Show>
|
||||
<Show when={isFixed()}>
|
||||
<div class="absolute inset-0 pointer-events-none" />
|
||||
</Show>
|
||||
|
||||
{/* 复制按钮 HUD */}
|
||||
<Show when={!isFixed()}>
|
||||
<div class="absolute top-2 right-2 z-20">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
|
|
@ -117,21 +181,22 @@ customElement("md-pin-editor", {}, (props, { element }) => {
|
|||
📋 复制
|
||||
</button>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Pin 列表 */}
|
||||
<For each={pins()}>
|
||||
{(pin, index) => (
|
||||
<span
|
||||
onClick={(e) => removePin(index(), e)}
|
||||
class="absolute transform -translate-x-1/2 -translate-y-1/2 pointer-events-auto cursor-pointer
|
||||
class={`absolute transform -translate-x-1/2 -translate-y-1/2 pointer-events-auto
|
||||
bg-red-500 text-white text-xs font-bold rounded-full w-6 h-6
|
||||
flex items-center justify-center shadow-lg
|
||||
hover:bg-red-600 hover:scale-110 transition-all z-10"
|
||||
${!isFixed() ? 'cursor-pointer hover:bg-red-600 hover:scale-110 transition-all z-10' : 'cursor-default z-10'}`}
|
||||
style={{
|
||||
left: `${pin.x}%`,
|
||||
top: `${pin.y}%`
|
||||
}}
|
||||
title={`点击删除 (${pin.x}, ${pin.y})`}
|
||||
title={isFixed() ? `(${pin.x}, ${pin.y})` : `点击删除 (${pin.x}, ${pin.y})`}
|
||||
>
|
||||
{pin.label}
|
||||
</span>
|
||||
|
|
|
|||
Loading…
Reference in New Issue