UNPKG

gamepadzilla

Version:

A plug-and-play gamepad frontend framework for HTML5 mobile games, supporting both virtual and physical joysticks. No scripting required.

2 lines (1 loc) 8.96 kB
var C;((o)=>{o.Start="touchstart";o.Move="touchmove";o.End="touchend";o.Cancel="touchcancel"})(C||={});var h;((o)=>{o.Joy=".gpz-joy";o.Btn=".gpz-btn";o.Btn4=".gpz-btn4";o.Dpad=".gpz-dpad"})(h||={});function w(e,n){n.fakekeys.forEach((t,r)=>{if(n.stateNew[r]&&!n.stateOld[r])e.dispatchEvent(new e.KeyboardEvent("keydown",t));if(!n.stateNew[r]&&n.stateOld[r])e.dispatchEvent(new e.KeyboardEvent("keyup",t))})}var K={Joy:(e)=>w(window,e),Btn:(e)=>w(window,e),Btn4:(e)=>w(window,e),Dpad:(e)=>w(window,e)};function G(e,n,t){return(e-n.min)*(t.max-t.min)/(n.max-n.min)+t.min}function j(e,n){return Math.min(Math.max(e,n.min),n.max)}function E(e){return G(j(e,{min:0,max:1}),{min:0,max:1},{min:-1,max:1})}function A(e){return G(j(e,{min:-1,max:1}),{min:-1,max:1},{min:0,max:1})}function x(e,n){if(n.length==0)return{dis:0,pos:e};let t=Number.MAX_SAFE_INTEGER,r=n[0];return n.forEach((o)=>{let i=Math.sqrt((o.x-e.x)**2+(o.y-e.y)**2);if(i<t)t=i,r=o}),{dis:t,pos:r}}function z(e,n){let t=n.trim().split(" ").filter((o)=>(o in e)),r=[];return t.forEach((o)=>{r.push({code:o,key:e[o].key,keyCode:e[o].keyCode})}),r}function l(e,n,t){let o=(e?e:n).split(" ");return o.length===t?o:new Array(t).fill(o[0])}function O(e){if(e.stateOld=[...e.stateNew],e.from===1){let t=e.canvas.getBoundingClientRect(),r={x:t.width/2,y:t.height/2},o=x(r,e.fingers),i=E(o.pos.x/t.width),a=E(o.pos.y/t.height);e.axis2d={x:i,y:a}}if(e.axis2d){let{x:t,y:r}=e.axis2d;e.stateNew=[r<-0.18,t<-0.18,r>0.18,t>0.18]}}var F={Joy:O,Dpad:O,Btn4(e){if(e.from==2)e.stateOld=[...e.stateNew],e.stateNew=e.buttons??[];else O(e)},Btn(e){if(e.stateOld=[...e.stateNew],e.from==1){let n=e.canvas.getBoundingClientRect(),t={x:n.width/2,y:n.height/2},r=n.width/e.canvas.width,o=x(t,e.fingers);e.stateNew[0]=o.dis!==0&&o.dis<25*r}if(e.from==2)e.stateNew=e.buttons??[]}};function b(e){return e*Math.PI/180}function v(e,n,t,r){let o=e.reduce((y,s)=>y+s.x,0)/e.length,i=e.reduce((y,s)=>y+s.y,0)/e.length;return e.map((y)=>{let s=y.x*t.x,d=y.y*t.y,c=s-o,u=d-i,k=c*Math.cos(r)-u*Math.sin(r),H=c*Math.sin(r)+u*Math.cos(r),J=k+o+n.x,Z=H+i+n.y;return{x:J,y:Z}})}var g={dpad:[{x:0,y:0},{x:-1,y:-1},{x:-1,y:-3},{x:1,y:-3},{x:1,y:-1},{x:0,y:0}]};function p(e,n,t,r){e.beginPath(),e.arc(r.x,r.y,t,0,2*Math.PI),e.fillStyle=n,e.fill(),e.fillStyle="black",e.stroke(),e.closePath(),e.fillStyle=n}var M={Btn4(e){let n=e.canvas.width/2,t=e.canvas.height/2,r=20,o=[e.stateNew[0]?"yellow":"#88888880",e.stateNew[1]?"blue":"#88888880",e.stateNew[2]?"red":"#88888880",e.stateNew[3]?"green":"#88888880"];e.ctx2d.clearRect(0,0,e.canvas.width,e.canvas.height),p(e.ctx2d,o[0],20,{x:n,y:t/2}),p(e.ctx2d,o[1],20,{x:n/2,y:t}),p(e.ctx2d,o[2],20,{x:n,y:t+t/2}),p(e.ctx2d,o[3],20,{x:n+n/2,y:t})},Btn(e){let n=e.canvas.width/2,t=e.canvas.height/2,r=25,o=e.canvas.dataset.gpzColor??"#88888880",i=e.canvas.dataset.gpzColorAction??"red",a=e.stateNew[0]?i:o;e.ctx2d.clearRect(0,0,e.canvas.width,e.canvas.height),p(e.ctx2d,a,25,{x:n,y:t})},Joy(e){let o={x:e.canvas.width/2,y:e.canvas.height/2},i={x:A(e.axis2d.x)*e.canvas.width,y:A(e.axis2d.y)*e.canvas.height},a=x(o,[i]);if(a.dis/1.5>33.33333333333333){let y=Math.atan2(a.pos.y-o.y,a.pos.x-o.x);a.pos.x=o.x+49.99999999999999*Math.cos(y),a.pos.y=o.y+49.99999999999999*Math.sin(y)}e.ctx2d.clearRect(0,0,e.canvas.width,e.canvas.height),p(e.ctx2d,"#aaaaaa80",50,{x:o.x,y:o.y}),p(e.ctx2d,"#88888880",16.666666666666668,{x:a.pos.x,y:a.pos.y})},Dpad(e){let n=Number(e.canvas.dataset.gpzOffset??6),t=l(e.canvas.dataset.gpzSize,"16",2).map((d)=>Number(d)),r={x:t[0],y:t[1]},o={x:e.canvas.width/2,y:e.canvas.height/2},i=l(e.canvas.dataset.gpzColor,"#aaaaaa80",4),a=l(e.canvas.dataset.gpzColorAction,"#44444480",4),y=e.stateNew.map((d,c)=>d?a[c]:i[c]),s=[v(g.dpad,{x:o.x,y:o.y-n},r,b(0)),v(g.dpad,{x:o.x-n,y:o.y},r,b(270)),v(g.dpad,{x:o.x,y:o.y+n},r,b(180)),v(g.dpad,{x:o.x+n,y:o.y},r,b(90))];e.ctx2d.clearRect(0,0,e.canvas.width,e.canvas.height),s.forEach((d,c)=>{e.ctx2d.beginPath(),e.ctx2d.moveTo(d[0].x,d[0].y),d.forEach((u)=>{e.ctx2d.lineTo(u.x,u.y)}),e.ctx2d.fillStyle=y[c],e.ctx2d.fill(),e.ctx2d.fillStyle="black",e.ctx2d.stroke(),e.ctx2d.closePath()})}};var V={Backspace:{key:"Backspace",keyCode:8},Tab:{key:"Tab",keyCode:9},Enter:{key:"Enter",keyCode:13},ShiftLeft:{key:"Shift",keyCode:16},ControlLeft:{key:"Control",keyCode:17},AltLeft:{key:"Alt",keyCode:18},CapsLock:{key:"CapsLock",keyCode:20},Escape:{key:"Escape",keyCode:27},Space:{key:" ",keyCode:32},PageUp:{key:"PageUp",keyCode:33},PageDown:{key:"PageDown",keyCode:34},End:{key:"End",keyCode:35},Home:{key:"Home",keyCode:36},ArrowLeft:{key:"ArrowLeft",keyCode:37},ArrowUp:{key:"ArrowUp",keyCode:38},ArrowRight:{key:"ArrowRight",keyCode:39},ArrowDown:{key:"ArrowDown",keyCode:40},Delete:{key:"Delete",keyCode:46},Digit0:{key:"0",keyCode:48},Digit1:{key:"1",keyCode:49},Digit2:{key:"2",keyCode:50},Digit3:{key:"3",keyCode:51},Digit4:{key:"4",keyCode:52},Digit5:{key:"5",keyCode:53},Digit6:{key:"6",keyCode:54},Digit7:{key:"7",keyCode:55},Digit8:{key:"8",keyCode:56},Digit9:{key:"9",keyCode:57},KeyA:{key:"a",keyCode:65},KeyB:{key:"b",keyCode:66},KeyC:{key:"c",keyCode:67},KeyD:{key:"d",keyCode:68},KeyE:{key:"e",keyCode:69},KeyF:{key:"f",keyCode:70},KeyG:{key:"g",keyCode:71},KeyH:{key:"h",keyCode:72},KeyI:{key:"i",keyCode:73},KeyJ:{key:"j",keyCode:74},KeyK:{key:"k",keyCode:75},KeyL:{key:"l",keyCode:76},KeyM:{key:"m",keyCode:77},KeyN:{key:"n",keyCode:78},KeyO:{key:"o",keyCode:79},KeyP:{key:"p",keyCode:80},KeyQ:{key:"q",keyCode:81},KeyR:{key:"r",keyCode:82},KeyS:{key:"s",keyCode:83},KeyT:{key:"t",keyCode:84},KeyU:{key:"u",keyCode:85},KeyV:{key:"v",keyCode:86},KeyW:{key:"w",keyCode:87},KeyX:{key:"x",keyCode:88},KeyY:{key:"y",keyCode:89},KeyZ:{key:"z",keyCode:90},F1:{key:"F1",keyCode:112},F2:{key:"F2",keyCode:113},F3:{key:"F3",keyCode:114},F4:{key:"F4",keyCode:115},F5:{key:"F5",keyCode:116},F6:{key:"F6",keyCode:117},F7:{key:"F7",keyCode:118},F8:{key:"F8",keyCode:119},F9:{key:"F9",keyCode:120},F10:{key:"F10",keyCode:121},F11:{key:"F11",keyCode:122},F12:{key:"F12",keyCode:123}};function D(){let e=[];return Object.keys(h).forEach((n)=>{Array.from(document.querySelectorAll(h[n])).filter((t)=>t instanceof HTMLCanvasElement).forEach((t)=>{let r=n!=="Btn"?{x:0,y:0}:null,o=t,i=o.getContext("2d"),a=z(V,t.dataset.gpzBind);e.push({buttons:null,fingers:[],emu:K[n],core:F[n],draw:M[n],type:h[n],from:0,canvas:o,ctx2d:i,axis2d:r,fakekeys:a,stateNew:new Array(a.length).fill(!1),stateOld:new Array(a.length).fill(!1),chain:[]})})}),e}function m(e){e.chain.forEach((n)=>n(e))}function N(e,n){n.forEach((t)=>{t.chain.push(t.core),t.chain.push((r)=>e.requestAnimationFrame(()=>t.draw(r))),t.chain.push(t.emu),m(t)})}function q(e,n,t){let r=(i,a)=>i&&e.stateOld[a]==!1;if(e.stateNew.findIndex(r)!==-1&&t.click){let a=(e.canvas.dataset.gpzVibrate??"200").split(" ").map((y)=>Number(y));n.vibrate(a)}}function L(e,n){let t={click:!1};e.addEventListener("click",()=>{t.click=!0}),n.forEach((r)=>{if("navigator"in e&&"vibrate"in e.navigator)r.chain.push((o)=>q(o,e.navigator,t))})}function T(e,n){let t=n.getBoundingClientRect(),r=e.clientX-t.left,o=e.clientY-t.top;return[{x:r,y:o}]}function I(e,n){let t=[],r=n.getBoundingClientRect();return Array.from(e.touches).forEach((o)=>{let i=o.clientX-r.left,a=o.clientY-r.top;t.push({x:i,y:a})}),t}function R(e){let n=[],t=e.axes.length-1;for(let r=0;r<t;r+=2)if(e.axes[r]!==0||e.axes[r+1]!==0)n.push({x:e.axes[r],y:e.axes[r+1]});return n}function B(e){return e.buttons.map((n)=>n.pressed)}function P(e){return e.getGamepads?.().filter((n)=>n?.connected)??[]}function Q(e,n){if(P(e.navigator).length>0)e.setInterval(n,16.666666666666668)}function S(e,n){let t=0,r=n.filter((d)=>d.type==".gpz-joy"||d.type==".gpz-dpad"),o=n.filter((d)=>d.type==".gpz-btn"||d.type==".gpz-btn4");function i(d){o.forEach((c)=>{if(c.buttons==null)c.buttons=new Array(c.stateNew.length).fill(!1);c.stateNew.forEach((u,k)=>{if(c.buttons&&d[k]!=c.stateNew[k])c.from=2,c.buttons[k]=d[k]}),m(c)})}function a(d){if(d.length>0)t=1,r.forEach((c)=>{c.from=2,c.axis2d=d[0],m(c)});if(d.length===0&&t==1)t=2,r.forEach((c)=>{c.from=2,c.axis2d={x:0,y:0},m(c)})}function y(){P(navigator).forEach((c)=>{let u=B(c),k=R(c);i(u),a(k)})}function s(){Q(e,y),window.removeEventListener("gamepadconnected",s)}window.addEventListener("gamepadconnected",s)}function X(e,n){n.forEach((t)=>{function r(a){t.from=1,t.fingers=[],m(t)}function o(a){t.from=1,t.fingers=[...T(a,t.canvas)],m(t)}function i(a){t.canvas.removeEventListener("mousemove",o),t.canvas.removeEventListener("mouseleave",r),e.removeEventListener("touchstart",i)}e.addEventListener("touchstart",i),t.canvas.addEventListener("mousemove",o),t.canvas.addEventListener("mouseleave",r)})}function Y(e,n){n.forEach((t)=>{function r(o){t.from=1,t.fingers=[...I(o,t.canvas)],m(t)}Object.values(C).forEach((o)=>{t.canvas.addEventListener(o,r)})})}function W(e,n){}document.addEventListener("DOMContentLoaded",()=>{let e=D();N(window,e),W(window,e),Y(window,e),X(window,e),S(window,e),L(window,e)});