いつもんとこまで.js
だいたいprocessingとかで遊ぶ時に幾何とGUIとをいっしょくたにしてアレコレする時のスケルトン。
簡単なメインループがある。
幾何はマウスによって操作される能力を持つかもしれない。
ツールは幾何を操作する能力を持つかもしれない。
各種マネージャーはシングルトン。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Loop Control Example</title>
<style>
canvas {
display: block;
margin: 0 auto;
background-color: #eee;
}
button {
display: block;
margin: 10px auto;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="300" height="600"></canvas>
<button id="pauseButton">Pause</button>
<button id="resumeButton" disabled>Resume</button>
<script>
class InputManager{
static instance = null;
constructor(){
if (InputManager.instance) {
return InputManager.instance;
}
this.mouseLeftPressed = false;
this.mouseMiddlePressed = false;
this.mouseRightPressed = false;
this.onMouseMove = false;
this.dragStartRange = 5;
this.onDrag = false;
this.x=null;
this.y=null;
this.prevX=null;
this.prevY=null;
this.startX=null;
this.startY=null;
this.endX=null;
this.endY=null;
InputManager.instance = this;
}
// インスタンスの取得を管理する静的メソッド
static getInstance() {
if (!InputManager.instance) {
InputManager.instance = new InputManager();
}
return InputManager.instance;
}
mouseDown(event){
//console.log("mousedown");
event.preventDefault();
this.x = event.offsetX;
this.y = event.offsetY;
this.startX=event.offsetX;
this.startY=event.offsetY;
this.endX=null;
this.endY=null;
switch (event.button) {
case 0:
//console.log('Left button pressed');
this.mouseLeftPressed=true;
break;
case 1:
//console.log('Middle button pressed');
this.mouseMiddlePressed=true;
break;
case 2:
//console.log('Right button pressed');
this.mouseRightPressed=true;
break;
case 3:
//console.log('Browser back button pressed');
break;
case 4:
//console.log('Browser forward button pressed');
break;
default:
//console.log('Unknown button pressed');
}
}
mouseMove(event){
event.preventDefault();
this.onMouseMove=true;
let dx = this.x-this.startX;
let dy = this.y-this.startY;
if(this.mouseLeftPressed && (dx*dx)+(dy*dy)>this.dragStartRange*this.dragStartRange){
//console.log("ok");
this.onDrag=true;
}
this.prevX=this.x;
this.prevY=this.y;
this.x = event.offsetX;
this.y = event.offsetY;
}
mouseUp(event){
event.preventDefault();
this.onMouseMove=false;
this.onDrag=false;
this.prevX=this.x;
this.prevY=this.y;
this.x=null;
this.y=null;
this.startX=null;
this.startY=null;
this.endX=event.offsetX;
this.endY=event.offsetY;
switch (event.button) {
case 0:
//console.log('Left button up');
this.mouseLeftPressed=false;
break;
case 1:
//console.log('Middle button up');
this.mouseMiddlePressed=false;
break;
case 2:
//console.log('Right button up');
this.mouseRightPressed=false;
break;
}
}
mouseClick(event){
event.preventDefault();
}
keyDown(event){
//event.preventDefault();
//console.log("keydown");
}
keyUp(event){
//event.preventDefault();
}
wheel(event){
// deltaYを使用してスクロール方向を判断
if (event.deltaY < 0) {
//console.log('スクロール: 上');
} else if (event.deltaY > 0) {
//console.log('スクロール: 下');
}
}
}
class ToolManager{
static instance = null;
constructor(){
if (ToolManager.instance) {
return ToolManager.instance;
}
this.ToolMode = {
SINGLE: 'SINGLE',
SERIAL: 'SERIAL',//順次実行、ないし先頭のTOOLを実行
PARALLEL: 'PARALLEL'//同時実行
};
this.mode = this.ToolMode.SINGLE;
this.CurrentTool = new DefaultTool(this);
this.CurrentTools = [];
ToolManager.instance = this;
}
// インスタンスの取得を管理する静的メソッド
static getInstance() {
if (!ToolManager.instance) {
ToolManager.instance = new ToolManager();
}
return ToolManager.instance;
}
update(){
// if(this.mode==="SINGLE"){
// if(this.CurrentTool){
// if(this.CurrentTool.update){
// this.CurrentTool.update();
// }
// }
// }
if(this.mode === "SINGLE" && this.CurrentTool?.update){
this.CurrentTool.update();
}
else if (this.mode === "PARALLEL" && Array.isArray(this.CurrentTools)) {
for (const tool of this.CurrentTools) {
if (tool.update) {
tool.update();
}
}
}
//関数抽出記法
// function updateTool(tool) {
// if (tool && tool.update) {
// tool.update();
// }
// }
// // PARALLELモードでのツールの更新
// if (this.mode === "PARALLEL" && Array.isArray(this.CurrentTools)) {
// this.CurrentTools.forEach(updateTool);
// }
}
//そのToolなりの表示能力がある場合
draw(ctx){
if(this.mode === "SINGLE" && this.CurrentTool?.draw){
this.CurrentTool.draw(ctx);
}
else if (this.mode === "PARALLEL" && Array.isArray(this.CurrentTools)) {
for (const tool of this.CurrentTools) {
if (tool.draw) {
tool.draw(ctx);
}
}
}
}
}
class ObjectManager{
static instance = null;
constructor(){
if (ObjectManager.instance) {
return ObjectManager.instance;
}
this.Objects = [];
ObjectManager.instance = this;
}
// インスタンスの取得を管理する静的メソッド
static getInstance() {
if (!ObjectManager.instance) {
ObjectManager.instance = new ObjectManager();
}
return ObjectManager.instance;
}
//自動で動く場合
update(){
for(const obj of this.Objects){
if(obj?.update){
obj.update();
}
}
}
//基本的に表示能力は持つ
draw(ctx){
for(const obj of this.Objects){
if(obj?.draw){
obj.draw(ctx);
}
}
}
}
class DefaultTool{
constructor(toolManager){
this.ToolManager=toolManager;
this.Target = null;
}
update(){
//console.log(inputManager.x);
if(inputManager.x===null||inputManager.y===null){return;}
//console.log("ok");
if(inputManager.onDrag&&this.Target!==null){
this.Target.x=inputManager.x;
this.Target.y=inputManager.y;
}else if(inputManager.mouseLeftPressed){
for (const obj of objectManager?.Objects || []) {
if (obj instanceof Dot) {
let dx = inputManager.x - obj.x;
let dy = inputManager.y - obj.y;
// console.log("x:"+inputManager.x+"-"+"obj.x"+obj.x+"="+"dx:"+dx);
// console.log("dy"+dy);
// console.log("r"+obj.r);
//console.log("ok");
if ((dx*dx) + (dy*dy) < obj.r * obj.r) {
//console.log("ok");
this.Target = obj;
}
}
}
}else if(!inputManager.mouseLeftPressed){
this.Target=null;
}
}
draw(){
}
}
class Dot {
constructor(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
}
update() {
}
draw(ctx) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
ctx.stroke();
}
}
let inputManager = null;
let toolManager = null;
let objectManager = null;
class App {
constructor() {
this.canvas = document.getElementById("myCanvas");
this.ctx = this.canvas.getContext("2d");
this.lastFrameTime = 0;
this.targetFrameDuration = 1000 / 60; // 60FPS
this.animationFrameId = null;
this.isPaused = false;
this.inputManager = new InputManager();
inputManager=this.inputManager;
this.canvas.addEventListener('mousedown', this.inputManager.mouseDown.bind(this.inputManager));
this.canvas.addEventListener('mousemove', this.inputManager.mouseMove.bind(this.inputManager));
this.canvas.addEventListener('mouseup', this.inputManager.mouseUp.bind(this.inputManager));
window.addEventListener('wheel', this.inputManager.wheel.bind(this.inputManager));
//Canvas内で右クリックメニューが出ないようにする
this.canvas.addEventListener('contextmenu', function (event) {
event.preventDefault();
});
document.addEventListener('keydown', this.inputManager.keyDown.bind(this.inputManager));
document.addEventListener('keyup', this.inputManager.keyUp.bind(this.inputManager));
this.toolManager = new ToolManager();
toolManager=this.toolManager;
this.objectManager=new ObjectManager();
objectManager=this.objectManager;
let dot = new Dot(100, 100, 100);
this.objectManager.Objects.push(dot);
};
start() {
this.gameLoop(performance.now());
}
gameLoop(currentTime) {
if (this.isPaused) return;
const deltaTime = currentTime - this.lastFrameTime;
if (deltaTime >= this.targetFrameDuration) {
//inputはInputManagerがやる
this.update(deltaTime);
// Check if update took too long
if (performance.now() - currentTime < this.targetFrameDuration) {
this.render();
}
this.lastFrameTime = currentTime;
}
//this.animationFrameId = requestAnimationFrame(this.gameLoop);
this.animationFrameId = requestAnimationFrame(() => this.gameLoop(performance.now()));
}
update(deltaTime) {
// Update logic here
//console.log(this.inputManager.x);
this.toolManager.update();
this.objectManager.update();
}
render() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Rendering logic here
//this.dot.draw(this.ctx);
this.toolManager.draw(this.ctx);
this.objectManager.draw(this.ctx);
}
}//App
function init() {
document.getElementById("pauseButton").onclick = function () {
app.isPaused = true;
if (app.animationFrameId) {
cancelAnimationFrame(app.animationFrameId);
app.animationFrameId = null;
}
this.disabled = true;
document.getElementById("resumeButton").disabled = false;
};
document.getElementById("resumeButton").onclick = function () {
if (!app.isPaused) return;
app.isPaused = false;
app.lastFrameTime = performance.now();
app.gameLoop(app.lastFrameTime);
this.disabled = true;
document.getElementById("pauseButton").disabled = false;
};
const app = new App();
app.start();
}//init
document.addEventListener('DOMContentLoaded', function () {
init();
})
</script>
</body>
</html>
この記事が気に入ったらサポートをしてみませんか?