UNPKG

vue-grabbing-box

Version:

vue-grabbing-box is a container component that allows you to grab to change view-box and also with scaling

416 lines (415 loc) 13.3 kB
import { computed as v, openBlock as T, createElementBlock as j, createElementVNode as d, normalizeClass as b, unref as p, withModifiers as D, toDisplayString as J, pushScopeId as Q, popScopeId as tt, ref as I, reactive as et, onMounted as at, onBeforeUnmount as ot, createBlock as nt, normalizeStyle as st, createCommentVNode as lt, renderSlot as rt } from "vue"; const Dt = "1.0.0"; function $() { const r = navigator.userAgent; return !["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"].some((o) => r.includes(o)); } function ct(r, f = 50) { let o = 0; return function(...i) { if (typeof r != "function") throw new Error(`${r} is not a function`); const c = this; if (!(Date.now() - o < f)) return o = Date.now(), r.apply(c, i); }; } const H = (r, f) => { const o = r.__vccOpts || r; for (const [i, c] of f) o[i] = c; return o; }, L = (r) => (Q("data-v-a9729596"), r = r(), tt(), r), it = { class: "scale-btn-group flex items-center" }, ut = ["onClick"], ft = /* @__PURE__ */ L(() => /* @__PURE__ */ d("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24" }, [ /* @__PURE__ */ d("path", { fill: "currentColor", d: "M18 12.998h-5v5a1 1 0 0 1-2 0v-5H6a1 1 0 0 1 0-2h5v-5a1 1 0 0 1 2 0v5h5a1 1 0 0 1 0 2z" }) ], -1)), dt = [ ft ], mt = { style: { display: "inline-block", margin: "0 8px" } }, gt = ["onClick"], pt = /* @__PURE__ */ L(() => /* @__PURE__ */ d("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24" }, [ /* @__PURE__ */ d("path", { fill: "currentColor", d: "M18 12.998H6a1 1 0 0 1 0-2h12a1 1 0 0 1 0 2z" }) ], -1)), ht = [ pt ], yt = ["onClick"], vt = { __name: "index", props: { // 当前缩放值 scaling: { type: Number, default: 100 }, // scale最大百分比 maxScale: { type: Number, default: 200 }, // scale最小百分比 minScale: { type: Number, default: 20 } }, emits: ["zoom-in", "zoom-out", "reset"], setup(r, { emit: f }) { const o = r, i = v(() => o.scaling >= o.maxScale), c = v(() => o.scaling <= o.minScale), e = () => { f("zoom-in"); }, S = () => { f("zoom-out"); }, h = () => { f("reset"); }; return (P, B) => (T(), j("div", it, [ d("div", { class: b(["scale-button btn-plus flex justify-center items-center", { "is-disabled": p(i) }]), onClick: D(e, ["stop"]) }, dt, 10, ut), d("div", mt, J(r.scaling) + "%", 1), d("div", { class: b(["scale-button btn-minus flex justify-center items-center", { "is-disabled": p(c) }]), onClick: D(S, ["stop"]) }, ht, 10, gt), d("div", { class: "scale-button btn-reset", onClick: D(h, ["stop"]) }, "重置", 8, yt) ])); } }, bt = /* @__PURE__ */ H(vt, [["__scopeId", "data-v-a9729596"]]); const St = { data() { return {}; } }, _t = /* @__PURE__ */ Object.assign(St, { __name: "grabbing-box", props: { useCapture: { type: Boolean, default: !0 }, // scale最大百分比 maxScale: { type: Number, default: 200 }, // scale最小百分比 minScale: { type: Number, default: 20 }, // 初始化为指定比例 initScale: { type: Number, default: 100 }, // 是否显示缩放按钮组 scaleButtons: { type: Boolean, default: !0 }, // Scale buttons position scaleButtonsPosition: { type: String, default: "top right" }, scaleButtonsSpaceX: { type: String, default: "10px" }, scaleButtonsSpaceY: { type: String, default: "20px" }, // 点击缩放按钮时的步长 scaleStep: { type: Number, default: 10 }, // 节流函数的时间间隔 throttleSpan: { type: Number, default: 50 }, /** * [#7](https://github.com/CoderMonkie/vue-grabbing-box/issues/7) * 阻止 drag 时的 click 事件 * * 默认当 drag 后不触发 click 事件 * 如有特殊需要,将`emitClickOnDrag`设为`true` */ emitClickOnDrag: { type: Boolean, default: !1 } }, emits: ["zoom-in", "zoom-out", "reset", "move", "update"], setup(r, { emit: f }) { const o = r, i = I(null), c = I(null), e = et({ lastTransformData: { scale: 1, translateX: 0, translateY: 0 }, lastPosition: { x: null, y: null, distance: 0 // 两指触点之间的距离 }, readyToDrag: !1, readyToScale: !1, dragging: !1, isScaling: !1, // 缩放导致偏移的比例 scaleTranslateProportion: [0, 0], // TODO dragged: !1 // fix #7 完成一次拖拽后标记为true,控制 click 事件是否取消冒泡等,适时重置 }), S = v(() => { const { scale: t } = e.lastTransformData; return parseInt(t * 1e4 / 100); }), h = v(() => { const t = (o.scaleButtonsPosition || "").trim().toLowerCase(); if (!t) return { right: !0, top: !0, left: !1, bottom: !1 }; const a = t.includes("left") ? "left" : "right", n = t.includes("bottom") ? "bottom" : "top"; return { top: n === "top", bottom: n === "bottom", left: a === "left", right: a === "right" }; }), P = () => { if (o.maxScale < o.minScale) throw new Error("[Props Error]maxScale should greater than minSacle"); let t = o.initScale; t > o.maxScale ? t = o.maxScale : t < o.minScale && (t = o.minScale), e.lastTransformData.scale = N( t / 100 ), m(c.value); }, B = () => { P(); const t = k(); Object.entries(t).forEach((s) => { const [l, u] = s; _(document, l, u, { passive: !1, capture: !1 }); }); const a = X(), n = Object.keys(t); Object.entries(a).forEach((s) => { const [l, u] = s, g = n.includes(l) ? u : ct(u, o.throttleSpan); _(i.value, l, g, { capture: o.useCapture, passive: !1 }); }); }, k = () => $() ? { wheel: (t) => { t.ctrlKey && Y(t); } } : { touchmove: Y }, X = () => $() ? { mousedown: W, mousemove: V, mouseup: A, mouseleave: G, wheel: q } : { touchstart: O, touchmove: K, touchend: U }, Y = (t) => { t.preventDefault(); }, R = () => { e.lastTransformData = { scale: 1, translateX: 0, translateY: 0 }, m(c.value), f("reset"); }, C = () => { const { scale: t } = e.lastTransformData, a = (t * 100 + o.scaleStep) / 100; e.lastTransformData.scale = Math.min(a, o.maxScale / 100), m(c.value), f("zoom-in", e.lastTransformData); }, z = () => { const { scale: t } = e.lastTransformData, a = (t * 100 - o.scaleStep) / 100; e.lastTransformData.scale = Math.max(a, o.minScale / 100), m(c.value), f("zoom-out", e.lastTransformData); }, _ = (t, a, n, s) => { t.addEventListener(a, n, s); }, x = (t, a, n) => { t.removeEventListener(a, n); }, E = (t, a) => { const { translateX: n, translateY: s } = e.lastTransformData, { deltaX: l, deltaY: u } = a, [g, F] = [n + l, s + u]; e.lastTransformData = { ...e.lastTransformData, translateX: g, translateY: F }, m(t), f("move", e.lastTransformData); }, m = (t) => { const { scale: a, translateX: n, translateY: s } = e.lastTransformData, l = N(a); t.style.transform = `matrix(${l}, 0, 0, ${l}, ${n}, ${s})`, e.lastTransformData.scale = l, f("update", e.lastTransformData); }, M = (t) => { !o.emitClickOnDrag && e.dragged && (t.stopPropagation(), t.preventDefault()); }, W = (t) => { e.readyToDrag = !0, e.lastPosition = { x: t.clientX, y: t.clientY }, o.emitClickOnDrag || (e.dragged = !1, _(i.value, "click", M, { capture: !0 })); }, V = (t) => { if (e.readyToDrag) { t.stopPropagation(), t.preventDefault(), e.dragging = !0; const { clientX: a, clientY: n } = t, { x: s, y: l } = e.lastPosition, [u, g] = [a - s, n - l]; E(c.value, { deltaX: u, deltaY: g }), e.lastPosition = { x: a, y: n }, o.emitClickOnDrag || (e.dragged = !0); } }, A = () => { e.readyToDrag = !1, e.dragging = !1, x(i.value, "click", M); }, G = () => { }, q = (t) => { const { ctrlKey: a, wheelDelta: n } = t; if (t.preventDefault(), t.stopPropagation(), a) n > 0 ? C() : z(); else { const { deltaMode: s, wheelDeltaX: l, wheelDeltaY: u } = t; s === 0 && (e.lastTransformData.translateX += l, e.lastTransformData.translateY += u), m(c.value); } }, O = (t) => { if (t.touches.length === 1) { const { clientX: a, clientY: n } = t.touches[0]; e.lastPosition = { x: a, y: n }, e.readyToDrag = !0, e.dragging = !1, e.readyToScale = !1; } else if (t.touches.length === 2) { e.readyToScale = !0, e.readyToDrag = !1, e.dragging = !1; const [a, n] = t.touches, s = a.clientX - n.clientX, l = a.clientY - n.clientY; e.lastPosition.distance = Math.sqrt(s * s + l * l); const u = [ (a.clientX + s / 2 - e.lastTransformData.x) / e.lastTransformData.scale, (a.clientY + l / 2 - e.lastTransformData.y) / e.lastTransformData.scale ]; e.scaleTranslateProportion = [ u[0] / c.offsetWidth, u[1] / c.offsetHeight ]; } }, K = (t) => { if (t.preventDefault(), t.stopPropagation(), t.touches.length === 1 && e.readyToDrag) { e.dragging = !0; const { clientX: a, clientY: n } = t.touches[0], { x: s, y: l } = e.lastPosition, [u, g] = [a - s, n - l]; E(c.value, { deltaX: u, deltaY: g }), e.lastPosition = { ...e.lastPosition, x: a, y: n }; } else if (t.touches.length === 2) { if (!e.readyToScale) { O(t); return; } const [a, n] = t.touches, s = Math.sqrt( (a.clientX - n.clientX) * (a.clientX - n.clientX) + (a.clientY - n.clientY) * (a.clientY - n.clientY) ); Z(s / e.lastPosition.distance, !1), e.lastPosition.distance = s; } }, U = (t) => { e.readyToDrag = !1, e.dragging = !1, e.readyToScale = !1, e.isScaling = !1, e.lastPosition = { x: 0, y: 0, distance: 0 }; }, Z = (t, a = !0) => { if (t === 0 && t === 1) return; const n = [ c.offsetWidth * e.lastTransformData.scale, c.offsetHeight * e.lastTransformData.scale ]; let s = e.scaleTranslateProportion; if (a) { const l = [ (i.offsetWidth / 2 - e.lastTransformData.x) / e.lastTransformData.scale, (i.offsetHeight / 2 - e.lastTransformData.y) / e.lastTransformData.scale ]; s = [ l[0] / i.offsetWidth, l[1] / i.offsetHeight ]; } e.lastTransformData.x += n[0] * (1 - t) * s[0] || 0, e.lastTransformData.y += n[1] * (1 - t) * s[1] || 0, e.lastTransformData.scale *= t, m(c.value); }, N = (t) => (t * 100 > o.maxScale ? t = o.maxScale / 100 : t * 100 < o.minScale && (t = o.minScale / 100), t); return at(() => { B(); }), ot(() => { Object.entries(X()).forEach((t) => { const [a, n] = t; x(i.value, a, n); }), Object.entries(k()).forEach((t) => { const [a, n] = t; x(document, a, n); }); }), (t, a) => (T(), j("div", { ref_key: "containerRef", ref: i, class: b(["grabbing-box-container w-full h-full overflow-hidden flex flex-col relative", { "cursor-grab": !e.dragging, "cursor-grabbing": e.dragging }]) }, [ o.scaleButtons ? (T(), nt(bt, { key: 0, scaling: p(S), onZoomIn: C, onZoomOut: z, onReset: R, class: b(["scale-button-group", { "custom-position-top": p(h).top, "custom-position-bottom": p(h).bottom, "custom-position-left": p(h).left, "custom-position-right": p(h).right }]), style: st(` --scale-buttons-space-x: ${o.scaleButtonsSpaceX}; --scale-buttons-space-y: ${o.scaleButtonsSpaceY}; `) }, null, 8, ["scaling", "class", "style"])) : lt("", !0), d("div", { ref_key: "contentBoxRef", ref: c, class: "grabbing-box w-full h-full", style: { "transform-origin": "center center", "user-select": "none", transform: "matrix(1, 0, 0, 1, 0, 0)" } }, [ rt(t.$slots, "default", {}, void 0, !0) ], 512) ], 2)); } }), y = /* @__PURE__ */ H(_t, [["__scopeId", "data-v-58cc9f3c"]]); y.name = "GrabbingBox"; y.install = function(r) { r.component(y.name, y); }; const w = (r) => { w.installed || (w.installed = !0, r.use(y)); }; typeof window < "u" && window.Vue && w(window.Vue); export { y as GrabbingBox, w as default, Dt as version };