play/pause button

This commit is contained in:
Lentil Hoffman 2025-12-25 23:41:29 -06:00
parent d96aefd3f2
commit 6fd3bcae15
Signed by: lentil
GPG Key ID: 0F5B99F3F4D0C087
7 changed files with 137 additions and 17 deletions

View File

@ -12,3 +12,5 @@ export const MOTION_TIME_SCALE = 0.001;
export const OFFSCREEN_OBJECT_LINE_SCALE = 5;
export const OFFSCREEN_OBJECT_LINE_WIDTH = 1.5;
export const OFFSCREEN_OBJECT_ARROWHEAD_LENGTH = 15;
export const ZOOM_IN_FACTOR = 2;
export const ZOOM_OUT_FACTOR = 0.5;

View File

@ -15,7 +15,7 @@ body {
}
div[id=simulator] {
position: absolute;
position: float;
top: 0;
left: 0;
width: 100%;

View File

@ -1,4 +1,8 @@
import { POINTER_HISTORY_SIZE } from './config.js';
import {
POINTER_HISTORY_SIZE,
ZOOM_IN_FACTOR,
ZOOM_OUT_FACTOR,
} from './config.js';
export class Pointer {
sim = undefined;
@ -11,30 +15,36 @@ export class Pointer {
// Monitor mouse movements
const el = window;
el.addEventListener('mousemove', e => {
// e.preventDefault();
this.handlePointerMove(this.sim.screenToSim(e.clientX, e.clientY));
});
// Monitor touch events
el.addEventListener('touchmove', e => {
// e.preventDefault();
const {pageX, pageY} = e.touches[0];
this.handlePointerMove(this.sim.screenToSim(pageX, pageY));
});
el.addEventListener('pointerdown', e => {
// e.preventDefault();
this.handlePointerDown(this.sim.screenToSim(e.clientX, e.clientY));
});
el.addEventListener('pointerup', e => {
// e.preventDefault();
this.handlePointerUp(this.sim.screenToSim(e.clientX, e.clientY));
});
el.addEventListener('click', e => {
// e.preventDefault();
});
// Monitor wheel events
el.addEventListener('wheel', e => {
// e.preventDefault();
// Wheel scroll down => positive deltaY => ZOOM IN
const factor = e.deltaY > 0 ? 2 : 0.5;
const factor = e.deltaY > 0 ? ZOOM_IN_FACTOR : ZOOM_OUT_FACTOR;
this.sim.scheduleZoom(this.sim.screenToSim(e.clientX, e.clientY), factor);
});

View File

@ -2,12 +2,15 @@ import { Display } from './display.js';
import { Overlay } from './overlay.js';
import { Pointer } from './pointer.js';
import { Objects } from './objects.js';
import { Toolbar } from './toolbar.js';
import { PlayPause } from './tools/play-pause.js';
export class Sim {
info = {};
frame = 0;
frameCount = 0;
time = undefined;
nextZoom = undefined;
playing = true;
display = undefined;
overlay = undefined;
@ -23,6 +26,10 @@ export class Sim {
this.overlay = new Overlay(this);
this.pointer = new Pointer(this);
this.objects = new Objects(this);
this.toolbar = new Toolbar(this);
// Set up toolbar
this.toolbar.addTool(new PlayPause(this.toolbar));
// Initiate main loop
this.time = document.timeline.currentTime;
@ -43,26 +50,38 @@ export class Sim {
this.display.viewOrigin.y = y - this.display.height / 2;
}
// Transform display coordinates to simulator coordinates using scale and viewOrigin
screenToSim(x, y) {
return this.display.screenToSim(x, y);
}
play() {
this.playing = true;
}
pause() {
this.playing = false;
}
// Main loop
loop(currentTime) {
const elapsedTime = currentTime - this.time;
this.time = currentTime;
if (this.nextZoom) {
this.zoom(this.nextZoom);
this.nextZoom = undefined;
if (this.playing) {
this.frameCount += 1;
const elapsedTime = currentTime - this.time;
this.time = currentTime;
if (this.nextZoom) {
this.zoom(this.nextZoom);
this.nextZoom = undefined;
}
this.display.fillCanvas();
this.objects.computeFrame(elapsedTime);
this.info['scale'] = this.display.scale;
this.display.drawObjects();
this.overlay.renderInfo();
}
this.display.fillCanvas();
this.objects.computeFrame(elapsedTime);
this.info['scale'] = this.display.scale;
this.display.drawObjects();
this.overlay.renderInfo();
requestAnimationFrame(t => this.loop(t));
}
}

20
tool.js Normal file
View File

@ -0,0 +1,20 @@
// Idea here is, tool can declare its parameters;
// can call back to toolbar for whatever...
// through toolbar can access sim
export class Tool {
toolbar = undefined;
sim = undefined;
constructor(toolbar) {
this.toolbar = toolbar;
this.sim = this.toolbar.sim;
const div = document.createElement('div');
this.div = div;
div.style.position = 'inline-block';
div.style.border = '1px #0fb solid';
div.style.margin = 10;
}
frame() {}
}

30
toolbar.js Normal file
View File

@ -0,0 +1,30 @@
export class Toolbar {
sim = undefined;
tools = [];
constructor(sim) {
this.sim = sim;
// Create ourselves a div, as child of sim's div
const div = document.createElement('div');
this.div = div;
this.sim.div.appendChild(div);
div.style.position = 'absolute';
div.style.top = 0;
div.style.left = 0;
div.style.zIndex = 2;
}
// tool: instance of Tool
addTool(tool) {
this.div.appendChild(tool.div);
this.tools.push(tool);
}
frame() {
for (let tool in this.tools) {
// TODO: tool.frame()
}
}
}

39
tools/play-pause.js Normal file
View File

@ -0,0 +1,39 @@
import { Tool } from '../tool.js';
export class PlayPause extends Tool {
constructor(toolbar) {
super(toolbar);
const playHTML = 'Play';
const pauseHTML = 'Pause';
// For now, use a regular button
const button = document.createElement('button');
this.div.appendChild(button);
if (this.playing) {
button.innerHTML = pauseHTML;
} else {
button.innerHTML = playHTML;
}
button.addEventListener('click', (e) => {
e.stopPropagation();
if (this.playing) {
button.innerHTML = playHTML;
this.playing = false;
} else {
button.innerHTML = pauseHTML;
this.playing = true;
}
});
console.log('PlayPause tool constructed ~!~');
}
get playing() {
return this.sim.playing;
}
set playing(playing) {
return this.sim.playing = playing;
}
}