fix(tag): bug

This commit is contained in:
zhangheng
2026-04-16 16:36:24 +08:00
parent 8b89c37a1a
commit fca9430246
4 changed files with 121 additions and 49 deletions

View File

@@ -166,6 +166,24 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
[activeImage, continueObjectId, storeLabel]
)
const getObjectItemsById = useCallback(
(id: number) => {
return getItemsById(id).filter((item) => item.data?.type !== "tag")
},
[getItemsById]
)
const bringTagItemsToFront = useCallback(
(id: number) => {
getItemsById(id).forEach((item) => {
if (item.data?.type === "tag") {
item.bringToFront()
}
})
},
[getItemsById]
)
// 切换选中对象时,清除所有其他对象的当前选中状态
const clearSelectedItems = () => {
usePaperStore
@@ -215,11 +233,16 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
const handleSelect = useCallback(
(id: number, operationId: any) => {
if (pathStatus[activeImage + id]?.HIDE) return
const objectItems = getObjectItemsById(id)
const targetItem = getItemById(id)
if (selectedPath[activeImage]?.length) {
if (!selectedPath[activeImage]?.includes(id)) {
if (pressShift) {
if (getItemsById(id)?.length) {
getItemsById(id).forEach((item) => {
if (objectItems.length && targetItem) {
objectItems.forEach((item) => {
if (item instanceof paper.Path) {
handleSelectedPath(item)
} else if (item instanceof paper.CompoundPath) {
@@ -228,13 +251,14 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
else item.data.selected = true
}
})
bringTagItemsToFront(id)
let category = useTopToolsStore
.getState()
.objectOperations.find(
(item) => item.category_id.toString() === operationId
)
const globalPosition = getItemById(id)!.globalMatrix.transform(
getItemById(id)!.position
const globalPosition = targetItem.globalMatrix.transform(
targetItem.position
)
const { top, left } = getShowContextMenuPosition(
globalPosition,
@@ -251,8 +275,9 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
} else {
clearSelectedItems()
selectedPath[activeImage].forEach((selectedId) => {
if (getItemsById(selectedId)?.length) {
getItemsById(selectedId).forEach((item) => {
const selectedObjectItems = getObjectItemsById(selectedId)
if (selectedObjectItems.length) {
selectedObjectItems.forEach((item) => {
cancelSelectedPathColor(item)
})
useOtherToolsStore.getState().setShowContextMenu({
@@ -267,8 +292,8 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
})
}
})
if (getItemsById(id)?.length) {
getItemsById(id).forEach((item) => {
if (objectItems.length && targetItem) {
objectItems.forEach((item) => {
if (item instanceof paper.Path) {
handleSelectedPath(item)
} else if (item instanceof paper.CompoundPath) {
@@ -277,13 +302,14 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
else item.data.selected = true
}
})
bringTagItemsToFront(id)
let category = useTopToolsStore
.getState()
.objectOperations.find(
(item) => item.category_id.toString() === operationId
)
const globalPosition = getItemById(id)!.globalMatrix.transform(
getItemById(id)!.position
const globalPosition = targetItem.globalMatrix.transform(
targetItem.position
)
const { top, left } = getShowContextMenuPosition(
globalPosition,
@@ -302,8 +328,8 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
} else {
clearSelectedItems()
updateSelectedPath(activeImage, "DELETE", id)
if (getItemsById(id)?.length) {
getItemsById(id).forEach((item) => {
if (objectItems.length) {
objectItems.forEach((item) => {
cancelSelectedPathColor(item)
})
useOtherToolsStore.getState().setShowContextMenu({
@@ -321,8 +347,8 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
} else {
clearSelectedItems()
updateSelectedPath(activeImage, "ADD", id)
if (getItemsById(id)?.length) {
getItemsById(id).forEach((item) => {
if (objectItems.length && targetItem) {
objectItems.forEach((item) => {
if (item instanceof paper.Path) {
handleSelectedPath(item)
} else if (item instanceof paper.CompoundPath) {
@@ -337,14 +363,15 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
// item.fillColor = item.data?.fillColor;
// item.bringToFront();
})
bringTagItemsToFront(id)
let category = useTopToolsStore
.getState()
.objectOperations.find(
(item) => item.category_id.toString() === operationId
)
const globalPosition = getItemById(id)!.globalMatrix.transform(
getItemById(id)!.position
const globalPosition = targetItem.globalMatrix.transform(
targetItem.position
)
const { top, left } = getShowContextMenuPosition(
globalPosition,
@@ -362,8 +389,10 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
},
[
activeImage,
bringTagItemsToFront,
getObjectItemsById,
getItemById,
getItemsById,
pathStatus,
pressShift,
selectedPath,
updateSelectedPath,
@@ -889,10 +918,13 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
: "var(--mantine-color-gray-0)"
}
c={isSelected ? "white" : undefined}
onClick={() =>
onClick={() => {
if (isChildHide) return
handleSelect(child[0], operationId)
}
style={{ cursor: "pointer" }}>
}}
style={{
cursor: isChildHide ? "default" : "pointer",
}}>
<Flex justify="space-between" align="center">
<Group gap="xs">
<ActionIcon

View File

@@ -20,6 +20,7 @@ import { usePaperSupportStore } from "./usePaperSupportStore"
import { useRightToolsStore } from "./useRightToolsStore"
import { DEFAULT_NODE_SIZE, useTopToolsStore } from "./useTopToolsStore"
import { safeClone } from "./utils/clone"
import { getObjectTagVisibility } from "./utils/objectVisibility"
interface ContinuePolygonTarget {
imageId: string
@@ -216,6 +217,16 @@ const initialPaperState = {
reciprocalRasterScale: {},
resizeGhostRequestToken: 0,
}
const insertItemBelowTags = (item: paper.Item | null | undefined) => {
if (!item) return
const group = usePaperStore.getState().group
if (!group || item.parent !== group) return
const firstTagItem = group.getItems({ data: { type: "tag" } })[0]
if (!firstTagItem || firstTagItem === item) return
item.insertBelow(firstTagItem)
}
const handleDelete = () => {
const group = usePaperStore.getState().group!
const imageId = useBottomToolsStore.getState().activeImage
@@ -450,12 +461,7 @@ const clearPaperSelectedObjects = () => {
.getState()
.getItemsById(id)
.forEach((item) => {
if (
(item instanceof paper.Path || item instanceof paper.CompoundPath) &&
["rectangle", "polygon"].includes(item.data?.type || "")
) {
item.fillColor = item.data?.blankColor || item.fillColor || null
}
resetPaperShapeFillColor(item)
})
})
@@ -464,6 +470,16 @@ const clearPaperSelectedObjects = () => {
})
}
const resetPaperShapeFillColor = (item: paper.Item) => {
if (!(item instanceof paper.Path || item instanceof paper.CompoundPath)) {
return
}
if (!["rectangle", "polygon"].includes(item.data?.type || "")) {
return
}
item.fillColor = item.data?.blankColor || item.fillColor || null
}
const applyPaperSelectedPath = (item: paper.Path | paper.CompoundPath) => {
if (item.data.type === "rectangle" && item instanceof paper.Path) {
item.data.selected = true
@@ -2011,15 +2027,14 @@ export const usePaperStore = create<PaperState>((set) => ({
type: "tag",
},
})
// 标识位为true时选中路径隐藏tags; 没选中显示tags
const configs = useTopToolsStore.getState().showTagsConfigs
// 仅恢复当前应显示的标签,隐藏对象的标签保持不可见
if (useTopToolsStore.getState().showTags) {
const setTagVisibleByConfig = (tag: any) => {
if (tag.data.tag_category === "mask")
tag.visible = configs.includes("外框")
else if (tag.data.tag_category === "point_text")
tag.visible = configs.includes("点ID")
else tag.visible = true
tag.visible = getObjectTagVisibility(
useBottomToolsStore.getState().activeImage,
Number(tag.data?.id),
tag.data?.tag_category
)
}
lastTags.forEach(setTagVisibleByConfig)
tags.forEach(setTagVisibleByConfig)
@@ -3147,6 +3162,7 @@ export const usePaperStore = create<PaperState>((set) => ({
})
)
polygon.data = Object.assign({ type: "polygon", ...option }, data)
insertItemBelowTags(polygon)
points?.forEach((point) => {
polygon.add(point)
})
@@ -3219,6 +3235,7 @@ export const usePaperStore = create<PaperState>((set) => ({
})
)
compoundPath.data = Object.assign({ type: "polygon", ...option }, data)
insertItemBelowTags(compoundPath)
compoundPath.onMouseDown = (e: {
event: MouseEvent
point: paper.Point
@@ -4606,8 +4623,7 @@ export const usePaperStore = create<PaperState>((set) => ({
.getState()
.group!.getItems({})
.forEach((item) => {
// clear color
item.fillColor = item?.data?.blankColor
resetPaperShapeFillColor(item)
})
}
@@ -4803,6 +4819,7 @@ export const usePaperStore = create<PaperState>((set) => ({
Object.assign(option, { strokeScaling: false })
)
rect.data = Object.assign({ type: "rectangle", ...option }, data)
insertItemBelowTags(rect)
rect.onMouseEnter = (_e: paper.MouseEvent) => {
// if (usePaperStore.getState().mode === "pan") {
// e.target.strokeWidth += 2;
@@ -5045,6 +5062,7 @@ export const usePaperStore = create<PaperState>((set) => ({
)
)
brush.data = Object.assign({ type: "brush", ...option }, data)
insertItemBelowTags(brush)
// points?.forEach((point) => {
// brush.add(point);
// });
@@ -5545,6 +5563,7 @@ export const usePaperStore = create<PaperState>((set) => ({
const newScale = useTopToolsStore.getState().scale / currentScaling
myCircle.scale(newScale / currentScaling)
myCircle.data = Object.assign({ type: "circle", ...option }, data)
insertItemBelowTags(myCircle)
syncPaperCircleDiameter(myCircle)
myCircle.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
if (e.event && e.event.button === 2) {
@@ -5614,6 +5633,7 @@ export const usePaperStore = create<PaperState>((set) => ({
)
)
myCircleLine.data = Object.assign({ type: "point", ...option }, data)
insertItemBelowTags(myCircleLine)
return myCircleLine
},

View File

@@ -4,6 +4,7 @@ import { useLabelStore } from "./store"
import { useBottomToolsStore } from "./useBottomToolsStore"
import { usePaperStore } from "./usePaperStore"
import { useTopToolsStore } from "./useTopToolsStore"
import { getObjectTagVisibility } from "./utils/objectVisibility"
const TAG_FONT_SIZE = 12
const TAG_VERTICAL_GAP = 8
@@ -159,13 +160,12 @@ const useRenderTag = () => {
const activeImage = useBottomToolsStore.getState().activeImage
const group = usePaperStore.getState().group
if (!group) return
const visible = useTopToolsStore.getState().showTags
const configs = useTopToolsStore.getState().showTagsConfigs
const groupScale =
Number.isFinite(group.scaling?.x) && group.scaling.x !== 0
? group.scaling.x
: 1
const applyPointTagCompensation = (
const applyScreenSpaceTagCompensation = (
item: paper.Item,
anchor: [number, number]
) => {
@@ -235,7 +235,7 @@ const useRenderTag = () => {
tag_anchor: tagAnchor,
},
parent: group,
visible,
visible: getObjectTagVisibility(activeImage, id, "text"),
})
text.translate(
new paper.Point(
@@ -259,8 +259,10 @@ const useRenderTag = () => {
tag_anchor: tagAnchor,
},
parent: group,
visible,
visible: getObjectTagVisibility(activeImage, id, "text_mask"),
})
applyScreenSpaceTagCompensation(text, tagAnchor)
applyScreenSpaceTagCompensation(textMask, tagAnchor)
text.bringToFront()
}
@@ -276,7 +278,7 @@ const useRenderTag = () => {
tag_category: "mask",
},
parent: group,
visible: visible && configs.includes("外框"),
visible: getObjectTagVisibility(activeImage, id, "mask"),
})
// 标注对象点id
if (path instanceof paper.CompoundPath) {
@@ -301,9 +303,9 @@ const useRenderTag = () => {
tag_anchor: pointTextPosition,
},
parent: group,
visible: visible && configs.includes("点ID"),
visible: getObjectTagVisibility(activeImage, id, "point_text"),
})
applyPointTagCompensation(pointText, pointTextPosition)
applyScreenSpaceTagCompensation(pointText, pointTextPosition)
index++
})
})
@@ -327,9 +329,9 @@ const useRenderTag = () => {
tag_anchor: pointTextPosition,
},
parent: group,
visible: visible && configs.includes("点ID"),
visible: getObjectTagVisibility(activeImage, id, "point_text"),
})
applyPointTagCompensation(pointText, pointTextPosition)
applyScreenSpaceTagCompensation(pointText, pointTextPosition)
})
}
})

View File

@@ -5,7 +5,7 @@ import { usePaperStore } from "../usePaperStore"
import { usePaperSupportStore } from "../usePaperSupportStore"
import { useTopToolsStore } from "../useTopToolsStore"
const getTagVisibility = (tagCategory?: string) => {
const getTagVisibilityByConfig = (tagCategory?: string) => {
const { showTags, showTagsConfigs } = useTopToolsStore.getState()
if (!showTags) return false
if (tagCategory === "mask") return showTagsConfigs.includes("外框")
@@ -13,6 +13,16 @@ const getTagVisibility = (tagCategory?: string) => {
return true
}
export const getObjectTagVisibility = (
imageId: string,
objectId: number,
tagCategory?: string
) => {
const { pathStatus } = useObjectStore.getState()
if (pathStatus[imageId + objectId]?.HIDE) return false
return getTagVisibilityByConfig(tagCategory)
}
const setSelectedItemFalse = (id: number) => {
if (!id) return
const sameIdItems = usePaperStore.getState().getItemsById(id)
@@ -50,7 +60,11 @@ const clearHiddenObjectSelectionState = (imageId: string, objectId: number) => {
}
}
const syncObjectItemsVisibility = (objectId: number, hide: boolean) => {
const syncObjectItemsVisibility = (
imageId: string,
objectId: number,
hide: boolean
) => {
const sameIdItems = usePaperStore.getState().getItemsById(objectId)
if (!sameIdItems?.length) return
sameIdItems.forEach((item) => {
@@ -59,7 +73,11 @@ const syncObjectItemsVisibility = (objectId: number, hide: boolean) => {
return
}
if (item.data?.type === "tag") {
item.visible = getTagVisibility(item.data?.tag_category)
item.visible = getObjectTagVisibility(
imageId,
objectId,
item.data?.tag_category
)
return
}
item.visible = true
@@ -77,7 +95,7 @@ export const toggleObjectHideByShortcut = (
if (hide) {
updatePathStatus(imageId + objectId, "HIDE", hide)
syncObjectItemsVisibility(objectId, hide)
syncObjectItemsVisibility(imageId, objectId, hide)
setSelectedItemFalse(objectId)
clearHiddenObjectSelectionState(imageId, objectId)
@@ -96,5 +114,5 @@ export const toggleObjectHideByShortcut = (
updateOperationStatus(imageId + operationId, "HIDE", hide)
updatePathStatus(imageId + objectId, "HIDE", hide)
syncObjectItemsVisibility(objectId, hide)
syncObjectItemsVisibility(imageId, objectId, hide)
}