UNPKG

vue-icon-cloud

Version:

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

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