refactored code and added win/lose popups
This commit is contained in:
parent
40ec4fa09a
commit
4746f34537
6 changed files with 353 additions and 146 deletions
|
@ -1,13 +1,16 @@
|
||||||
{
|
{
|
||||||
"touchscreen": {
|
"touchscreen": {
|
||||||
|
"enabled": true,
|
||||||
"mode": "swipe",
|
"mode": "swipe",
|
||||||
"deadzone": 50,
|
"deadzone": 50,
|
||||||
"buffer": false
|
"buffer": false
|
||||||
},
|
},
|
||||||
"keyboard": {
|
"keyboard": {
|
||||||
|
"enabled": true,
|
||||||
"buffer": false
|
"buffer": false
|
||||||
},
|
},
|
||||||
"gamepad": {
|
"gamepad": {
|
||||||
|
"enabled": true,
|
||||||
"deadzone": 0.5,
|
"deadzone": 0.5,
|
||||||
"buffer": true
|
"buffer": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
"levelDisplay": "Level <n>",
|
"levelDisplay": "Level <n>",
|
||||||
"levels": [
|
"levels": [
|
||||||
1, 2, 3, 4, 5
|
1, 2, 3, 4, 5
|
||||||
]
|
],
|
||||||
|
"nextLevel": true
|
||||||
},
|
},
|
||||||
"arcade": {
|
"arcade": {
|
||||||
"desc": "Have fun just like in the good ol' days, walls wrap around, fruits respawn and speed increases",
|
"desc": "Have fun just like in the good ol' days, walls wrap around, fruits respawn and speed increases",
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
"Survival",
|
"Survival",
|
||||||
"Versus"
|
"Versus"
|
||||||
],
|
],
|
||||||
|
"nextLevel": false,
|
||||||
"levelDesc": [
|
"levelDesc": [
|
||||||
"The old classic, try to get as high as a score as you can",
|
"The old classic, try to get as high as a score as you can",
|
||||||
"Get a score as high as you can in 30 seconds",
|
"Get a score as high as you can in 30 seconds",
|
||||||
|
|
152
src/js/input.js
Normal file
152
src/js/input.js
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
let currentInputs={};
|
||||||
|
let handlers=[];
|
||||||
|
let config;
|
||||||
|
|
||||||
|
const toAngleMagnitude=(x, y) => {
|
||||||
|
return {
|
||||||
|
angle: ((Math.atan2(x, y)+2*Math.PI)%(2*Math.PI))/Math.PI,
|
||||||
|
magnitude: Math.hypot(x, y)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAngleMagnitude=(x, y, threshold=0, fn=null, clearBuffer=false) => {
|
||||||
|
const {angle, magnitude}=toAngleMagnitude(x, y);
|
||||||
|
|
||||||
|
if(magnitude>threshold) {
|
||||||
|
if(angle>.25 && angle <.75) inputs.right=true;
|
||||||
|
else if(angle>.75 && angle<1.25) inputs.up=true;
|
||||||
|
else if(angle>1.25 && angle<1.75) inputs.left=true;
|
||||||
|
else inputs.down=true;
|
||||||
|
|
||||||
|
if(clearBuffer) inputs.clearBuffer=true;
|
||||||
|
if(fn) fn(angle, magnitude);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCrosspad=(() => {
|
||||||
|
const fn=e =>
|
||||||
|
handleAngleMagnitude(
|
||||||
|
e.touches[0].clientX-window.innerWidth/2,
|
||||||
|
e.touches[0].clientY-window.innerHeight/2,
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
!config.touchscreen.buffer
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
touchstart: fn,
|
||||||
|
touchmove: fn
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
const handleKeyboard={
|
||||||
|
keydown: e => {
|
||||||
|
let inputs=currentInputs;
|
||||||
|
if(e.key=='ArrowUp') inputs.up=true;
|
||||||
|
else if(e.key=='ArrowDown') inputs.down=true;
|
||||||
|
else if(e.key=='ArrowLeft') inputs.left=true;
|
||||||
|
else if(e.key=='ArrowRight') inputs.right=true;
|
||||||
|
|
||||||
|
if(!config.keyboard.buffer) inputs.clearBuffer=true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleJoystick=(() => {
|
||||||
|
let center={
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
touchstart: e => {
|
||||||
|
center.x=e.touches[0].clientX;
|
||||||
|
center.y=e.touches[0].clientY;
|
||||||
|
},
|
||||||
|
touchmove: e =>
|
||||||
|
handleAngleMagnitude(
|
||||||
|
e.touches[0].clientX-center.x,
|
||||||
|
e.touches[0].clientY-center.y,
|
||||||
|
config.touchscreen.deadzone,
|
||||||
|
null,
|
||||||
|
!config.touchscreen.buffer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSwipe=(() => {
|
||||||
|
let center={
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
};
|
||||||
|
let resetCenter=e => {
|
||||||
|
center.x=e.touches[0].clientX;
|
||||||
|
center.y=e.touches[0].clientY;
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
touchstart: resetCenter,
|
||||||
|
touchmove: e =>
|
||||||
|
handleAngleMagnitude(
|
||||||
|
e.touches[0].clientX-center.x,
|
||||||
|
e.touches[0].clientY-center.y,
|
||||||
|
config.touchscreen.deadzone,
|
||||||
|
() => resetCenter(e),
|
||||||
|
!config.touchscreen.buffer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleGamepads={
|
||||||
|
frame: () => {
|
||||||
|
const gp=navigator.getGamepads()[0];
|
||||||
|
let inputs=currentInputs;
|
||||||
|
if(!gp || !gp.axes) return;
|
||||||
|
|
||||||
|
handleAngleMagnitude(
|
||||||
|
gp.axes[0],
|
||||||
|
gp.axes[1],
|
||||||
|
config.gamepad.deadzone,
|
||||||
|
null,
|
||||||
|
!config.gamepad.buffer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEvent=(type, evt) => {
|
||||||
|
for(let handler of handlers) {
|
||||||
|
let fn=handler[type];
|
||||||
|
if(fn) fn(evt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const enableHandler=handler => {
|
||||||
|
if(!handlers.includes(handler)) handlers.push(handler);
|
||||||
|
};
|
||||||
|
const disableHandler=handler => {
|
||||||
|
let idx=handlers.indexOf(handler);
|
||||||
|
if(idx!=-1) handlers.splice(idx, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateConfig=cfg =>
|
||||||
|
config=cfg;
|
||||||
|
|
||||||
|
const clear=() =>
|
||||||
|
Object
|
||||||
|
.keys(currentInputs)
|
||||||
|
.forEach(key => delete currentInputs[key]);
|
||||||
|
|
||||||
|
for(let type of ['keydown', 'touchstart', 'touchmove']) {
|
||||||
|
window.addEventListener(type, handleEvent.bind(null, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
return module.exports={
|
||||||
|
inputs: currentInputs,
|
||||||
|
clear,
|
||||||
|
enableHandler, disableHandler,
|
||||||
|
framefn: handleEvent.bind(null, 'frame'),
|
||||||
|
availableHandlers: {
|
||||||
|
keyboard: handleKeyboard,
|
||||||
|
gamepad: handleGamepads,
|
||||||
|
touchscreenCrosspad: handleCrosspad,
|
||||||
|
touchscreenJoystick: handleJoystick,
|
||||||
|
touchscreenSwipe: handleSwipe
|
||||||
|
},
|
||||||
|
updateConfig
|
||||||
|
};
|
18
src/js/levels.js
Normal file
18
src/js/levels.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
const cache=Object.create(null);
|
||||||
|
|
||||||
|
const get=async filename => {
|
||||||
|
if(cache[filename]) return cache[filename];
|
||||||
|
const req=await fetch('levels/'+filename);
|
||||||
|
const json=await req.json();
|
||||||
|
return cache[filename]=json;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCache=() =>
|
||||||
|
Object
|
||||||
|
.keys(cache)
|
||||||
|
.forEach(key => delete cache[key]);
|
||||||
|
|
||||||
|
return module.exports={
|
||||||
|
get,
|
||||||
|
clearCache
|
||||||
|
};
|
294
src/js/main.js
294
src/js/main.js
|
@ -1,22 +1,35 @@
|
||||||
(async () => {
|
(async () => {
|
||||||
location.hash='';
|
|
||||||
|
|
||||||
|
// load modules
|
||||||
const assets=require('assets');
|
const assets=require('assets');
|
||||||
const Popup=require('popup');
|
const Popup=require('popup');
|
||||||
const SnekGame=require('snek');
|
const SnekGame=require('snek');
|
||||||
|
const input=require('input');
|
||||||
|
const levels=require('levels');
|
||||||
|
|
||||||
|
// get a known state
|
||||||
await new Promise(ok => assets.onReady(ok));
|
await new Promise(ok => assets.onReady(ok));
|
||||||
|
location.hash='menu';
|
||||||
|
|
||||||
|
// get our DOM in check
|
||||||
const main=document.querySelector('main');
|
const main=document.querySelector('main');
|
||||||
const nav=main.querySelector('nav');
|
const nav=main.querySelector('nav');
|
||||||
const canvas=main.querySelector('canvas');
|
const canvas=main.querySelector('canvas');
|
||||||
|
|
||||||
const config=assets.get('config');
|
// load config
|
||||||
|
const config=assets.get('config'); //TODO use an actual config module
|
||||||
|
|
||||||
|
// load data from server
|
||||||
|
const levelList=assets.get('levelList');
|
||||||
|
|
||||||
|
// get our global variables
|
||||||
let currentGame=null;
|
let currentGame=null;
|
||||||
let currentInputs={};
|
|
||||||
|
|
||||||
const resizeCanvas=() => {
|
// forward-declare functions
|
||||||
|
let resizeCanvas, getLevel, startGame, handleWin, handleDeath, menu, help;
|
||||||
|
|
||||||
|
// handle window resize and fullscreen
|
||||||
|
resizeCanvas=() => {
|
||||||
if(document.fullscreenElement) {
|
if(document.fullscreenElement) {
|
||||||
canvas.width=screen.width;
|
canvas.width=screen.width;
|
||||||
canvas.height=screen.height;
|
canvas.height=screen.height;
|
||||||
|
@ -27,8 +40,35 @@
|
||||||
};
|
};
|
||||||
resizeCanvas();
|
resizeCanvas();
|
||||||
window.addEventListener('resize', resizeCanvas);
|
window.addEventListener('resize', resizeCanvas);
|
||||||
|
window.addEventListener('keydown', async e => {
|
||||||
|
if(e.key=='f') {
|
||||||
|
if(document.fullscreenElement) await document.exitFullscreen();
|
||||||
|
else await main.requestFullscreen();
|
||||||
|
resizeCanvas();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const levelList=assets.get('levelList');
|
// get a level for a category and an id
|
||||||
|
getLevel=(category, id) => {
|
||||||
|
const cat=levelList[category];
|
||||||
|
id=''+id;
|
||||||
|
|
||||||
|
const displayName=cat.levelDisplay
|
||||||
|
.replace(/<n>/g, id)
|
||||||
|
.replace(/<l>/g, id.toLowerCase());
|
||||||
|
const fileName=cat.levelFilename
|
||||||
|
.replace(/<n>/g, id)
|
||||||
|
.replace(/<l>/g, id.toLowerCase());
|
||||||
|
const levelString=category+'/'+id+'/'+fileName;
|
||||||
|
|
||||||
|
return {
|
||||||
|
displayName,
|
||||||
|
fileName,
|
||||||
|
levelString
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// buid menu from level list
|
||||||
Object.keys(levelList).forEach(category => {
|
Object.keys(levelList).forEach(category => {
|
||||||
const cat=levelList[category];
|
const cat=levelList[category];
|
||||||
|
|
||||||
|
@ -41,16 +81,10 @@
|
||||||
|
|
||||||
const ul=section.appendChild(document.createElement('ul'));
|
const ul=section.appendChild(document.createElement('ul'));
|
||||||
cat.levels.forEach((level, i) => {
|
cat.levels.forEach((level, i) => {
|
||||||
level=''+level;
|
const {displayName, fileName, levelString}=getLevel(category, level);
|
||||||
const displayName=cat.levelDisplay
|
|
||||||
.replace(/<n>/g, level)
|
|
||||||
.replace(/<l>/g, level.toLowerCase());
|
|
||||||
const fileName=cat.levelFilename
|
|
||||||
.replace(/<n>/g, level)
|
|
||||||
.replace(/<l>/g, level.toLowerCase());
|
|
||||||
const li=ul.appendChild(document.createElement('li'));
|
const li=ul.appendChild(document.createElement('li'));
|
||||||
const a=li.appendChild(document.createElement('a'));
|
const a=li.appendChild(document.createElement('a'));
|
||||||
a.href='#'+category+'/'+fileName;
|
a.href='#'+levelString;
|
||||||
a.innerText=displayName;
|
a.innerText=displayName;
|
||||||
if(cat.levelDesc) {
|
if(cat.levelDesc) {
|
||||||
const span=li.appendChild(document.createElement('span'));
|
const span=li.appendChild(document.createElement('span'));
|
||||||
|
@ -59,45 +93,62 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleGamepads=() => {
|
// start a new game
|
||||||
const gp=navigator.getGamepads()[0];
|
startGame=async (category, levelId, filename) => {
|
||||||
let inputs=currentInputs;
|
// stop any running games
|
||||||
if(!gp || !gp.axes) return;
|
if(currentGame) currentGame.playing=false;
|
||||||
|
|
||||||
const magnitude=Math.hypot(gp.axes[0], gp.axes[1]);
|
// load rules and level from cache or server
|
||||||
const angle=((Math.atan2(gp.axes[0], gp.axes[1])+2*Math.PI)%(2*Math.PI))/Math.PI;
|
|
||||||
if(magnitude>config.gamepad.deadzone) {
|
|
||||||
if(angle>.25 && angle <.75) inputs.right=true;
|
|
||||||
else if(angle>.75 && angle<1.25) inputs.up=true;
|
|
||||||
else if(angle>1.25 && angle<1.75) inputs.left=true;
|
|
||||||
else inputs.down=true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!config.gamepad.buffer) inputs.clearBuffer=true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const startGame=async (category, filename) => {
|
|
||||||
//TODO migrate relevant code here
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('hashchange', async () => {
|
|
||||||
nav.classList.add('hidden');
|
|
||||||
|
|
||||||
const [_, category, filename]=location.hash.match(/([a-zA-Z0-9_-]+?)\/(.+)/);
|
|
||||||
const rules=levelList[category].rules || {};
|
const rules=levelList[category].rules || {};
|
||||||
const level=await (async () => {
|
const level=await levels.get(filename);
|
||||||
const resp=await fetch('levels/'+filename);
|
|
||||||
return await resp.json();
|
|
||||||
})();
|
|
||||||
|
|
||||||
const snek=new SnekGame(level, canvas, rules);
|
// create the game and attach the callbacks
|
||||||
canvas.classList.remove('hidden');
|
const snek=currentGame=new SnekGame(level, canvas, rules);
|
||||||
snek.start();
|
|
||||||
snek.callback=evt => {
|
snek.callback=evt => {
|
||||||
if(evt=='tick') {
|
if(evt=='tick') {
|
||||||
if(navigator.getGamepads) handleGamepads();
|
input.framefn();
|
||||||
snek.handleInputs(currentInputs);
|
snek.handleInputs(input.inputs);
|
||||||
} else if(evt=='win') {
|
} else if(evt=='win') {
|
||||||
|
handleWin(snek);
|
||||||
|
} else if(evt=='die') {
|
||||||
|
handleDeath(snek);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// setup the DOM
|
||||||
|
nav.classList.add('hidden');
|
||||||
|
canvas.classList.remove('hidden');
|
||||||
|
|
||||||
|
// push some userdata to the snake
|
||||||
|
snek.userdata={
|
||||||
|
category,
|
||||||
|
levelId,
|
||||||
|
filename
|
||||||
|
};
|
||||||
|
|
||||||
|
// reset the inputs
|
||||||
|
input.clear();
|
||||||
|
|
||||||
|
// start the actual game
|
||||||
|
snek.start();
|
||||||
|
};
|
||||||
|
|
||||||
|
// return to the menu
|
||||||
|
menu=() => {
|
||||||
|
// stop any running games
|
||||||
|
if(currentGame) currentGame.playing=false;
|
||||||
|
|
||||||
|
// setup the DOM
|
||||||
|
nav.classList.remove('hidden');
|
||||||
|
canvas.classList.add('hidden');
|
||||||
|
};
|
||||||
|
|
||||||
|
// display the win popup
|
||||||
|
handleWin=async snek => {
|
||||||
|
// get userdata back
|
||||||
|
const {category, levelId, filename}=snek.userdata;
|
||||||
|
|
||||||
|
// create and configure popup
|
||||||
let popup=new Popup("Finished!");
|
let popup=new Popup("Finished!");
|
||||||
popup.addStrong("You won!");
|
popup.addStrong("You won!");
|
||||||
popup.addContent({
|
popup.addContent({
|
||||||
|
@ -107,102 +158,83 @@
|
||||||
});
|
});
|
||||||
popup.buttons={
|
popup.buttons={
|
||||||
retry: "Retry",
|
retry: "Retry",
|
||||||
next: "Next level",
|
|
||||||
menu: "Main menu"
|
menu: "Main menu"
|
||||||
};
|
};
|
||||||
popup.display();
|
if(levelList[category].nextLevel) {
|
||||||
//TODO do something with the result
|
let nextId=(+levelId)+1;
|
||||||
|
if(levelList[category].levels.includes(nextId)) popup.buttons.next="Next level";
|
||||||
|
}
|
||||||
|
|
||||||
|
// show the actual popup
|
||||||
|
let result=await popup.display(main);
|
||||||
|
|
||||||
|
// act on it
|
||||||
|
if(result=='retry') {
|
||||||
|
startGame(category, levelId, filename);
|
||||||
|
} else if(result=='menu') {
|
||||||
|
location.hash='menu';
|
||||||
|
} else if(result=='next') {
|
||||||
|
let nextId=(+levelId)+1;
|
||||||
|
let {levelString}=getLevel(category, nextId)
|
||||||
|
location.hash=levelString;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
currentGame=snek;
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('keydown', async e => {
|
// display the death popup
|
||||||
e.preventDefault();
|
handleDeath=async snek => {
|
||||||
if(e.key=='f') {
|
// get userdata back
|
||||||
if(document.fullscreenElement) await document.exitFullscreen();
|
const {category, levelId, filename}=snek.userdata;
|
||||||
else await main.requestFullscreen();
|
|
||||||
resizeCanvas();
|
// create and configure popup
|
||||||
|
let popup=new Popup("Finished!");
|
||||||
|
popup.addStrong("You died...");
|
||||||
|
popup.addContent({
|
||||||
|
"Time": snek.playTime/1000+'s',
|
||||||
|
"Score": snek.score,
|
||||||
|
"Final length": snek.snake.length
|
||||||
|
});
|
||||||
|
popup.buttons={
|
||||||
|
retry: "Retry",
|
||||||
|
menu: "Main menu"
|
||||||
|
};
|
||||||
|
|
||||||
|
// show the actual popup
|
||||||
|
let result=await popup.display(main);
|
||||||
|
|
||||||
|
// act on it
|
||||||
|
if(result=='retry') {
|
||||||
|
startGame(category, levelId, filename);
|
||||||
|
} else if(result=='menu') {
|
||||||
|
location.hash='menu';
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let inputs=currentInputs;
|
// handle page navigation
|
||||||
if(e.key=='ArrowUp') inputs.up=true;
|
window.addEventListener('hashchange', () => {
|
||||||
else if(e.key=='ArrowDown') inputs.down=true;
|
const hash=location.hash.substr(1);
|
||||||
else if(e.key=='ArrowLeft') inputs.left=true;
|
|
||||||
else if(e.key=='ArrowRight') inputs.right=true;
|
|
||||||
|
|
||||||
if(!config.keyboard.buffer) inputs.clearBuffer=true;
|
if(hash=='' || hash=='menu') return menu();
|
||||||
|
else if(hash=='help') return help();
|
||||||
|
|
||||||
|
const [_, category, levelId, filename]=location.hash.match(/([a-zA-Z0-9_-]+?)\/([a-zA-Z0-9_-]+?)\/(.+)/);
|
||||||
|
startGame(category, levelId, filename);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// enable input methods according to config
|
||||||
|
if(config.keyboard.enabled) {
|
||||||
|
input.enableHandler(input.availableHandlers.keyboard);
|
||||||
|
}
|
||||||
|
if(config.gamepad.enabled) {
|
||||||
|
input.enableHandler(input.availableHandlers.gamepad);
|
||||||
|
}
|
||||||
|
if(config.touchscreen.enabled) {
|
||||||
if(config.touchscreen.mode=='crosspad') {
|
if(config.touchscreen.mode=='crosspad') {
|
||||||
const handleTouch=e => {
|
input.enableHandler(input.availableHandlers.touchscreenCrosspad);
|
||||||
let x=e.touches[0].clientX-window.innerWidth/2;
|
} else if(config.touchscreen.mode=='joystick') {
|
||||||
let y=e.touches[0].clientY-window.innerHeight/2;
|
input.enableHandler(input.availableHandlers.touchscreenJoystick);
|
||||||
const angle=((Math.atan2(x, y)+2*Math.PI)%(2*Math.PI))/Math.PI;
|
} else if(config.touchscreen.mode=='swipe') {
|
||||||
|
input.enableHandler(input.availableHandlers.touchscreenSwipe);
|
||||||
let inputs=currentInputs;
|
|
||||||
if(angle>.25 && angle <.75) inputs.right=true;
|
|
||||||
else if(angle>.75 && angle<1.25) inputs.up=true;
|
|
||||||
else if(angle>1.25 && angle<1.75) inputs.left=true;
|
|
||||||
else inputs.down=true;
|
|
||||||
|
|
||||||
if(!config.touchscreen.buffer) inputs.clearBuffer=true;
|
|
||||||
};
|
|
||||||
window.addEventListener('touchstart', handleTouch);
|
|
||||||
window.addEventListener('touchmove', handleTouch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(config.touchscreen.mode=='joystick') {
|
|
||||||
let center={x: 0, y: 0};
|
|
||||||
window.center=center;
|
|
||||||
window.addEventListener('touchstart', e => {
|
|
||||||
center.x=e.touches[0].clientX;
|
|
||||||
center.y=e.touches[0].clientY;
|
|
||||||
});
|
|
||||||
window.addEventListener('touchmove', e => {
|
|
||||||
let x=e.touches[0].clientX-center.x;
|
|
||||||
let y=e.touches[0].clientY-center.y;
|
|
||||||
const angle=((Math.atan2(x, y)+2*Math.PI)%(2*Math.PI))/Math.PI;
|
|
||||||
const magnitude=Math.hypot(x, y);
|
|
||||||
|
|
||||||
let inputs=currentInputs;
|
|
||||||
if(magnitude>config.touchscreen.deadzone) {
|
|
||||||
if(angle>.25 && angle <.75) inputs.right=true;
|
|
||||||
else if(angle>.75 && angle<1.25) inputs.up=true;
|
|
||||||
else if(angle>1.25 && angle<1.75) inputs.left=true;
|
|
||||||
else inputs.down=true;
|
|
||||||
}
|
}
|
||||||
|
input.updateConfig(config);
|
||||||
if(!config.touchscreen.buffer) inputs.clearBuffer=true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config.touchscreen.mode=='swipe') {
|
|
||||||
let center={x: 0, y: 0};
|
|
||||||
window.center=center;
|
|
||||||
window.addEventListener('touchstart', e => {
|
|
||||||
center.x=e.touches[0].clientX;
|
|
||||||
center.y=e.touches[0].clientY;
|
|
||||||
});
|
|
||||||
window.addEventListener('touchmove', e => {
|
|
||||||
let x=e.touches[0].clientX-center.x;
|
|
||||||
let y=e.touches[0].clientY-center.y;
|
|
||||||
const angle=((Math.atan2(x, y)+2*Math.PI)%(2*Math.PI))/Math.PI;
|
|
||||||
const magnitude=Math.hypot(x, y);
|
|
||||||
|
|
||||||
let inputs=currentInputs;
|
|
||||||
if(magnitude>config.touchscreen.deadzone) {
|
|
||||||
if(angle>.25 && angle <.75) inputs.right=true;
|
|
||||||
else if(angle>.75 && angle<1.25) inputs.up=true;
|
|
||||||
else if(angle>1.25 && angle<1.75) inputs.left=true;
|
|
||||||
else inputs.down=true;
|
|
||||||
|
|
||||||
center.x=e.touches[0].clientX;
|
|
||||||
center.y=e.touches[0].clientY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!config.touchscreen.buffer) inputs.clearBuffer=true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -46,7 +46,7 @@ class Popup {
|
||||||
this.content.push(objToDom({[Popup.STRONG]: cnt}));
|
this.content.push(objToDom({[Popup.STRONG]: cnt}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async display() {
|
async display(parent=document.body) {
|
||||||
let outer=document.createElement('div');
|
let outer=document.createElement('div');
|
||||||
outer.classList.add('popup');
|
outer.classList.add('popup');
|
||||||
let popup=outer.appendChild(document.createElement('div'));
|
let popup=outer.appendChild(document.createElement('div'));
|
||||||
|
@ -67,7 +67,7 @@ class Popup {
|
||||||
});
|
});
|
||||||
buttons.forEach(btn => buttonSection.appendChild(btn));
|
buttons.forEach(btn => buttonSection.appendChild(btn));
|
||||||
|
|
||||||
document.body.appendChild(outer);
|
parent.appendChild(outer);
|
||||||
|
|
||||||
const code=await Promise.race(buttons.map(btn => new Promise(ok => {
|
const code=await Promise.race(buttons.map(btn => new Promise(ok => {
|
||||||
btn.addEventListener('click', e => {
|
btn.addEventListener('click', e => {
|
||||||
|
@ -76,7 +76,7 @@ class Popup {
|
||||||
});
|
});
|
||||||
})));
|
})));
|
||||||
|
|
||||||
document.body.removeChild(outer);
|
parent.removeChild(outer);
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue