UNPKG

vue-icon-cloud

Version:

A Vue component for rendering an interactive img or word cloud on canvas

136 lines (135 loc) 5.79 kB
import { defineComponent as G, ref as m, reactive as q, onMounted as J, onUnmounted as K, createElementBlock as N, openBlock as Q, h as V } from "vue"; import { renderToString as tt } from "vue/server-renderer"; const at = /* @__PURE__ */ G({ __name: "cloud", props: { icons: {}, images: {}, iconStyle: {} }, setup(O) { const g = O, d = m(null), _ = m([]), U = q({ x: 0, y: 0 }), X = m(!1), v = q({ x: 0, y: 0 }), Y = q({ x: 0, y: 0 }), h = m(null), A = m(), o = q(U), y = m([]), I = m([]); function Z(t) { return 1 - Math.pow(1 - t, 3); } async function j() { if (!g.icons && !g.images) return; const t = g.icons || g.images || []; I.value = new Array(t.length).fill(!1), y.value = await Promise.all(t.map(async (e, c) => { const a = document.createElement("canvas"); a.width = 40, a.height = 40; const l = a.getContext("2d"); if (l) if (g.images) { const s = new Image(); s.crossOrigin = "anonymous", s.src = t[c], s.onload = () => { l.clearRect(0, 0, a.width, a.height), l.beginPath(), l.arc(20, 20, 20, 0, Math.PI * 2), l.closePath(), l.drawImage(s, 0, 0, 40, 40), I.value[c] = !0; }; } else { const n = Object.assign({ "font-size": "32px" }, g.iconStyle || {}); let f = await tt(V(e, { xmlns: "http://www.w3.org/2000/svg", ...n })); const i = new Image(); i.src = "data:image/svg+xml;base64," + btoa(f), i.onload = () => { a.height = i.height, a.style.height = i.height + "px", a.width = i.width, a.style.width = i.width + "px", l.drawImage(i, 0, 0), I.value[c] = !0; }; } return a; })); } function H() { const t = g.icons || g.images || [], e = [], c = t.length || 20, a = 2 / c, l = Math.PI * (3 - Math.sqrt(5)); for (let s = 0; s < c; s++) { const n = s * a - 1 + a / 2, f = Math.sqrt(1 - n * n), i = s * l, r = Math.cos(i) * f, u = Math.sin(i) * f; e.push({ x: r * 100, y: n * 100, z: u * 100, scale: 1, opacity: 1, id: s }); } _.value = e; } const L = (t) => { var s; const e = (s = d.value) == null ? void 0 : s.getBoundingClientRect(); if (!e || !d.value) return; const c = t.clientX - e.left, a = t.clientY - e.top; d.value.getContext("2d") && (_.value.forEach((n) => { const f = Math.cos(o.x), i = Math.sin(o.x), r = Math.cos(o.y), u = Math.sin(o.y), x = n.x * r - n.z * u, P = n.x * u + n.z * r, z = n.y * f + P * i, C = d.value.width / 2 + x, B = d.value.height / 2 + z, S = 20 * ((P + 200) / 300), M = c - C, b = a - B; if (M * M + b * b < S * S) { const w = -Math.atan2( n.y, Math.sqrt(n.x * n.x + n.z * n.z) ), p = Math.atan2(n.x, n.z), T = o.x, k = o.y, F = Math.sqrt( Math.pow(w - T, 2) + Math.pow(p - k, 2) ), $ = Math.min(2e3, Math.max(800, F * 1e3)); h.value = { x: w, y: p, startX: T, startY: k, distance: F, startTime: performance.now(), duration: $ }; return; } }), X.value = !0, v.x = t.clientX, v.y = t.clientY); }, W = (t) => { var c; const e = (c = d.value) == null ? void 0 : c.getBoundingClientRect(); if (e && (Y.x = t.clientX - e.left, Y.y = t.clientY - e.top), X.value) { const a = t.clientX - v.x, l = t.clientY - v.y; o.x += l * 2e-3, o.y += a * 2e-3, v.x = t.clientX, v.y = t.clientY; } }, D = () => { X.value = !1; }, E = () => { const t = d.value, e = t == null ? void 0 : t.getContext("2d"); if (!t || !e) return; e.clearRect(0, 0, t.width, t.height); const c = t.width / 2, a = t.height / 2, l = Math.sqrt(c * c + a * a), s = Y.x - c, n = Y.y - a, i = 3e-3 + Math.sqrt(s * s + n * n) / l * 0.01; if (h.value) { const r = performance.now() - h.value.startTime, u = Math.min(1, r / h.value.duration), x = Z(u); o.x = h.value.startX + (h.value.x - h.value.startX) * x, o.y = h.value.startY + (h.value.y - h.value.startY) * x, u >= 1 && (h.value = null); } else X.value || (o.x += n / t.height * i, o.y += s / t.width * i); _.value.forEach((r, u) => { const x = Math.cos(o.x), P = Math.sin(o.x), z = Math.cos(o.y), C = Math.sin(o.y), B = r.x * z - r.z * C, R = r.x * C + r.z * z, S = r.y * x + R * P, M = (R + 200) / 300, b = Math.max(0.2, Math.min(1, (R + 150) / 200)); if (e.save(), e.translate(t.width / 2 + B, t.height / 2 + S), e.scale(M, M), e.globalAlpha = b, g.icons || g.images) { if (y.value[u] && I.value[u]) { const w = y.value[u].width, p = y.value[u].height; e.drawImage(y.value[u], -w / 2, -p / 2, w, p); } } else e.beginPath(), e.arc(0, 0, 20, 0, Math.PI * 2), e.fillStyle = "#4444ff", e.fill(), e.fillStyle = "white", e.textAlign = "center", e.textBaseline = "middle", e.font = "16px Arial", e.fillText(`${r.id + 1}`, 0, 0); e.restore(); }), A.value = requestAnimationFrame(E); }; return J(async () => { await j(), H(), E(); }), K(() => { A.value && cancelAnimationFrame(A.value); }), (t, e) => (Q(), N("canvas", { ref_key: "canvasRef", ref: d, width: 300, height: 300, onMousedown: L, onMousemove: W, onMouseup: D, onMouseleave: D, "aria-label": "Interactive 3D Icon Cloud", role: "img" }, null, 544)); } }); export { at as default };