Movable JavaScript Library Implementation
Younkue Choi January 08, 2023
Moveable JavaScript library is a JavaScript library that makes an element draggable, resizable, scalable, scrollable, rotatable, roundable, clippable, and wrappable.
Moveable js Implementation
Install and import the moveable.
# NPM $ npm install moveable --save
import Moveable from "moveable";
Or load the umd version from the dist folder
<-- or from a cdn -->
Initialize the Moveable library and specify the target element
const moveable = new Moveable(document.body, { target: document.querySelector(".target"), container: null, rootContainer: null, rotatable: false, rotationPosition: "top", roundable: true, roundRelative: false, draggable: false, dragTarget: target, scalable: false, resizable: false, warpable: false, pinchable: true, // ["resizable", "scalable", "rotatable"] pinchThreshold: 0, pinchOutside: true, resizeFormat: v => v, // function to convert size for resize clippable: true, defaultClipPath: "inset", customClipPath: "", clipRelative: false, clipArea: false, dragWithClip: true, triggerAblesSimultaneously: false, snappable: true, snapDirections: { center: true, middle: true }, elementSnapDirections: { top: true, bottom: true, right: true, left: true }, snapThreshold: 5, snapContainer: document.body, snapGridWidth: 5, snapGridHeight: 5, snapDigit: 0, isDisplayInnerSnapDigit: true, snapGap: true, snapDistFormat: self, isDisplaySnapDigit: true, originDraggable: true, scrollable: true, scrollContainer: document.body, scrollThreshold: 0, getScrollPosition: ({ scrollContainer }) => ([scrollContainer.scrollLeft, scrollContainer.scrollTop]), bounds: { left: 0, top: 0, bottom: 1000, right: 1000 }, innerBounds: null, innerBound: null, verticalGuidelines: [100, 200, 300], horizontalGuidelines: [0, 100, 200], elementGuidelines: [document.querySelector(".element")], keepRatio: true, origin: true, padding: null, zoom: 1, dragArea: false, edge: false, // resize, scale events at edges keepRatio: false, clipVerticalGuidelines: [0, 100, 200], clipHorizontalGuidelines: [0, 100, 200], clipSnapThreshold: 5, clipTargetBounds: true, throttleDrag: 0, throttleResize: 0, throttleScale: 0, throttleRotate: 0, rotationTarget: false, throttleDragRotate: 0, renderDirections: ["n", "nw", "ne", "s", "se", "sw", "e", "w"], className: "", defaultGroupRotate: 0 // Sets the initial rotation of the group });
Callback functions
const moveable = new Moveable(document.body, { OnDrag: function () {}, OnDragEnd: function () {}, OnDragGroup: function () {}, OnDragGroupEnd: function () {}, OnDragGroupStart: function () {}, OnDragStart: function () {}, OnPinch: function () {}, OnPinchEnd: function () {}, OnPinchGroup: function () {}, OnPinchGroupEnd: function () {}, OnPinchGroupStart: function () {}, OnPinchStart: function () {}, OnResize: function () {}, OnResizeEnd: function () {}, OnResizeGroup: function () {}, OnResizeGroupEnd: function () {}, OnResizeGroupStart: function () {}, OnResizeStart: function () {}, OnRotate: function () {}, OnRotateEnd: function () {}, OnRotateGroup: function () {}, OnRotateGroupEnd: function () {}, OnRotateGroupStart: function () {},: function () {}, OnRotateStart: function () {}, OnScale: function () {}, OnScaleEnd: function () {}, OnScaleGroup: function () {}, OnScaleGroupEnd: function () {}, OnScaleGroupStart: function () {}, OnScaleStart: function () {}, OnWarp: function () {}, OnWarpEnd: function () {}, OnWarpStart: function () {}, OnRenderStart: function () {}, OnRender: function () {}, OnRenderEnd: function () {}, OnRenderGroupStart: function () {}, OnRenderGroup: function () {}, OnRenderGroupEnd: function () {}, });
Event handlers
moveable.on("click", ({ hasTarget, containsTarget, targetIndex }) => { // If you click on an element other than the target and not included in the target, index is -1. console.log("onClickGroup", target, hasTarget, containsTarget, targetIndex); }); moveable.on("clickGroup", ({ inputTarget, isTarget, containsTarget, targetIndex }) => { // If you click on an element other than the target and not included in the target, index is -1. console.log("onClickGroup", inputTarget, isTarget, containsTarget, targetIndex); }); moveable.on("drag", ({ target, transform }) => { target.style.transform = transform; }); moveable.on("dragEnd", ({ target, isDrag }) => { console.log(target, isDrag); }); moveable.on("dragGroup", ({ targets, events }) => { console.log("onDragGroup", targets); events.forEach(ev => { // drag event console.log("onDrag left, top", ev.left, ev.top); // ev.target!.style.left = `${ev.left}px`; // ev.target!.style.top = `${ev.top}px`; console.log("onDrag translate", ev.dist); ev.target!.style.transform = ev.transform;) }); }); moveable.on("dragGroupEnd", ({ targets, isDrag }) => { console.log("onDragGroupEnd", targets, isDrag); }); moveable.on("dragGroupStart", ({ targets }) => { console.log("onDragGroupStart", targets); }); moveable.on("dragStart", ({ target }) => { console.log(target); }); moveable.on("pinch", ({ target }) => { console.log(target); }); moveable.on("pinchEnd", ({ target }) => { console.log(target); }); moveable.on("pinchGroup", ({ targets, events }) => { console.log("onPinchGroup", targets); }); moveable.on("pinchGroupEnd", ({ targets, isDrag }) => { console.log("onPinchGroupEnd", targets, isDrag); }); moveable.on("pinchGroupStart", ({ targets }) => { console.log("onPinchGroupStart", targets); }); moveable.on("pinchStart", ({ target }) => { console.log(target); }); moveable.on("rotate", ({ target }) => { console.log(target); }); moveable.on("scroll", ({ scrollContainer, direction }) => { scrollContainer.scrollLeft += direction[0] * 10; scrollContainer.scrollTop += direction[1] * 10; }) moveable.on("scrollGroup", ({ scrollContainer, direction }) => { scrollContainer.scrollLeft += direction[0] * 10; scrollContainer.scrollTop += direction[1] * 10; }); moveable.on("scale", ({ target }) => { console.log(target); }); moveable.on("pinchEnd", ({ target }) => { console.log(target); }); moveable.on("rotateEnd", ({ target }) => { console.log(target); }); moveable.on("scaleEnd", ({ target }) => { console.log(target); }); moveable.on("pinchGroup", ({ targets, events }) => { console.log("onPinchGroup", targets); }); moveable.on("pinchGroupEnd", ({ targets, isDrag }) => { console.log("onPinchGroupEnd", targets, isDrag); }); moveable.on("pinchGroupStart", ({ targets }) => { console.log("onPinchGroupStart", targets); }); moveable.on("pinchStart", ({ target }) => { console.log(target); }); moveable.on("render", ({ target }) => { console.log("onRender", target); }); moveable.on("renderEnd", ({ target }) => { console.log("onRenderEnd", target); }); moveable.on("renderGroup", ({ targets }) => { console.log("onRenderGroup", targets); }); moveable.on("renderGroupEnd", ({ targets }) => { console.log("onRenderGroupEnd", targets); }); moveable.on("renderGroupStart", ({ targets }) => { console.log("onRenderGroupStart", targets); }); moveable.on("renderStart", ({ target }) => { console.log("onRenderStart", target); }); moveable.on("resize", ({ target, width, height }) => { target.style.width = `${e.width}px`; target.style.height = `${e.height}px`; }); moveable.on("beforeResize", ({ setFixedDirection }) => { if (shiftKey) { setFixedDirection([0, 0]); } }); moveable.on("resizeEnd", ({ target, isDrag }) => { console.log(target, isDrag); }); moveable.on("resizeGroup", ({ targets, events }) => { console.log("onResizeGroup", targets); events.forEach(ev => { const offset = [ direction[0] < 0 ? -ev.delta[0] : 0, direction[1] < 0 ? -ev.delta[1] : 0, ]; // ev.drag is a drag event that occurs when the group resize. const left = offset[0] + ev.drag.beforeDist[0]; const top = offset[1] + ev.drag.beforeDist[1]; const width = ev.width; const top = ev.top; }); }); moveable.on("resizeGroupEnd", ({ targets, isDrag }) => { console.log("onResizeGroupEnd", targets, isDrag); }); moveable.on("resizeGroupEnd", ({ targets, isDrag }) => { console.log("onResizeGroupEnd", targets, isDrag); }); moveable.on("resizeGroupStart", ({ targets }) => { console.log("onResizeGroupStart", targets); }); moveable.on("resizeStart", ({ target }) => { console.log(target); }); moveable.on("rotate", ({ target, transform, dist }) => { target.style.transform = transform; }); moveable.on("rotateEnd", ({ target, isDrag }) => { console.log(target, isDrag); }); moveable.on("rotateGroup", ({ targets, events }) => { console.log("onRotateGroup", targets); events.forEach(ev => { const target = ev.target; // ev.drag is a drag event that occurs when the group rotate. const left = ev.drag.beforeDist[0]; const top = ev.drag.beforeDist[1]; const deg = ev.beforeDist; }); }); moveable.on("rotateGroupEnd", ({ targets, isDrag }) => { console.log("onRotateGroupEnd", targets, isDrag); }); moveable.on("rotateGroupStart", ({ targets }) => { console.log("onRotateGroupStart", targets); }); moveable.on("rotateStart", ({ target }) => { console.log(target); }); moveable.on("rotateStart", ({ target }) => { console.log(target); }); moveable.on("scale", ({ target, transform, dist }) => { target.style.transform = transform; }); moveable.on("beforeScale", ({ setFixedDirection }) => { if (shiftKey) { setFixedDirection([0, 0]); } }); moveable.on("scaleEnd", ({ target, isDrag }) => { console.log(target, isDrag); }); moveable.on("scaleGroup", ({ targets, events }) => { console.log("onScaleGroup", targets); events.forEach(ev => { const target = ev.target; // ev.drag is a drag event that occurs when the group scale. const left = ev.drag.beforeDist[0]; const top = ev.drag.beforeDist[0]; const scaleX = ev.scale[0]; const scaleX = ev.scale[1]; }); }); moveable.on("scaleGroupEnd", ({ targets, isDrag }) => { console.log("onScaleGroupEnd", targets, isDrag); }); moveable.on("scaleGroupStart", ({ targets }) => { console.log("onScaleGroupStart", targets); }); moveable.on("scaleStart", ({ target }) => { console.log(target); }); moveable.on("scroll", ({ scrollContainer, direction }) => { scrollContainer.scrollLeft += direction[0] * 10; scrollContainer.scrollTop += direction[1] * 10; }); moveable.on("snap", e => { console.log("onSnap", e); }); moveable.on("warp", ({ target, transform, delta, multiply }) => { // target.style.transform = transform; matrix = multiply(matrix, delta); target.style.transform = `matrix3d(${matrix.join(",")})`; }); moveable.on("warpEnd", ({ target, isDrag }) => { console.log(target, isDrag); }); moveable.on("warpStart", ({ target }) => { console.log(target); });
API methods
// destroy moveable.destroy(); // check if is dragging moveable.isDragging(); // check if is moveable element moveable.isMoveableElement(e.target); // check whether an event has been attached to a component. moveable.hasOn(eventName); // indicate whether the event is attached. moveable.hitTest(el); // indicate whether the event is attached. moveable.hitTest(el); // update the shape of the moveable moveable.updateRect(); // change options or properties moveable.setState(state, callbackopt); // triggers a custom event moveable.trigger(eventName, customEventopt, ...restParam); // Instantly Request (requestStart - request - requestEnd) moveable.request("draggable", { deltaX: 10, deltaY: 10, isInstant: true }); // Start move const requester = moveable.request("draggable"); requester.request({ deltaX: 10, deltaY: 10 }); requester.request({ deltaX: 10, deltaY: 10 }); requester.request({ deltaX: 10, deltaY: 10 }); requester.requestEnd(); // update moveable.updateTarget(); // trigger an event moveable.on(event); // executs event just one time moveable.once(event); // unbind an event moveable.off(event);