import { Renderer, ClippableProps, OnClip, ClippableState, OnClipEnd, OnClipStart, ControlPose, MoveableManagerInterface, DraggableProps, } from "../types"; import { convertUnitSize, getRad } from "@daybrush/utils"; import { prefix, calculatePosition, getDiagonalSize, fillParams, triggerEvent, makeMatrixCSS, getRect, fillEndParams, getSizeDistByDist, getProps, fillCSSObject, abs, sign, } from "../utils"; import { plus, minus, multiply } from "@scena/matrix"; import { getDragDist, calculatePointerDist, setDragStart } from "../gesto/GestoUtils"; import { HORIZONTAL_RADIUS_ORDER, VERTICAL_RADIUS_ORDER, addRadiusPos, removeRadiusPos, } from "./roundable/borderRadius"; import { renderLine } from "../renderDirections"; import { checkSnapBoundPriority } from "./snappable/snap"; import { checkSnapBounds } from "./snappable/snapBounds"; import { getDefaultGuidelines } from "./snappable/getTotalGuidelines"; import { getControlSize, getClipPath, getClipStyles, getRectPoses, } from "./clippable/utils"; export function moveControlPos( controlPoses: ControlPose[], index: number, dist: number[], isRect?: boolean, keepRatio?: boolean, ) { const { direction, sub } = controlPoses[index]; const dists = controlPoses.map(() => [0, 0]); const directions = direction ? direction.split("") : []; if (isRect && index < 8) { const verticalDirections = directions.filter(dir => dir === "w" || dir === "e"); const horizontalDirections = directions.filter(dir => dir === "n" || dir === "s"); const verticalDirection = verticalDirections[0]; const horizontalDirection = horizontalDirections[0]; dists[index] = dist; const [width, height] = getControlSize(controlPoses); const ratio = width && height ? width / height : 0; if (ratio && keepRatio) { // 0 1 2 // 7 3 // 6 5 4 const fixedIndex = (index + 4) % 8; const fixedPosition = controlPoses[fixedIndex].pos; const sizeDirection = [0, 0]; if (direction!.indexOf("w") > -1) { sizeDirection[0] = -1; } else if (direction!.indexOf("e") > -1) { sizeDirection[0] = 1; } if (direction!.indexOf("n") > -1) { sizeDirection[1] = -1; } else if (direction!.indexOf("s") > -1) { sizeDirection[1] = 1; } const nextDist = getSizeDistByDist( [width, height], dist, ratio, sizeDirection, true, ); const nextWidth = width + nextDist[0]; const nextHeight = height + nextDist[1]; let top = fixedPosition[1]; let bottom = fixedPosition[1]; let left = fixedPosition[0]; let right = fixedPosition[0]; if (sizeDirection[0] === -1) { left = right - nextWidth; } else if (sizeDirection[0] === 1) { right = left + nextWidth; } else { left = left - nextWidth / 2; right = right + nextWidth / 2; } if (sizeDirection[1] === -1) { top = bottom - nextHeight; } else if (sizeDirection[1] === 1) { bottom = top + nextHeight; } else { top = bottom - nextHeight / 2; bottom = top + nextHeight; } const nextControlPoses = getRectPoses(top, right, bottom, left); controlPoses.forEach((controlPose, i) => { dists[i][0] = nextControlPoses[i].pos[0] - controlPose.pos[0]; dists[i][1] = nextControlPoses[i].pos[1] - controlPose.pos[1]; }); } else { controlPoses.forEach((controlPose, i) => { const { direction: controlDir, } = controlPose; if (!controlDir) { return; } if (controlDir.indexOf(verticalDirection) > -1) { dists[i][0] = dist[0]; } if (controlDir.indexOf(horizontalDirection) > -1) { dists[i][1] = dist[1]; } }); if (verticalDirection) { dists[1][0] = dist[0] / 2; dists[5][0] = dist[0] / 2; } if (horizontalDirection) { dists[3][1] = dist[1] / 2; dists[7][1] = dist[1] / 2; } } } else if (direction && !sub) { directions.forEach(dir => { const isVertical = dir === "n" || dir === "s"; controlPoses.forEach((controlPose, i) => { const { direction: dirDir, horizontal: dirHorizontal, vertical: dirVertical, } = controlPose; if (!dirDir || dirDir.indexOf(dir) === -1) { return; } dists[i] = [ isVertical || !dirHorizontal ? 0 : dist[0], !isVertical || !dirVertical ? 0 : dist[1], ]; }); }); } else { dists[index] = dist; } return dists; } function addClipPath(moveable: MoveableManagerInterface, e: any) { const [distX, distY] = calculatePointerDist(moveable, e); const { clipPath, clipIndex } = e.datas; const { type: clipType, poses: clipPoses, splitter, } = (clipPath as ReturnType)!; const poses = clipPoses.map(pos => pos.pos); if (clipType === "polygon") { poses.splice(clipIndex, 0, [distX, distY]); } else if (clipType === "inset") { const horizontalIndex = HORIZONTAL_RADIUS_ORDER.indexOf(clipIndex); const verticalIndex = VERTICAL_RADIUS_ORDER.indexOf(clipIndex); const length = clipPoses.length; addRadiusPos( clipPoses, poses, 8, horizontalIndex, verticalIndex, distX, distY, poses[4][0], poses[4][1], poses[0][0], poses[0][1], ); if (length === clipPoses.length) { return; } } else { return; } const clipStyles = getClipStyles(moveable, clipPath, poses)!; const clipStyle = `${clipType}(${clipStyles.join(splitter)})`; triggerEvent(moveable, "onClip", fillParams(moveable, e, { clipEventType: "added", clipType, poses, clipStyles, clipStyle, distX: 0, distY: 0, ...fillCSSObject({ clipPath: clipStyle, }, e), })); } function removeClipPath(moveable: MoveableManagerInterface, e: any) { const { clipPath, clipIndex } = e.datas; const { type: clipType, poses: clipPoses, splitter, } = (clipPath as ReturnType)!; const poses = clipPoses.map(pos => pos.pos); const length = poses.length; if (clipType === "polygon") { clipPoses.splice(clipIndex, 1); poses.splice(clipIndex, 1); } else if (clipType === "inset") { if (clipIndex < 8) { return; } removeRadiusPos(clipPoses, poses, clipIndex, 8, length); if (length === clipPoses.length) { return; } } else { return; } const clipStyles = getClipStyles(moveable, clipPath, poses)!; const clipStyle = `${clipType}(${clipStyles.join(splitter)})`; triggerEvent(moveable, "onClip", fillParams(moveable, e, { clipEventType: "removed", clipType, poses, clipStyles, clipStyle, distX: 0, distY: 0, ...fillCSSObject({ clipPath: clipStyle, }, e), })); } /** * @namespace Moveable.Clippable * @description Whether to clip the target. */ export default { name: "clippable", props: [ "clippable", "defaultClipPath", "customClipPath", "keepRatio", "clipRelative", "clipArea", "dragWithClip", "clipTargetBounds", "clipVerticalGuidelines", "clipHorizontalGuidelines", "clipSnapThreshold", ] as const, events: [ "clipStart", "clip", "clipEnd", ] as const, css: [ `.control.clip-control { background: #6d6; cursor: pointer; } .control.clip-control.clip-radius { background: #d66; } .line.clip-line { background: #6e6; cursor: move; z-index: 1; } .clip-area { position: absolute; top: 0; left: 0; } .clip-ellipse { position: absolute; cursor: move; border: 1px solid #6d6; border: var(--zoompx) solid #6d6; border-radius: 50%; transform-origin: 0px 0px; }`, `:host { --bounds-color: #d66; }`, `.guideline { pointer-events: none; z-index: 2; }`, `.line.guideline.bounds { background: #d66; background: var(--bounds-color); }`, ], render(moveable: MoveableManagerInterface, React: Renderer): any[] { const { customClipPath, defaultClipPath, clipArea, zoom, groupable, } = moveable.props; const { target, width, height, allMatrix, is3d, left, top, pos1, pos2, pos3, pos4, clipPathState, snapBoundInfos, rotation: rotationRad, } = moveable.getState(); if (!target || groupable) { return []; } const clipPath = getClipPath( target, width, height, defaultClipPath || "inset", clipPathState || customClipPath); if (!clipPath) { return []; } const n = is3d ? 4 : 3; const type = clipPath.type; const clipPoses = clipPath.poses; const poses = clipPoses.map(pos => { // return [x, y]; const calculatedPos = calculatePosition(allMatrix, pos.pos, n); return [ calculatedPos[0] - left, calculatedPos[1] - top, ]; }); let controls: any[] = []; let lines: any[] = []; const isRect = type === "rect"; const isInset = type === "inset"; const isPolygon = type === "polygon"; if (isRect || isInset || isPolygon) { const linePoses = isInset ? poses.slice(0, 8) : poses; lines = linePoses.map((to, i) => { const from = i === 0 ? linePoses[linePoses.length - 1] : linePoses[i - 1]; const rad = getRad(from, to); const dist = getDiagonalSize(from, to); return
; }); } controls = poses.map((pos, i) => { return
; }); if (isInset) { controls.push(...poses.slice(8).map((pos, i) => { return
; })); } if (type === "circle" || type === "ellipse") { const { left: clipLeft, top: clipTop, radiusX, radiusY, } = clipPath; const [distLeft, distTop] = minus( calculatePosition(allMatrix, [clipLeft!, clipTop!], n), calculatePosition(allMatrix, [0, 0], n), ); let ellipseClipPath = "none"; if (!clipArea) { const piece = Math.max(10, radiusX! / 5, radiusY! / 5); const areaPoses: number[][] = []; for (let i = 0; i <= piece; ++i) { const rad = Math.PI * 2 / piece * i; areaPoses.push([ radiusX! + (radiusX! - zoom!) * Math.cos(rad), radiusY! + (radiusY! - zoom!) * Math.sin(rad), ]); } areaPoses.push([radiusX!, -2]); areaPoses.push([-2, -2]); areaPoses.push([-2, radiusY! * 2 + 2]); areaPoses.push([radiusX! * 2 + 2, radiusY! * 2 + 2]); areaPoses.push([radiusX! * 2 + 2, -2]); areaPoses.push([radiusX!, -2]); ellipseClipPath = `polygon(${areaPoses.map(pos => `${pos[0]}px ${pos[1]}px`).join(", ")})`; } controls.push(
); } if (clipArea) { const { width: allWidth, height: allHeight, left: allLeft, top: allTop, } = getRect([pos1, pos2, pos3, pos4, ...poses]); if (isPolygon || isRect || isInset) { const areaPoses = isInset ? poses.slice(0, 8) : poses; controls.push(
`${pos[0] - allLeft}px ${pos[1] - allTop}px`).join(", ")})`, }}>
); } } if (snapBoundInfos) { (["vertical", "horizontal"] as const).forEach(directionType => { const info = snapBoundInfos[directionType]; const isHorizontal = directionType === "horizontal"; if (info.isSnap) { lines.push(...info.snap.posInfos.map(({ pos }, i) => { const snapPos1 = minus(calculatePosition( allMatrix, isHorizontal ? [0, pos] : [pos, 0], n), [left, top]); const snapPos2 = minus(calculatePosition( allMatrix, isHorizontal ? [width, pos] : [pos, height], n), [left, top]); return renderLine( React, "", snapPos1, snapPos2, zoom!, `clip${directionType}snap${i}`, "guideline"); })); } if (info.isBound) { lines.push(...info.bounds.map(({ pos }, i) => { const snapPos1 = minus(calculatePosition( allMatrix, isHorizontal ? [0, pos] : [pos, 0], n), [left, top]); const snapPos2 = minus(calculatePosition( allMatrix, isHorizontal ? [width, pos] : [pos, height], n), [left, top]); return renderLine( React, "", snapPos1, snapPos2, zoom!, `clip${directionType}bounds${i}`, "guideline", "bounds", "bold"); })); } }); } return [ ...controls, ...lines, ]; }, dragControlCondition(moveable: any, e: any) { return e.inputEvent && (e.inputEvent.target.getAttribute("class") || "").indexOf("clip") > -1; }, dragStart(moveable: MoveableManagerInterface, e: any) { const props = moveable.props; const { dragWithClip = true, } = props; if (dragWithClip) { return false; } return this.dragControlStart(moveable, e); }, drag(moveable: MoveableManagerInterface, e: any) { return this.dragControl(moveable, { ...e, isDragTarget: true }); }, dragEnd(moveable: MoveableManagerInterface, e: any) { return this.dragControlEnd(moveable, e); }, dragControlStart(moveable: MoveableManagerInterface, e: any) { const state = moveable.state; const { defaultClipPath, customClipPath } = moveable.props; const { target, width, height } = state; const inputTarget = e.inputEvent ? e.inputEvent.target : null; const className = (inputTarget && inputTarget.getAttribute("class")) || ""; const datas = e.datas; const clipPath = getClipPath(target!, width, height, defaultClipPath || "inset", customClipPath); if (!clipPath) { return false; } const { clipText, type, poses } = clipPath; const result = triggerEvent(moveable, "onClipStart", fillParams(moveable, e, { clipType: type, clipStyle: clipText, poses: poses.map(pos => pos.pos), })); if (result === false) { datas.isClipStart = false; return false; } datas.isControl = className && className.indexOf("clip-control") > -1; datas.isLine = className.indexOf("clip-line") > -1; datas.isArea = className.indexOf("clip-area") > -1 || className.indexOf("clip-ellipse") > -1; datas.clipIndex = inputTarget ? parseInt(inputTarget.getAttribute("data-clip-index"), 10) : -1; datas.clipPath = clipPath; datas.isClipStart = true; state.clipPathState = clipText; setDragStart(moveable, e); return true; }, dragControl(moveable: MoveableManagerInterface, e: any) { const { datas, originalDatas, isDragTarget } = e; if (!datas.isClipStart) { return false; } const { isControl, isLine, isArea, clipIndex, clipPath } = datas as { clipPath: ReturnType, [key: string]: any, }; if (!clipPath) { return false; } const props = getProps(moveable.props, "clippable"); const { keepRatio } = props; let distX = 0; let distY = 0; const originalDraggable = originalDatas.draggable; const originalDist = getDragDist(e); if (isDragTarget && originalDraggable) { [distX, distY] = originalDraggable.prevBeforeDist; } else { [distX, distY] = originalDist; } const firstDist = [distX, distY]; const state = moveable.state; const { width, height } = state; const isDragWithTarget = !isArea && !isControl && !isLine; const { type: clipType, poses: clipPoses, splitter, } = clipPath; const poses = clipPoses.map(pos => pos.pos); if (isDragWithTarget) { distX = -distX; distY = -distY; } const isAll = !isControl || clipPoses[clipIndex].direction === "nesw"; const isRect = clipType === "inset" || clipType === "rect"; let dists = clipPoses.map(() => [0, 0]); if (isControl && !isAll) { const { horizontal, vertical } = clipPoses[clipIndex]; const dist = [ distX * abs(horizontal), distY * abs(vertical), ]; dists = moveControlPos(clipPoses, clipIndex, dist, isRect, keepRatio); } else if (isAll) { dists = poses.map(() => [distX, distY]); } const nextPoses: number[][] = poses.map((pos, i) => plus(pos, dists[i])); const guidePoses = [...nextPoses]; state.snapBoundInfos = null; const isCircle = clipPath.type === "circle"; const isEllipse = clipPath.type === "ellipse"; if (isCircle || isEllipse) { const guideRect = getRect(nextPoses); const ry = abs(guideRect.bottom - guideRect.top); const rx = abs(isEllipse ? guideRect.right - guideRect.left : ry); const bottom = nextPoses[0][1] + ry; const left = nextPoses[0][0] - rx; const right = nextPoses[0][0] + rx; // right if (isCircle) { guidePoses.push([right, guideRect.bottom]); dists.push([1, 0]); } // bottom guidePoses.push([guideRect.left, bottom]); dists.push([0, 1]); // left guidePoses.push([left, guideRect.bottom]); dists.push([1, 0]); } const guidelines = getDefaultGuidelines( (props.clipHorizontalGuidelines || []).map(v => convertUnitSize(`${v}`, height)), (props.clipVerticalGuidelines || []).map(v => convertUnitSize(`${v}`, width)), width!, height!, ); let guideXPoses: number[] = []; let guideYPoses: number[] = []; if (isCircle || isEllipse) { guideXPoses = [guidePoses[4][0], guidePoses[2][0]]; guideYPoses = [guidePoses[1][1], guidePoses[3][1]]; } else if (isRect) { const rectPoses = [guidePoses[0], guidePoses[2], guidePoses[4], guidePoses[6]]; const rectDists = [dists[0], dists[2], dists[4], dists[6]]; guideXPoses = rectPoses.filter((_, i) => rectDists[i][0]).map(pos => pos[0]); guideYPoses = rectPoses.filter((_, i) => rectDists[i][1]).map(pos => pos[1]); } else { guideXPoses = guidePoses.filter((_, i) => dists[i][0]).map(pos => pos[0]); guideYPoses = guidePoses.filter((_, i) => dists[i][1]).map(pos => pos[1]); } const boundDelta = [0, 0]; const { horizontal: horizontalSnapInfo, vertical: verticalSnapInfo, } = checkSnapBounds( guidelines, props.clipTargetBounds && { left: 0, top: 0, right: width, bottom: height }, guideXPoses, guideYPoses, 5, ); let snapOffsetY = horizontalSnapInfo.offset; let snapOffsetX = verticalSnapInfo.offset; if (horizontalSnapInfo.isBound) { boundDelta[1] += snapOffsetY; } if (verticalSnapInfo.isBound) { boundDelta[0] += snapOffsetX; } if ((isEllipse || isCircle) && dists[0][0] === 0 && dists[0][1] === 0) { const guideRect = getRect(nextPoses); let cy = guideRect.bottom - guideRect.top; let cx = isEllipse ? guideRect.right - guideRect.left : cy; const distSnapX = verticalSnapInfo.isBound ? abs(snapOffsetX) : (verticalSnapInfo.snapIndex === 0 ? -snapOffsetX : snapOffsetX); const distSnapY = horizontalSnapInfo.isBound ? abs(snapOffsetY) : (horizontalSnapInfo.snapIndex === 0 ? -snapOffsetY : snapOffsetY); cx -= distSnapX; cy -= distSnapY; if (isCircle) { cy = checkSnapBoundPriority(verticalSnapInfo, horizontalSnapInfo) > 0 ? cy : cx; cx = cy; } const center = guidePoses[0]; guidePoses[1][1] = center[1] - cy; guidePoses[2][0] = center[0] + cx; guidePoses[3][1] = center[1] + cy; guidePoses[4][0] = center[0] - cx; } else if (isRect && keepRatio && isControl) { const [width, height] = getControlSize(clipPoses); const ratio = width && height ? width / height : 0; const clipPose = clipPoses[clipIndex]; const direction = clipPose.direction! || ""; let top = guidePoses[1][1]; let bottom = guidePoses[5][1]; let left = guidePoses[7][0]; let right = guidePoses[3][0]; if (abs(snapOffsetY) <= abs(snapOffsetX)) { snapOffsetY = sign(snapOffsetY) * abs(snapOffsetX) / ratio; } else { snapOffsetX = sign(snapOffsetX) * abs(snapOffsetY) * ratio; } if (direction!.indexOf("w") > -1) { left -= snapOffsetX; } else if (direction!.indexOf("e") > -1) { right -= snapOffsetX; } else { left += snapOffsetX / 2; right -= snapOffsetX / 2; } if (direction!.indexOf("n") > -1) { top -= snapOffsetY; } else if (direction!.indexOf("s") > -1) { bottom -= snapOffsetY; } else { top += snapOffsetY / 2; bottom -= snapOffsetY / 2; } const nextControlPoses = getRectPoses(top, right, bottom, left); guidePoses.forEach((pos, i) => { [pos[0], pos[1]] = nextControlPoses[i].pos; }); } else { guidePoses.forEach((pos, j) => { const dist = dists[j]; if (dist[0]) { pos[0] -= snapOffsetX; } if (dist[1]) { pos[1] -= snapOffsetY; } }); } const nextClipStyles = getClipStyles(moveable, clipPath, nextPoses)!; const clipStyle = `${clipType}(${nextClipStyles.join(splitter)})`; state.clipPathState = clipStyle; if (isCircle || isEllipse) { guideXPoses = [guidePoses[4][0], guidePoses[2][0]]; guideYPoses = [guidePoses[1][1], guidePoses[3][1]]; } else if (isRect) { const rectPoses = [guidePoses[0], guidePoses[2], guidePoses[4], guidePoses[6]]; guideXPoses = rectPoses.map(pos => pos[0]); guideYPoses = rectPoses.map(pos => pos[1]); } else { guideXPoses = guidePoses.map(pos => pos[0]); guideYPoses = guidePoses.map(pos => pos[1]); } state.snapBoundInfos = checkSnapBounds( guidelines, props.clipTargetBounds && { left: 0, top: 0, right: width, bottom: height }, guideXPoses, guideYPoses, 1, ); if (originalDraggable) { const { is3d, allMatrix, } = state; const n = is3d ? 4 : 3; let dragDist = boundDelta; if (isDragTarget) { dragDist = [ firstDist[0] + boundDelta[0] - originalDist[0], firstDist[1] + boundDelta[1] - originalDist[1], ]; } originalDraggable.deltaOffset = multiply(allMatrix, [dragDist[0], dragDist[1], 0, 0], n); } triggerEvent(moveable, "onClip", fillParams(moveable, e, { clipEventType: "changed", clipType, poses: nextPoses, clipStyle, clipStyles: nextClipStyles, distX, distY, ...fillCSSObject({ [clipType === "rect" ? "clip" : "clipPath"]: clipStyle, }, e), })); return true; }, dragControlEnd(moveable: MoveableManagerInterface, e: any) { this.unset(moveable); const { isDrag, datas, isDouble } = e; const { isLine, isClipStart, isControl } = datas; if (!isClipStart) { return false; } triggerEvent(moveable, "onClipEnd", fillEndParams(moveable, e, {})); if (isDouble) { if (isControl) { removeClipPath(moveable, e); } else if (isLine) { // add addClipPath(moveable, e); } } return isDouble || isDrag; }, unset(moveable: MoveableManagerInterface) { moveable.state.clipPathState = ""; moveable.state.snapBoundInfos = null; }, }; /** * Whether to clip the target. (default: false) * @name Moveable.Clippable#clippable * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * }); * moveable.on("clipStart", e => { * console.log(e); * }).on("clip", e => { * if (e.clipType === "rect") { * e.target.style.clip = e.clipStyle; * } else { * e.target.style.clipPath = e.clipStyle; * } * }).on("clipEnd", e => { * console.log(e); * }); */ /** * If clippath is not set, the default value can be set. (defaultClipPath < style < customClipPath < dragging clipPath) * @name Moveable.Clippable#defaultClipPath * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * }); * moveable.on("clipStart", e => { * console.log(e); * }).on("clip", e => { * if (e.clipType === "rect") { * e.target.style.clip = e.clipStyle; * } else { * e.target.style.clipPath = e.clipStyle; * } * }).on("clipEnd", e => { * console.log(e); * }); */ /** * % Can be used instead of the absolute px (`rect` not possible) (default: false) * @name Moveable.Clippable#clipRelative * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * }); * moveable.on("clipStart", e => { * console.log(e); * }).on("clip", e => { * if (e.clipType === "rect") { * e.target.style.clip = e.clipStyle; * } else { * e.target.style.clipPath = e.clipStyle; * } * }).on("clipEnd", e => { * console.log(e); * }); */ /** * You can force the custom clipPath. (defaultClipPath < style < customClipPath < dragging clipPath) * @name Moveable.Clippable#customClipPath * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * }); * moveable.on("clipStart", e => { * console.log(e); * }).on("clip", e => { * if (e.clipType === "rect") { * e.target.style.clip = e.clipStyle; * } else { * e.target.style.clipPath = e.clipStyle; * } * }).on("clipEnd", e => { * console.log(e); * }); */ /** * When dragging the target, the clip also moves. (default: true) * @name Moveable.Clippable#dragWithClip * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * }); * moveable.on("clipStart", e => { * console.log(e); * }).on("clip", e => { * if (e.clipType === "rect") { * e.target.style.clip = e.clipStyle; * } else { * e.target.style.clipPath = e.clipStyle; * } * }).on("clipEnd", e => { * console.log(e); * }); */ /** * You can drag the clip by setting clipArea. * @name Moveable.Clippable#clipArea * @default false * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * }); * moveable.on("clipStart", e => { * console.log(e); * }).on("clip", e => { * if (e.clipType === "rect") { * e.target.style.clip = e.clipStyle; * } else { * e.target.style.clipPath = e.clipStyle; * } * }).on("clipEnd", e => { * console.log(e); * }); */ /** * Whether the clip is bound to the target. * @name Moveable.Clippable#clipTargetBounds * @default false * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * clipTargetBounds: true, * }); * moveable.on("clipStart", e => { * console.log(e); * }).on("clip", e => { * if (e.clipType === "rect") { * e.target.style.clip = e.clipStyle; * } else { * e.target.style.clipPath = e.clipStyle; * } * }).on("clipEnd", e => { * console.log(e); * }); */ /** * Add clip guidelines in the vertical direction. * @name Moveable.Clippable#clipVerticalGuidelines * @default 0 * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * clipVerticalGuidelines: [0, 100, 200], * clipHorizontalGuidelines: [0, 100, 200], * clipSnapThreshold: 5, * }); */ /** * Add clip guidelines in the horizontal direction. * @name Moveable.Clippable#clipHorizontalGuidelines * @default [] * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * clipVerticalGuidelines: [0, 100, 200], * clipHorizontalGuidelines: [0, 100, 200], * clipSnapThreshold: 5, * }); */ /** * istance value that can snap to clip guidelines. * @name Moveable.Clippable#clipSnapThreshold * @default 5 * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * clipVerticalGuidelines: [0, 100, 200], * clipHorizontalGuidelines: [0, 100, 200], * clipSnapThreshold: 5, * }); */ /** * When drag start the clip area or controls, the `clipStart` event is called. * @memberof Moveable.Clippable * @event clipStart * @param {Moveable.Clippable.OnClipStart} - Parameters for the `clipStart` event * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * }); * moveable.on("clipStart", e => { * console.log(e); * }).on("clip", e => { * if (e.clipType === "rect") { * e.target.style.clip = e.clipStyle; * } else { * e.target.style.clipPath = e.clipStyle; * } * }).on("clipEnd", e => { * console.log(e); * }); */ /** * When drag the clip area or controls, the `clip` event is called. * @memberof Moveable.Clippable * @event clip * @param {Moveable.Clippable.OnClip} - Parameters for the `clip` event * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * }); * moveable.on("clipStart", e => { * console.log(e); * }).on("clip", e => { * if (e.clipType === "rect") { * e.target.style.clip = e.clipStyle; * } else { * e.target.style.clipPath = e.clipStyle; * } * }).on("clipEnd", e => { * console.log(e); * }); */ /** * When drag end the clip area or controls, the `clipEnd` event is called. * @memberof Moveable.Clippable * @event clipEnd * @param {Moveable.Clippable.OnClipEnd} - Parameters for the `clipEnd` event * @example * import Moveable from "moveable"; * * const moveable = new Moveable(document.body, { * clippable: true, * defaultClipPath: "inset", * customClipPath: "", * clipRelative: false, * clipArea: false, * dragWithClip: true, * }); * moveable.on("clipStart", e => { * console.log(e); * }).on("clip", e => { * if (e.clipType === "rect") { * e.target.style.clip = e.clipStyle; * } else { * e.target.style.clipPath = e.clipStyle; * } * }).on("clipEnd", e => { * console.log(e); * }); */