UNPKG

dragjs

Version:

Simple utility to make it easier to implement drag based things (ie. sliders and such)

202 lines (201 loc) 6.68 kB
/// <reference lib="dom" /> function draggable({ element, handle, xPosition }, cbs) { if (!element) { console.warn("drag is missing elem!"); return; } dragTemplate(element, "touchstart", "touchmove", "touchend", cbs, handle, xPosition); dragTemplate(element, "mousedown", "mousemove", "mouseup", cbs, handle, xPosition); } function xyslider(o) { const twod = div(o["class"] || "", o.parent); const pointer = div("pointer", twod); div("shape shape1", pointer); div("shape shape2", pointer); div("bg bg1", twod); div("bg bg2", twod); draggable({ element: twod }, attachPointer(o.cbs, pointer)); return { background: twod, pointer, }; } function slider(o) { const oned = div(o["class"], o.parent); const pointer = div("pointer", oned); div("shape", pointer); div("bg", oned); draggable({ element: oned }, attachPointer(o.cbs, pointer)); return { background: oned, pointer: pointer, }; } function attachPointer(cbs, pointer) { const ret = {}; Object.entries(cbs).forEach(([name, callback]) => { ret[name] = (p) => { callback({ ...p, pointer }); }; }); return ret; } // move to elemutils lib? function div(klass, p) { return e("div", klass, p); } function e(type, klass, p) { const elem = document.createElement(type); if (klass) { elem.className = klass; } p.appendChild(elem); return elem; } function dragTemplate(elem, down, move, up, cbs, handle, xPosition) { cbs = getCbs(cbs, xPosition); const beginCb = cbs.begin; const changeCb = cbs.change; const endCb = cbs.end; on(handle || elem, down, (e) => { // @ts-ignore Figure out how to type this const moveHandler = (e) => callCb(changeCb, elem, e); function upHandler() { off(document, move, moveHandler); off(document, up, upHandler); // @ts-ignore Figure out how to type this callCb(endCb, elem, e); } on(document, move, moveHandler); on(document, up, upHandler); // @ts-ignore Figure out how to type this callCb(beginCb, elem, e); }); } // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection function on(elem, evt, handler) { // Test via a getter in the options object to see if the passive property is accessed let supportsPassive = false; try { const opts = Object.defineProperty({}, "passive", { get: function () { supportsPassive = true; return undefined; }, }); // @ts-ignore Deno doesn't know this // deno-shim-ignore globalThis.addEventListener("testPassive", null, opts); // @ts-ignore Deno doesn't know this // deno-shim-ignore globalThis.removeEventListener("testPassive", null, opts); } catch (err) { console.error(err); } elem.addEventListener(evt, handler, supportsPassive ? { passive: false } : false); } function off(elem, evt, handler) { elem.removeEventListener(evt, handler, false); } function getCbs(cbs, xPosition = "left") { let initialOffset; let initialPos; const defaultCbs = { begin: (c) => { const bodyWidth = document.body.clientWidth; initialOffset = { x: xPosition === "left" ? c.elem.offsetLeft : bodyWidth - c.elem.offsetLeft - c.elem.clientWidth, y: c.elem.offsetTop, }; initialPos = xPosition === "left" ? c.cursor : { x: bodyWidth - c.cursor.x, y: c.cursor.y }; }, change: (c) => { if (typeof initialOffset.x !== "number" || typeof c.cursor.x !== "number" || typeof initialPos.x !== "number") { return; } const bodyWidth = document.body.clientWidth; style(c.elem, xPosition, xPosition === "left" ? (initialOffset.x + c.cursor.x - initialPos.x) + "px" : (initialOffset.x + (bodyWidth - c.cursor.x) - initialPos.x) + "px"); if (typeof initialOffset.y !== "number" || typeof c.cursor.y !== "number" || typeof initialPos.y !== "number") { return; } style(c.elem, "top", (initialOffset.y + c.cursor.y - initialPos.y) + "px"); }, end: () => { }, }; if (cbs) { return { begin: cbs.begin ? ((args) => { const ret = cbs.begin && cbs.begin(args); if (ret) { // @ts-ignore This is fine return defaultCbs.begin(args); } }) : defaultCbs.begin, change: cbs.change ? ((args) => { const ret = cbs.change && cbs.change(args); if (ret) { // @ts-ignore This is fine return defaultCbs.change(args); } }) : defaultCbs.change, end: cbs.end || defaultCbs.end, }; } return defaultCbs; } function style(e, prop, value) { // @ts-ignore https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style#setting_styles e.style[prop] = value; } function callCb(cb, elem, e) { e.preventDefault(); // http://www.quirksmode.org/js/findpos.html const offset = elem.getBoundingClientRect(); const width = elem.clientWidth; const height = elem.clientHeight; const cursor = { x: cursorX(elem, e), y: cursorY(elem, e), }; if (typeof cursor.x !== "number" || typeof cursor.y !== "number") { return; } const x = (cursor.x - offset.left) / width; const y = (cursor.y - offset.top) / height; cb({ x: isNaN(x) ? 0 : x, y: isNaN(y) ? 0 : y, cursor: cursor, elem, e, }); } // http://javascript.about.com/library/blmousepos.htm function cursorX(_elem, evt) { // https://github.com/bebraw/dragjs/issues/8 if (window.TouchEvent && evt instanceof window.TouchEvent) { return evt.touches.item(0)?.clientX; } return evt.clientX; } function cursorY(_elem, evt) { // https://github.com/bebraw/dragjs/issues/8 if (window.TouchEvent && evt instanceof window.TouchEvent) { return evt.touches.item(0)?.clientY; } return evt.clientY; } export { draggable, slider, xyslider };