vue-icon-cloud
Version:
A Vue component for rendering an interactive img or word cloud on canvas
136 lines (135 loc) • 5.79 kB
JavaScript
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
};