minecards-renderer
Version:
A library for displaying interactive 3D collectible cards for Telegram bot Minecrads and their web interface.
9 lines (8 loc) • 3.99 kB
JavaScript
(function(s,a){typeof exports=="object"&&typeof module<"u"?a(exports,require("skin3d")):typeof define=="function"&&define.amd?define(["exports","skin3d"],a):(s=typeof globalThis<"u"?globalThis:s||self,a(s.MinecardsRenderer={},s.Skin3D))})(this,function(s,a){"use strict";function d(n){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const t in n)if(t!=="default"){const i=Object.getOwnPropertyDescriptor(n,t);Object.defineProperty(e,t,i.get?i:{enumerable:!0,get:()=>n[t]})}}return e.default=n,Object.freeze(e)}const u=d(a);class m{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 u.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 c=Math.PI/6,o=.5,r=h=>{if(!this.skinViewer)return;const l=c*Math.sin(h/1e3*o);i.rotation.y=l,this.animationFrameId=requestAnimationFrame(r)};this.animationFrameId=requestAnimationFrame(r)}).catch(t=>{console.error("Failed to load skin:",t)})}handleMouseMove(e){const t=this.container.getBoundingClientRect(),i=e.clientX-t.left,c=e.clientY-t.top,o=t.width/2,r=t.height/2,h=(c-r)/r*-10,l=(i-o)/o*10;this.elements.card.style.transform=`rotateX(${h}deg) rotateY(${l}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={}}}s.Card=m,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});