必须确保块无论宽度和长度如何都不会相互重叠,并且在移动和调整大小时不会超出画布的边界。块可以是全宽或全长,并且具有最小单元的侧面。 如果有人有解决方案或至少有移动的方向,我会很高兴
这是我的代码。它不能正常工作,因为在某些情况下它会跳转到画布后面或另一个块内的错误区域。
const canvasSize = { width: 1923, height: 1083 }
const cellDimensions = { width: 120, height: 120 }
const borderStrokeWidth = 3
const objectTypes = {
frame: 'frame',
gridPoint: 'point',
gridLine: 'line',
}
const initialFramesList = [
{
top: 120,
left: 120,
width: 240,
height: 480,
},
{
top: 120,
left: 600,
width: 480,
height: 720,
},
]
let drawingCanvas
initCanvas()
initFrames(initialFramesList)
function initCanvas(id = 'canvas') {
let startX
let startY
let endX
let endY
let activeFrame
drawingCanvas = new fabric.Canvas(id, {
height: canvasSize.height + borderStrokeWidth,
width: canvasSize.width + borderStrokeWidth,
hoverCursor: 'default',
backgroundColor: 'gray',
selection: false,
})
const onClickPoint = (e) => {
const isGridPoint = e.target?.type === objectTypes.gridPoint
if (isGridPoint && !activeFrame) {
const { top: y, left: x, width: d } = e.target
const r = d / 2
startX = x + r
startY = y + r
activeFrame = createFrame({ x: startX, y: startY })
drawingCanvas.add(activeFrame)
} else if (activeFrame) {
startX = null
startY = null
endX = null
endY = null
activeFrame.setCoords()
activeFrame = null
drawingCanvas.renderAll()
}
}
const widgetsLoop = (fn) => {
const widgets = drawingCanvas.getObjects(objectTypes.frame)
widgets.forEach((obj) => {
fn(obj)
})
}
const getAbsolutePosition = (el) => {
return el.group
? { x: el.group.left, y: el.group.top }
: { x: el.left, y: el.top }
}
const hasIntersection = (target, obj) => {
const { x: targetLeft, y: targetTop } = getAbsolutePosition(target)
const { x: objLeft, y: objTop } = getAbsolutePosition(obj)
const {
width: targetWidth,
height: targetHeight,
originX: targetOriginX,
originY: targetOriginY,
} = target
const { width: objWidth, height: objHeight } = obj
const rectLeftCorrect =
targetOriginX === 'right'
? targetLeft - targetWidth - borderStrokeWidth
: targetLeft
const rectTopCorrect =
targetOriginY === 'bottom'
? targetTop - targetHeight - borderStrokeWidth
: targetTop
const xIntersection =
rectLeftCorrect + targetWidth > objLeft &&
rectLeftCorrect < objLeft + objWidth
const yIntersection =
rectTopCorrect + targetHeight > objTop &&
rectTopCorrect < objTop + objHeight
return xIntersection && yIntersection
}
const limitFrameMoving = (target) => {
const { width: screenW, height: screenH } = canvasSize
const { x: rectLeft, y: rectTop } = getAbsolutePosition(target)
const {
originX: targetOriginX,
originY: targetOriginY,
width: targetW,
height: targetH,
} = target
const rectLeftCorrect =
targetOriginX === 'right'
? rectLeft - targetW - borderStrokeWidth
: rectLeft
const rectTopCorrect =
targetOriginY === 'bottom'
? rectTop - targetH - borderStrokeWidth
: rectTop
if (rectLeftCorrect < 0) {
target.set({ left: 0 })
} else if (rectLeftCorrect + targetW > screenW) {
target.set({ left: screenW - targetW })
}
if (rectTopCorrect < 0) {
target.set({ top: 0 })
} else if (rectTopCorrect + targetH > screenH) {
target.set({ top: screenH - targetH })
}
}
const setValidSize = (target) => {
const { width: cellW, height: cellH } = cellDimensions
widgetsLoop((obj) => {
if (obj === target) {
return
}
if (hasIntersection(target, obj)) {
const { x: rectLeft, y: rectTop } = getAbsolutePosition(target)
const { x: objLeft, y: objTop } = getAbsolutePosition(obj)
const { originX: targetOriginX, originY: targetOriginY } =
target
const { width: objWidth, height: objHeight } = obj
const rectLeftCorrect =
targetOriginX === 'right'
? rectLeft - objWidth - borderStrokeWidth
: rectLeft
const rectTopCorrect =
targetOriginY === 'bottom'
? rectTop - objHeight - borderStrokeWidth
: rectTop
const dx = Math.abs(rectLeftCorrect - objLeft)
const dy = Math.abs(rectTopCorrect - objTop)
if (dx > dy && target.width > dx) {
target.set({ width: dx, cellY: dx / cellW })
} else if (dx < dy && target.height > dy) {
target.set({ height: dy, cellY: dy / cellH })
}
}
})
}
const setValidPosition = (target) => {
widgetsLoop((obj) => {
if (obj === target) {
return
}
limitFrameMoving(target)
if (hasIntersection(target, obj)) {
const dx = Math.abs(target.left - obj.left)
const dy = Math.abs(target.top - obj.top)
if (dx > dy) {
target.set({ left: obj.left - target.width })
if (target.left < 0) {
target.set({ left: obj.left + obj.width })
}
} else {
target.set({ top: obj.top - target.height })
if (target.top < 0) {
target.set({ top: obj.top + obj.height })
}
}
}
})
drawingCanvas.renderAll()
}
drawingCanvas.on('mouse:over', (e) => {
if (e.target?.type === objectTypes.gridPoint) {
drawingCanvas.bringToFront(e.target)
e.target.set('opacity', 1)
drawingCanvas.renderAll()
}
})
drawingCanvas.on('mouse:out', (e) => {
if (e.target?.type === objectTypes.gridPoint) {
e.target.set('opacity', 0)
drawingCanvas.renderAll()
}
})
drawingCanvas.on('mouse:down', (e) => {
onClickPoint(e)
})
drawingCanvas.on('mouse:move', (e) => {
if (!activeFrame) {
return
}
const { width: cellW, height: cellH } = cellDimensions
const pointerX = Math.floor(e.pointer.x / cellW) * cellW
const pointerY = Math.floor(e.pointer.y / cellH) * cellH
const width = pointerX - startX + cellW
const height = pointerY - startY + cellH
if (pointerX >= startX) {
activeFrame.set({ width, cellX: width / cellW })
}
if (pointerY >= startY) {
activeFrame.set({ height, cellY: height / cellH })
}
setValidSize(activeFrame)
drawingCanvas.renderAll()
})
drawingCanvas.on('object:scaling', (e) => {
const target = e.target
const { width: cellW, height: cellH } = cellDimensions
const width = Math.round((target.width * target.scaleX) / cellW) * cellW
const height =
Math.round((target.height * target.scaleY) / cellH) * cellH
const { originX, originY, corner } = e.transform
const { y: top, x: left } = target.getPointByOrigin(originX, originY)
target.set({
originX,
originY,
left,
top,
scaleX: 1,
scaleY: 1,
width: width > cellW ? width : cellW,
height: height > cellH ? height : cellH,
cellX: width / cellW,
cellY: height / cellH,
lockScalingFlip: true,
})
setValidSize(target)
})
drawingCanvas.on('object:moving', (e) => {
const target = e.target
const { width: cellW, height: cellH } = cellDimensions
const left = Math.floor(target.left / cellW) * cellW
const top = Math.floor(target.top / cellH) * cellH
target.set({
left,
top,
})
setValidPosition(target)
})
drawingCanvas.on('object:modified', (e) => {
const obj = e.target
const { y: top, x: left } = obj.getPointByOrigin('left', 'top')
obj.set({ top, left, originX: 'left', originY: 'top' })
drawingCanvas.renderAll()
})
drawGrid()
}
function drawGrid() {
const { width: colSize, height: rowSize } = cellDimensions
const makePoint = (x, y) => {
const r = 8
const point = new fabric.Rect({
top: y - r,
left: x - r,
width: r * 2,
height: r * 2,
rx: 1,
ry: 1,
fill: 'black',
hasControls: false,
hasBorders: false,
selectable: false,
opacity: 0,
type: objectTypes.gridPoint,
hoverCursor: 'pointer',
})
drawingCanvas.add(point)
}
const drawLine = (params) => {
const line = new fabric.Line(params, {
stroke: 'yellow',
borderStrokeWidth: borderStrokeWidth,
selectable: false,
evented: false,
type: objectTypes.gridLine,
})
drawingCanvas.add(line)
}
const makeRows = () => {
const rowsCount = canvasSize.height / rowSize
for (let i = 0; i < rowsCount; i++) {
const y = i * rowSize
drawLine([0, y, canvasSize.width, y])
}
}
const makeCols = () => {
const colsCount = canvasSize.width / colSize
for (let i = 0; i < colsCount; i++) {
const x = i * colSize
drawLine([x, 0, x, canvasSize.height])
}
}
const makePoints = () => {
const rowsCount = canvasSize.height / rowSize
const colsCount = canvasSize.width / colSize
for (let i = 0; i < colsCount; i++) {
const x = i * colSize
for (let j = 0; j < rowsCount; j++) {
const y = j * rowSize
makePoint(x, y)
}
}
}
makeRows()
makeCols()
makePoints()
}
function initFrames(frames) {
const { width: cellW, height: cellH } = cellDimensions
frames.forEach(({ top, left, width, height }) => {
const frame = createFrame(
{
x: left,
y: top,
},
{
width: width,
height: height,
}
)
frame.set({
cellX: width / cellW,
cellY: height / cellH,
lockScalingFlip: true,
})
drawingCanvas.add(frame)
})
}
function createFrame({ x, y }, { width, height } = { width: 0, height: 0 }) {
const rect = new fabric.Rect({
top: y,
left: x,
width,
height,
stroke: 'gray',
borderStrokeWidth: borderStrokeWidth,
fill: 'white',
type: objectTypes.frame,
strokeUniform: true,
noScaleCache: false,
objectCaching: false,
})
return rect
}
<canvas id="canvas"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.4.0/fabric.min.js"></script>
我尝试过的所有内容都列在我的代码中。元素旋转不会使用,稍后我会将其从库中排除。
我认为这可能就是你所谓的跳转到错误区域。
我们可以看到我正在移动左侧的方块,但它跳到了底部......
所以我只关注这个问题
查看您的代码,您有
setValidPosition
,其中有一个条件:if (hasIntersection(target, obj)) {
我做的两件事:
首先在画布上启用有状态
drawingCanvas.stateful = true;
然后将 setValidPosition 更改为
const setValidPosition = (target) => {
widgetsLoop((obj) => {
if (obj === target) {
return
}
if (hasIntersection(target, obj)) {
target.set({ left: target._stateProperties.left, top: target._stateProperties.top })
} else {
target.saveState();
}
})
drawingCanvas.renderAll()
}
const canvasSize = { width: 520, height: 400 }
const cellDimensions = { width: 40, height: 40 }
const borderStrokeWidth = 3
const objectTypes = {frame: 'frame', gridPoint: 'point', gridLine: 'line'}
const initialFramesList = [
{top: 40, left: 200, width: 80, height: 200},
{top: 40, left: 40, width: 80, height: 120}
]
let drawingCanvas
initCanvas()
initFrames(initialFramesList)
function initCanvas(id = 'canvas') {
let startX
let startY
let endX
let endY
let activeFrame
drawingCanvas = new fabric.Canvas(id, {
height: canvasSize.height + borderStrokeWidth,
width: canvasSize.width + borderStrokeWidth,
hoverCursor: 'default',
backgroundColor: 'gray',
selection: false,
})
drawingCanvas.stateful = true;
const widgetsLoop = (fn) => {
const widgets = drawingCanvas.getObjects(objectTypes.frame)
widgets.forEach((obj) => { fn(obj) })
}
const getAbsolutePosition = (el) => {
return el.group
? { x: el.group.left, y: el.group.top }
: { x: el.left, y: el.top }
}
const hasIntersection = (target, obj) => {
const { x: targetLeft, y: targetTop } = getAbsolutePosition(target)
const { x: objLeft, y: objTop } = getAbsolutePosition(obj)
const {
width: targetWidth,
height: targetHeight,
originX: targetOriginX,
originY: targetOriginY,
} = target
const { width: objWidth, height: objHeight } = obj
const rectLeftCorrect =
targetOriginX === 'right'
? targetLeft - targetWidth - borderStrokeWidth
: targetLeft
const rectTopCorrect =
targetOriginY === 'bottom'
? targetTop - targetHeight - borderStrokeWidth
: targetTop
const xIntersection =
rectLeftCorrect + targetWidth > objLeft &&
rectLeftCorrect < objLeft + objWidth
const yIntersection =
rectTopCorrect + targetHeight > objTop &&
rectTopCorrect < objTop + objHeight
return xIntersection && yIntersection
}
const setValidPosition = (target) => {
widgetsLoop((obj) => {
if (obj === target) {
return
}
if (hasIntersection(target, obj)) {
target.set({
left: target._stateProperties.left,
top: target._stateProperties.top, })
} else {
target.saveState();
}
})
drawingCanvas.renderAll()
}
drawingCanvas.on('mouse:over', (e) => {
if (e.target?.type === objectTypes.gridPoint) {
drawingCanvas.bringToFront(e.target)
e.target.set('opacity', 1)
drawingCanvas.renderAll()
}
})
drawingCanvas.on('mouse:out', (e) => {
if (e.target?.type === objectTypes.gridPoint) {
e.target.set('opacity', 0)
drawingCanvas.renderAll()
}
})
drawingCanvas.on('object:moving', (e) => {
const target = e.target
const { width: cellW, height: cellH } = cellDimensions
const left = Math.floor(target.left / cellW) * cellW
const top = Math.floor(target.top / cellH) * cellH
target.set({ left, top })
setValidPosition(target)
})
drawingCanvas.on('object:modified', (e) => {
const obj = e.target
const { y: top, x: left } = obj.getPointByOrigin('left', 'top')
obj.set({ top, left, originX: 'left', originY: 'top' })
drawingCanvas.renderAll()
})
drawGrid()
}
function drawGrid() {
const { width: colSize, height: rowSize } = cellDimensions
const drawLine = (params) => {
const line = new fabric.Line(params, {
stroke: 'yellow',
borderStrokeWidth: borderStrokeWidth,
selectable: false,
evented: false,
type: objectTypes.gridLine,
})
drawingCanvas.add(line)
}
const makeRows = () => {
const rowsCount = canvasSize.height / rowSize
for (let i = 0; i < rowsCount; i++) {
const y = i * rowSize
drawLine([0, y, canvasSize.width, y])
}
}
const makeCols = () => {
const colsCount = canvasSize.width / colSize
for (let i = 0; i < colsCount; i++) {
const x = i * colSize
drawLine([x, 0, x, canvasSize.height])
}
}
makeRows()
makeCols()
}
function initFrames(frames) {
const { width: cellW, height: cellH } = cellDimensions
frames.forEach(({ top, left, width, height }) => {
const frame = createFrame(
{ x: left, y: top },
{ width: width, height: height }
)
frame.set({
cellX: width / cellW,
cellY: height / cellH,
lockScalingFlip: true,
})
drawingCanvas.add(frame)
})
}
function createFrame({ x, y }, { width, height } = { width: 0, height: 0 }) {
const rect = new fabric.Rect({
top: y,
left: x,
width,
height,
stroke: 'gray',
borderStrokeWidth: borderStrokeWidth,
fill: 'white',
type: objectTypes.frame,
strokeUniform: true,
noScaleCache: false,
objectCaching: false,
})
drawingCanvas.setActiveObject(rect)
return rect
}
<canvas id="canvas"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.4.0/fabric.min.js"></script>