Picto Free Tailwind CSS Portfolio Website Template
Picto is a well-designed portfolio template perfect for developers, freelancers, and creative professionals who want to present their work with elegan…
Moveable JavaScript library is a JavaScript library that makes an element draggable, resizable, scalable, scrollable, rotatable, roundable, clippable, and wrappable.
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);