fix(paper): click area
This commit is contained in:
@@ -1,17 +1,17 @@
|
|||||||
import { Comment } from "./api/label/typing"
|
|
||||||
import { Project } from "./api/project/typing"
|
|
||||||
import { notifications } from "@mantine/notifications"
|
import { notifications } from "@mantine/notifications"
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import paper from "paper"
|
import paper from "paper"
|
||||||
import { DispatchWithoutAction } from "react"
|
import { DispatchWithoutAction } from "react"
|
||||||
import { create } from "zustand"
|
import { create } from "zustand"
|
||||||
import { usePermissionStore } from "./store/auth"
|
import { Comment } from "./api/label/typing"
|
||||||
|
import { Project } from "./api/project/typing"
|
||||||
import {
|
import {
|
||||||
initialDetail,
|
initialDetail,
|
||||||
useKeyEventStore,
|
useKeyEventStore,
|
||||||
useLabelStore,
|
useLabelStore,
|
||||||
useObjectStore,
|
useObjectStore,
|
||||||
} from "./store"
|
} from "./store"
|
||||||
|
import { usePermissionStore } from "./store/auth"
|
||||||
import { useBottomToolsStore } from "./useBottomToolsStore"
|
import { useBottomToolsStore } from "./useBottomToolsStore"
|
||||||
import { useKeyboardStore } from "./useKeyBoardStore"
|
import { useKeyboardStore } from "./useKeyBoardStore"
|
||||||
import { useOtherToolsStore } from "./useOtherToolsStore"
|
import { useOtherToolsStore } from "./useOtherToolsStore"
|
||||||
@@ -319,6 +319,26 @@ const hidePaperContextMenu = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let isMiddleMousePanning = false
|
let isMiddleMousePanning = false
|
||||||
|
let hoveredBufferPathInfo: {
|
||||||
|
id: number
|
||||||
|
index: number
|
||||||
|
path: paper.Path
|
||||||
|
clearHighlight: () => void
|
||||||
|
scheduleClear: () => void
|
||||||
|
cancelScheduledClear: () => void
|
||||||
|
} | null = null
|
||||||
|
let hoveredPathCircleInfo: {
|
||||||
|
id: number
|
||||||
|
index: number
|
||||||
|
item: paper.Path.Circle
|
||||||
|
} | null = null
|
||||||
|
|
||||||
|
const clearHoveredBufferPathHighlight = () => {
|
||||||
|
if (hoveredBufferPathInfo) {
|
||||||
|
hoveredBufferPathInfo.clearHighlight()
|
||||||
|
}
|
||||||
|
hoveredBufferPathInfo = null
|
||||||
|
}
|
||||||
|
|
||||||
const startMiddleMousePan = (event?: MouseEvent) => {
|
const startMiddleMousePan = (event?: MouseEvent) => {
|
||||||
if (!event || event.button !== 1) return false
|
if (!event || event.button !== 1) return false
|
||||||
@@ -510,6 +530,8 @@ export const getShowContextMenuPosition = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DOUBLE_CLICK_TOLERANCE_PX = 8
|
const DOUBLE_CLICK_TOLERANCE_PX = 8
|
||||||
|
const PAN_DRAG_THRESHOLD_PX = 4
|
||||||
|
const BUFFER_HOVER_STICKY_MS = 180
|
||||||
|
|
||||||
const getPaperDoubleClickTolerance = (
|
const getPaperDoubleClickTolerance = (
|
||||||
coordinateSpace: "project" | "local" = "project"
|
coordinateSpace: "project" | "local" = "project"
|
||||||
@@ -525,6 +547,37 @@ const getPaperDoubleClickTolerance = (
|
|||||||
return projectTolerance
|
return projectTolerance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getPaperDragThreshold = (
|
||||||
|
coordinateSpace: "project" | "local" = "project"
|
||||||
|
) => {
|
||||||
|
const viewZoom = usePaperStore.getState().paperScope?.view.zoom || 1
|
||||||
|
const projectThreshold = PAN_DRAG_THRESHOLD_PX / viewZoom
|
||||||
|
|
||||||
|
if (coordinateSpace === "local") {
|
||||||
|
const groupScale = usePaperStore.getState().group?.scaling.x || 1
|
||||||
|
return projectThreshold / groupScale
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectThreshold
|
||||||
|
}
|
||||||
|
|
||||||
|
const showDeletePointLimitNotification = (
|
||||||
|
type: "polygon" | "brush" | "point"
|
||||||
|
) => {
|
||||||
|
const messageMap = {
|
||||||
|
polygon: "当前对象至少保留3个点,无法继续删除",
|
||||||
|
brush: "当前对象至少保留2个点,无法继续删除",
|
||||||
|
point: "当前对象至少保留1个点,无法继续删除",
|
||||||
|
}
|
||||||
|
|
||||||
|
notifications.show({
|
||||||
|
id: "label-delete-point-limit",
|
||||||
|
color: "yellow",
|
||||||
|
autoClose: 1500,
|
||||||
|
message: messageMap[type],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const isPaperDoubleClick = (
|
const isPaperDoubleClick = (
|
||||||
event: MouseEvent,
|
event: MouseEvent,
|
||||||
lastPoint: paper.Point | null | undefined,
|
lastPoint: paper.Point | null | undefined,
|
||||||
@@ -668,6 +721,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
let panTool = paperPanTool
|
let panTool = paperPanTool
|
||||||
let lastPoint: paper.Point
|
let lastPoint: paper.Point
|
||||||
let isChanged: boolean = false
|
let isChanged: boolean = false
|
||||||
|
let hasExceededPanDragThreshold = false
|
||||||
|
|
||||||
// const handleSelected = (path: paper.Path) => {
|
// const handleSelected = (path: paper.Path) => {
|
||||||
// // path.blendMode = "subtract";
|
// // path.blendMode = "subtract";
|
||||||
@@ -810,6 +864,8 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
? baseTolerance / useTopToolsStore.getState().scale
|
? baseTolerance / useTopToolsStore.getState().scale
|
||||||
: 0
|
: 0
|
||||||
|
|
||||||
|
false && getPanHitTolerance
|
||||||
|
|
||||||
const createPanHitOptions = (
|
const createPanHitOptions = (
|
||||||
tolerance: number,
|
tolerance: number,
|
||||||
match: (hit: paper.HitResult) => boolean
|
match: (hit: paper.HitResult) => boolean
|
||||||
@@ -827,13 +883,39 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
const group = usePaperStore.getState().group
|
const group = usePaperStore.getState().group
|
||||||
if (!group) return null
|
if (!group) return null
|
||||||
|
|
||||||
const supportHit = group.hitTest(
|
const hoveredCircleInfo = hoveredPathCircleInfo?.item.parent
|
||||||
projectPoint,
|
? hoveredPathCircleInfo
|
||||||
createPanHitOptions(getPanHitTolerance(), (hit) =>
|
: null
|
||||||
["pathCircle", "pathBuff"].includes(hit.item.data?.type || "")
|
if (hoveredCircleInfo) {
|
||||||
|
const pointHit = group.hitTest(
|
||||||
|
projectPoint,
|
||||||
|
createPanHitOptions(0, (hit) => {
|
||||||
|
return (
|
||||||
|
hit.item.data?.type === "pathCircle" &&
|
||||||
|
hit.item.data?.id === hoveredCircleInfo.id &&
|
||||||
|
hit.item.data?.index === hoveredCircleInfo.index
|
||||||
|
)
|
||||||
|
})
|
||||||
)
|
)
|
||||||
)
|
if (pointHit) return pointHit
|
||||||
if (supportHit) return supportHit
|
}
|
||||||
|
|
||||||
|
const hoveredBufferInfo = hoveredBufferPathInfo?.path.parent
|
||||||
|
? hoveredBufferPathInfo
|
||||||
|
: null
|
||||||
|
if (hoveredBufferInfo) {
|
||||||
|
const edgeHit = group.hitTest(
|
||||||
|
projectPoint,
|
||||||
|
createPanHitOptions(0, (hit) => {
|
||||||
|
return (
|
||||||
|
hit.item.data?.type === "pathBuff" &&
|
||||||
|
hit.item.data?.id === hoveredBufferInfo.id &&
|
||||||
|
hit.item.data?.index === hoveredBufferInfo.index
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
if (edgeHit) return edgeHit
|
||||||
|
}
|
||||||
|
|
||||||
return group.hitTest(
|
return group.hitTest(
|
||||||
projectPoint,
|
projectPoint,
|
||||||
@@ -886,9 +968,16 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
point,
|
point,
|
||||||
"local"
|
"local"
|
||||||
)
|
)
|
||||||
|
hasExceededPanDragThreshold = false
|
||||||
|
|
||||||
let hitResult = resolvePanHitResult(e.point)
|
let hitResult = resolvePanHitResult(e.point)
|
||||||
console.log("hitResult", hitResult)
|
console.log("hitResult", hitResult)
|
||||||
|
const hoveredBufferInfo = hoveredBufferPathInfo?.path.parent
|
||||||
|
? hoveredBufferPathInfo
|
||||||
|
: null
|
||||||
|
const hoveredCircleInfo = hoveredPathCircleInfo?.item.parent
|
||||||
|
? hoveredPathCircleInfo
|
||||||
|
: null
|
||||||
let lastTagDataId = -1
|
let lastTagDataId = -1
|
||||||
if (activePath?.data.id) {
|
if (activePath?.data.id) {
|
||||||
lastTagDataId = activePath.data.id
|
lastTagDataId = activePath.data.id
|
||||||
@@ -999,8 +1088,18 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
useBottomToolsStore.getState().activeImage
|
useBottomToolsStore.getState().activeImage
|
||||||
] || []
|
] || []
|
||||||
const hitId = hitResult?.item?.data?.id
|
const hitId = hitResult?.item?.data?.id
|
||||||
|
const keepHoveredPointSelection =
|
||||||
|
!!hoveredCircleInfo &&
|
||||||
|
selectedIds.length === 1 &&
|
||||||
|
selectedIds[0] === hoveredCircleInfo.id
|
||||||
|
const keepHoveredEdgeSelection =
|
||||||
|
!!hoveredBufferInfo &&
|
||||||
|
selectedIds.length === 1 &&
|
||||||
|
selectedIds[0] === hoveredBufferInfo.id
|
||||||
const keepCurrentSelection =
|
const keepCurrentSelection =
|
||||||
hitId && selectedIds.length === 1 && selectedIds[0] === hitId
|
keepHoveredPointSelection ||
|
||||||
|
keepHoveredEdgeSelection ||
|
||||||
|
(hitId && selectedIds.length === 1 && selectedIds[0] === hitId)
|
||||||
|
|
||||||
if (!keepCurrentSelection) {
|
if (!keepCurrentSelection) {
|
||||||
clearCurrentSelection()
|
clearCurrentSelection()
|
||||||
@@ -1242,6 +1341,56 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
id: data.id,
|
id: data.id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
isDoubleClick &&
|
||||||
|
hoveredBufferInfo &&
|
||||||
|
hoveredBufferInfo.id === activePath?.data?.id &&
|
||||||
|
["polygon", "brush", "point"].includes(activePath?.data?.type || "")
|
||||||
|
) {
|
||||||
|
let currentPath: paper.Path | null = null
|
||||||
|
if (activePath instanceof paper.CompoundPath) {
|
||||||
|
currentPath =
|
||||||
|
(activePath.children as paper.Path[]).find(
|
||||||
|
(item) => item.data.selected
|
||||||
|
) || null
|
||||||
|
} else if (activePath instanceof paper.Path) {
|
||||||
|
currentPath = activePath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPath) {
|
||||||
|
hitIndex = hoveredBufferInfo.index
|
||||||
|
const insertPoint = currentPath.getNearestPoint(point)
|
||||||
|
currentPath.insert(hitIndex + 1, insertPoint)
|
||||||
|
usePaperSupportStore.getState().handleBufferPaths(currentPath)
|
||||||
|
usePaperSupportStore.getState().handleCircles(currentPath)
|
||||||
|
isChanged = true
|
||||||
|
panMode = "stroke"
|
||||||
|
hitIndex = hitIndex + 1
|
||||||
|
|
||||||
|
if (currentPath.data.type === "brush") {
|
||||||
|
usePaperSupportStore.getState().handleMask(currentPath)
|
||||||
|
}
|
||||||
|
if (currentPath.data.type === "point") {
|
||||||
|
usePaperSupportStore.getState().handleText(currentPath)
|
||||||
|
let circles = usePaperStore.getState().group!.getItems({
|
||||||
|
data: {
|
||||||
|
id: currentPath.data.id,
|
||||||
|
type: "circle",
|
||||||
|
},
|
||||||
|
}) as paper.Path[]
|
||||||
|
|
||||||
|
currentPath.segments.forEach((segment, index) => {
|
||||||
|
let findIndex = circles.findIndex((circle) => {
|
||||||
|
return circle.contains(segment.point)
|
||||||
|
})
|
||||||
|
circles[index].data = Object.assign({}, circles[index]?.data, {
|
||||||
|
text: findIndex + 1,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
usePaperSupportStore.getState().handleBufferPaths(currentPath)
|
||||||
|
usePaperSupportStore.getState().handleCircles(currentPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (hitResult && hitResult.type === "fill") {
|
} else if (hitResult && hitResult.type === "fill") {
|
||||||
if (
|
if (
|
||||||
hitResult &&
|
hitResult &&
|
||||||
@@ -1276,28 +1425,30 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
// doubleclick
|
// doubleclick
|
||||||
if (isDoubleClick) {
|
if (isDoubleClick) {
|
||||||
if (activePath instanceof paper.CompoundPath) {
|
if (activePath instanceof paper.CompoundPath) {
|
||||||
;(activePath.children as paper.Path[]).forEach((item) => {
|
const selectedChild =
|
||||||
const childHitResult = item.hitTest(point, {
|
(activePath.children as paper.Path[]).find(
|
||||||
segments: true,
|
(item) => item.data.selected
|
||||||
stroke: true,
|
) ||
|
||||||
curves: true,
|
(activePath.children as paper.Path[]).reduce(
|
||||||
fill: true,
|
(nearest, item) => {
|
||||||
guide: false,
|
if (!nearest) return item
|
||||||
tolerance: usePaperStore.getState().paperScope
|
return item.getNearestPoint(point).getDistance(point) <
|
||||||
? 8 / usePaperStore.getState().paperScope!.view.zoom
|
nearest.getNearestPoint(point).getDistance(point)
|
||||||
: 0,
|
? item
|
||||||
})
|
: nearest
|
||||||
if (childHitResult) {
|
},
|
||||||
if (childHitResult.location.point) {
|
null as paper.Path | null
|
||||||
item.insert(hitIndex + 1, childHitResult.location.point)
|
)
|
||||||
usePaperSupportStore.getState().handleBufferPaths(item)
|
|
||||||
usePaperSupportStore.getState().handleCircles(item)
|
if (selectedChild) {
|
||||||
isChanged = true
|
const insertPoint = selectedChild.getNearestPoint(point)
|
||||||
panMode = "stroke"
|
selectedChild.insert(hitIndex + 1, insertPoint)
|
||||||
hitIndex = hitIndex + 1
|
usePaperSupportStore.getState().handleBufferPaths(selectedChild)
|
||||||
}
|
usePaperSupportStore.getState().handleCircles(selectedChild)
|
||||||
}
|
isChanged = true
|
||||||
})
|
panMode = "stroke"
|
||||||
|
hitIndex = hitIndex + 1
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
activePath.insert(hitIndex + 1, hitResult.location.point)
|
activePath.insert(hitIndex + 1, hitResult.location.point)
|
||||||
activePath?.data?.type === "point" &&
|
activePath?.data?.type === "point" &&
|
||||||
@@ -1380,8 +1531,16 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
let checkLength = 3
|
let checkLength = 3
|
||||||
if (item?.data?.type === "brush") checkLength = 2
|
if (item?.data?.type === "brush") checkLength = 2
|
||||||
if (item?.data?.type === "point") checkLength = 1
|
if (item?.data?.type === "point") checkLength = 1
|
||||||
if (item.segments && item.segments.length <= checkLength)
|
if (item.segments && item.segments.length <= checkLength) {
|
||||||
|
if (
|
||||||
|
item?.data?.type === "polygon" ||
|
||||||
|
item?.data?.type === "brush" ||
|
||||||
|
item?.data?.type === "point"
|
||||||
|
) {
|
||||||
|
showDeletePointLimitNotification(item.data.type)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
|
}
|
||||||
;(childHitResult.item as paper.Path).removeSegment(hitIndex)
|
;(childHitResult.item as paper.Path).removeSegment(hitIndex)
|
||||||
usePaperSupportStore
|
usePaperSupportStore
|
||||||
.getState()
|
.getState()
|
||||||
@@ -1401,8 +1560,16 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
if (
|
if (
|
||||||
activePath.segments &&
|
activePath.segments &&
|
||||||
activePath.segments.length <= checkLength
|
activePath.segments.length <= checkLength
|
||||||
)
|
) {
|
||||||
|
if (
|
||||||
|
activePath?.data?.type === "polygon" ||
|
||||||
|
activePath?.data?.type === "brush" ||
|
||||||
|
activePath?.data?.type === "point"
|
||||||
|
) {
|
||||||
|
showDeletePointLimitNotification(activePath.data.type)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
activePath.removeSegment(hitIndex)
|
activePath.removeSegment(hitIndex)
|
||||||
usePaperSupportStore.getState().handleBufferPaths(activePath)
|
usePaperSupportStore.getState().handleBufferPaths(activePath)
|
||||||
@@ -1551,6 +1718,20 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
point = usePaperStore.getState().group!.globalToLocal(e.point)
|
point = usePaperStore.getState().group!.globalToLocal(e.point)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
activePath &&
|
||||||
|
["fill", "stroke", "segment"].includes(panMode) &&
|
||||||
|
!hasExceededPanDragThreshold
|
||||||
|
) {
|
||||||
|
const dragDistance = e.point.getDistance(e.downPoint)
|
||||||
|
if (dragDistance < getPaperDragThreshold()) return
|
||||||
|
|
||||||
|
hasExceededPanDragThreshold = true
|
||||||
|
if (usePaperStore.getState().el) {
|
||||||
|
usePaperStore.getState().el!.style.cursor = "move"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let movePath = (path: paper.Path) => {
|
let movePath = (path: paper.Path) => {
|
||||||
const currentGroupScaling = usePaperStore.getState().group!.scaling
|
const currentGroupScaling = usePaperStore.getState().group!.scaling
|
||||||
const delta = {
|
const delta = {
|
||||||
@@ -1753,6 +1934,10 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
panTool.onMouseUp = (_e: paper.ToolEvent) => {
|
panTool.onMouseUp = (_e: paper.ToolEvent) => {
|
||||||
if (stopMiddleMousePan()) return
|
if (stopMiddleMousePan()) return
|
||||||
if (useTopToolsStore.getState().isView) return
|
if (useTopToolsStore.getState().isView) return
|
||||||
|
hasExceededPanDragThreshold = false
|
||||||
|
if (usePaperStore.getState().el) {
|
||||||
|
usePaperStore.getState().el!.style.cursor = "default"
|
||||||
|
}
|
||||||
|
|
||||||
let newDrawPath: paper.Path | paper.CompoundPath | null = null
|
let newDrawPath: paper.Path | paper.CompoundPath | null = null
|
||||||
|
|
||||||
@@ -5233,12 +5418,27 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
)
|
)
|
||||||
circle.scale(newScale / currentScaling)
|
circle.scale(newScale / currentScaling)
|
||||||
circle.onMouseEnter = (_e: any) => {
|
circle.onMouseEnter = (_e: any) => {
|
||||||
|
hoveredPathCircleInfo = {
|
||||||
|
id,
|
||||||
|
index,
|
||||||
|
item: circle,
|
||||||
|
}
|
||||||
|
clearHoveredBufferPathHighlight()
|
||||||
circle.strokeWidth = 4
|
circle.strokeWidth = 4
|
||||||
circle.fillColor = new paper.Color("#FFF")
|
circle.fillColor = new paper.Color("#FFF")
|
||||||
|
if (usePaperStore.getState().el) {
|
||||||
|
usePaperStore.getState().el!.style.cursor = "move"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
circle.onMouseLeave = (_e: any) => {
|
circle.onMouseLeave = (_e: any) => {
|
||||||
|
if (hoveredPathCircleInfo?.item === circle) {
|
||||||
|
hoveredPathCircleInfo = null
|
||||||
|
}
|
||||||
circle.strokeWidth = 2
|
circle.strokeWidth = 2
|
||||||
circle.fillColor = color
|
circle.fillColor = color
|
||||||
|
if (usePaperStore.getState().el) {
|
||||||
|
usePaperStore.getState().el!.style.cursor = "default"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return circle
|
return circle
|
||||||
},
|
},
|
||||||
@@ -5251,8 +5451,8 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
segments: [item.point, item.next],
|
segments: [item.point, item.next],
|
||||||
strokeColor: color, // 设置为透明,以便不可见
|
strokeColor: color, // 设置为透明,以便不可见
|
||||||
strokeScaling: false,
|
strokeScaling: false,
|
||||||
strokeWidth: 4, // 设置一个较大的strokeWidth作为缓冲区
|
strokeWidth: 6, // 适度放宽 hover 区域,但避免干扰对象外默认点击
|
||||||
strokeCap: "butt",
|
strokeCap: "round",
|
||||||
onSelect: function (event: { stopPropagation: () => void }) {
|
onSelect: function (event: { stopPropagation: () => void }) {
|
||||||
event.stopPropagation() // 阻止事件冒泡
|
event.stopPropagation() // 阻止事件冒泡
|
||||||
},
|
},
|
||||||
@@ -5266,10 +5466,54 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
// bufferPath.segments[0].handleIn = item.handleIn;
|
// bufferPath.segments[0].handleIn = item.handleIn;
|
||||||
// bufferPath.segments[1].handleOut = item.handleOut;
|
// bufferPath.segments[1].handleOut = item.handleOut;
|
||||||
let edge: paper.Path | null
|
let edge: paper.Path | null
|
||||||
|
let clearTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
const cancelScheduledClear = () => {
|
||||||
|
if (clearTimer) {
|
||||||
|
clearTimeout(clearTimer)
|
||||||
|
clearTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const clearHighlight = () => {
|
||||||
|
cancelScheduledClear()
|
||||||
|
edge?.remove()
|
||||||
|
edge = null
|
||||||
|
if (hoveredBufferPathInfo?.path === bufferPath) {
|
||||||
|
hoveredBufferPathInfo = null
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
usePaperStore.getState().el &&
|
||||||
|
!hoveredPathCircleInfo?.item.parent &&
|
||||||
|
!hoveredBufferPathInfo
|
||||||
|
) {
|
||||||
|
usePaperStore.getState().el!.style.cursor = "default"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const scheduleClear = () => {
|
||||||
|
cancelScheduledClear()
|
||||||
|
clearTimer = setTimeout(() => {
|
||||||
|
clearHighlight()
|
||||||
|
}, BUFFER_HOVER_STICKY_MS)
|
||||||
|
}
|
||||||
// 鼠标移入时高亮显示
|
// 鼠标移入时高亮显示
|
||||||
bufferPath.onMouseEnter = function (event: {
|
bufferPath.onMouseEnter = function (event: {
|
||||||
stopPropagation: () => void
|
stopPropagation?: () => void
|
||||||
}) {
|
}) {
|
||||||
|
if (hoveredPathCircleInfo?.item.parent) {
|
||||||
|
event.stopPropagation?.()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cancelScheduledClear()
|
||||||
|
hoveredBufferPathInfo = {
|
||||||
|
id,
|
||||||
|
index,
|
||||||
|
path: bufferPath,
|
||||||
|
clearHighlight,
|
||||||
|
scheduleClear,
|
||||||
|
cancelScheduledClear,
|
||||||
|
}
|
||||||
|
if (edge && !edge.parent) {
|
||||||
|
edge = null
|
||||||
|
}
|
||||||
if (!edge) {
|
if (!edge) {
|
||||||
edge = new paper.Path(
|
edge = new paper.Path(
|
||||||
Object.assign(
|
Object.assign(
|
||||||
@@ -5280,27 +5524,33 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
bufferPath.segments[1].point,
|
bufferPath.segments[1].point,
|
||||||
],
|
],
|
||||||
strokeColor: "white", // 初始颜色与原始路径相同
|
strokeColor: "white", // 初始颜色与原始路径相同
|
||||||
strokeWidth: 2,
|
strokeWidth: 3,
|
||||||
strokeScaling: false,
|
strokeScaling: false,
|
||||||
strokeCap: "butt",
|
strokeCap: "round",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
data: { index, type: "pathBuff" },
|
data: { index, type: "pathBuffHover" },
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
// edge.bringToFront();
|
// edge.bringToFront();
|
||||||
event.stopPropagation() // 阻止事件冒泡
|
if (usePaperStore.getState().el) {
|
||||||
|
usePaperStore.getState().el!.style.cursor = "crosshair"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
event.stopPropagation?.() // 阻止事件冒泡
|
||||||
}
|
}
|
||||||
|
|
||||||
// 鼠标移出时取消高亮
|
// 鼠标移出时取消高亮
|
||||||
bufferPath.onMouseLeave = function (event: {
|
bufferPath.onMouseLeave = function (event: {
|
||||||
stopPropagation: () => void
|
stopPropagation?: () => void
|
||||||
}) {
|
}) {
|
||||||
edge?.remove()
|
if (hoveredBufferPathInfo?.path === bufferPath) {
|
||||||
edge = null
|
hoveredBufferPathInfo.scheduleClear()
|
||||||
event.stopPropagation() // 阻止事件冒泡
|
} else {
|
||||||
|
scheduleClear()
|
||||||
|
}
|
||||||
|
event.stopPropagation?.() // 阻止事件冒泡
|
||||||
}
|
}
|
||||||
return bufferPath
|
return bufferPath
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user