minecards-renderer
Version:
A library for displaying interactive 3D collectible cards for Telegram bot Minecrads and their web interface.
73 lines (72 loc) • 4.01 kB
JavaScript
import * as h from "skin3d";
class l {
constructor(e, t) {
this.container = e, this.data = t, this.elements = {}, this.skinViewer = null, this.animationFrameId = null;
const i = this.container.getBoundingClientRect();
this.width = i.width, this.height = i.height, this.container.style.perspective = "1500px", this.handleMouseMove = this.handleMouseMove.bind(this), this.handleMouseLeave = this.handleMouseLeave.bind(this), this.init();
}
async init() {
this.createDOM(), await this.applyData(), this.setupSkinViewer(), this.setupInteractions();
}
createDOM() {
this.container.innerHTML = `
<div class="card">
<div class="glare"></div>
<canvas class="card-skin-viewer"></canvas>
<img class="card-pack-logo" alt="Pack Logo">
<div class="card-sticker-layer"></div>
</div>
`, this.elements.card = this.container.querySelector(".card"), this.elements.skinCanvas = this.container.querySelector(".card-skin-viewer"), this.elements.packLogo = this.container.querySelector(".card-pack-logo"), this.elements.stickerLayer = this.container.querySelector(".card-sticker-layer");
}
async applyData() {
if (this.elements.card.classList.add(`rarity-${this.data.rarity}`), this.elements.card.style.backgroundImage = `url('${this.data.backgroundImage}')`, this.elements.packLogo.src = this.data.packLogoImage, this.data.stickerUrl)
try {
const e = await fetch(this.data.stickerUrl);
if (!e.ok)
throw new Error(`HTTP error! status: ${e.status}`);
const t = await e.text();
this.elements.stickerLayer.innerHTML = t;
} catch (e) {
console.error(`Could not load sticker from ${this.data.stickerUrl}:`, e);
}
}
setupSkinViewer() {
this.skinViewer = new h.View({
canvas: this.elements.skinCanvas,
width: this.width,
height: this.height,
background: null
}), this.skinViewer.controls.enableRotate = !1, this.skinViewer.controls.enableZoom = !1;
const e = this.height / 560;
this.skinViewer.camera.fov = 75 - this.width / this.height * 10, this.skinViewer.camera.updateProjectionMatrix(), this.skinViewer.camera.position.set(-25 * e, 35 * e, 50 * e), this.skinViewer.camera.lookAt(0, 0, 0), this.skinViewer.loadSkin(this.data.skinImage).then(() => {
const t = this.skinViewer.playerObject;
t.scale.set(e, e, e);
const i = t.skin;
i.leftArm.rotation.x = -Math.PI / 4, i.rightArm.rotation.x = Math.PI / 4, i.leftLeg.rotation.x = Math.PI / 5, i.rightLeg.rotation.x = -Math.PI / 5;
const n = Math.PI / 6, a = 0.5, s = (r) => {
if (!this.skinViewer) return;
const o = n * Math.sin(r / 1e3 * a);
i.rotation.y = o, this.animationFrameId = requestAnimationFrame(s);
};
this.animationFrameId = requestAnimationFrame(s);
}).catch((t) => {
console.error("Failed to load skin:", t);
});
}
handleMouseMove(e) {
const t = this.container.getBoundingClientRect(), i = e.clientX - t.left, n = e.clientY - t.top, a = t.width / 2, s = t.height / 2, r = (n - s) / s * -10, o = (i - a) / a * 10;
this.elements.card.style.transform = `rotateX(${r}deg) rotateY(${o}deg)`;
}
handleMouseLeave() {
this.elements.card.style.transform = "rotateX(0deg) rotateY(0deg)";
}
setupInteractions() {
this.container.addEventListener("mousemove", this.handleMouseMove), this.container.addEventListener("mouseleave", this.handleMouseLeave);
}
destroy() {
console.log("Destroying card instance..."), this.container.removeEventListener("mousemove", this.handleMouseMove), this.container.removeEventListener("mouseleave", this.handleMouseLeave), this.animationFrameId && cancelAnimationFrame(this.animationFrameId), this.skinViewer && typeof this.skinViewer.dispose == "function" && this.skinViewer.dispose(), this.skinViewer = null, this.container.innerHTML = "", this.elements = {};
}
}
export {
l as Card
};