Tldraw/examples/core-example-advanced/src/state/actions/transform/rotateSelectedShapes.ts

72 wiersze
2.4 KiB
TypeScript

import { TLPointerInfo, Utils } from '@tldraw/core'
import Vec from '@tldraw/vec'
import { getShapeUtils } from 'shapes'
import type { ArrowShape } from 'shapes/arrow'
import type { Action } from 'state/constants'
import { mutables } from 'state/mutables'
export const rotateSelectedShapes: Action = (data, payload: TLPointerInfo) => {
const { initialPoint, snapshot } = mutables
const { selectedIds } = data.pageState
const initialCommonCenter = Utils.getBoundsCenter(
Utils.getCommonBounds(
selectedIds
.map((id) => snapshot.page.shapes[id])
.map((shape) => getShapeUtils(shape).getBounds(shape))
)
)
const initialAngle = Vec.angle(initialCommonCenter, initialPoint)
const currentAngle = Vec.angle(initialCommonCenter, mutables.currentPoint)
let angleDelta = currentAngle - initialAngle
if (payload.shiftKey) {
angleDelta = Utils.snapAngleToSegments(angleDelta, 24)
}
selectedIds.forEach((id) => {
const initialShape = snapshot.page.shapes[id]
let initialAngle = 0
if (payload.shiftKey) {
const { rotation = 0 } = initialShape
initialAngle = Utils.snapAngleToSegments(rotation, 24) - rotation
}
const shape = data.page.shapes[id]
const utils = getShapeUtils(initialShape)
const initialShapeCenter = utils.getCenter(initialShape)
const relativeCenter = Vec.sub(initialShapeCenter, initialShape.point)
const rotatedCenter = Vec.rotWith(initialShapeCenter, initialCommonCenter, angleDelta)
if (shape.handles) {
// Don't rotate shapes with handles; instead, rotate the handles
Object.values(shape.handles).forEach((handle) => {
handle.point = Vec.rotWith(
initialShape.handles![handle.id as keyof ArrowShape['handles']].point,
relativeCenter,
angleDelta
)
})
const handlePoints = {
start: [...shape.handles.start.point],
end: [...shape.handles.end.point],
}
const offset = Utils.getCommonTopLeft([handlePoints.start, handlePoints.end])
shape.handles.start.point = Vec.sub(handlePoints.start, offset)
shape.handles.end.point = Vec.sub(handlePoints.end, offset)
shape.point = Vec.add(Vec.sub(rotatedCenter, relativeCenter), offset)
} else {
shape.point = Vec.sub(rotatedCenter, relativeCenter)
shape.rotation = (initialShape.rotation || 0) + angleDelta + initialAngle
}
})
}