diff --git a/draw.js b/draw.js new file mode 100644 index 0000000..2109a8d --- /dev/null +++ b/draw.js @@ -0,0 +1,155 @@ +class Drawing { + constructor() { + const div = document.createElement('div'); + const buttonsDiv = document.createElement('div'); + this.startButton = document.createElement('button'); + this.startButton.onclick = () => this.start(); + this.startButton.innerHTML = "Start"; + this.stopButton = document.createElement('button'); + this.stopButton.onclick = () => this.stop(); + this.stopButton.innerHTML = "Stop"; + this.canvas = document.createElement('canvas'); + this.canvas.width = 200; + this.canvas.height = 200; + buttonsDiv.appendChild(this.startButton); + buttonsDiv.appendChild(this.stopButton); + div.appendChild(buttonsDiv); + div.appendChild(this.canvas); + document.body.appendChild(div); + this.ctx = this.canvas.getContext('2d'); + this.offset = {x: 0, y: 0}; + this.sequence = []; + this.t = 0; + this.rt = 0; + this.dt = 0; + this.points = {}; + this.stopped = true; + } + + pixel([x, y]) { + return [ + this.offset.x + x, + this.canvas.height - this.offset.y - y + ]; + } + + setOffset([x, y]) { + this.sequence.push(() => { + this.offset = {x, y}; + }); + } + + setStroke(style, width) { + this.sequence.push(() => { + this.ctx.strokeStyle = style; + this.ctx.lineWidth = width; + }); + } + + setFill(style) { + this.sequence.push(() => { + this.ctx.fillStyle = style; + }); + } + + line(p1, p2) { + this.sequence.push(() => { + this.ctx.beginPath(); + this.ctx.moveTo(...this.pixel(this.getPoint(p1))); + this.ctx.lineTo(...this.pixel(this.getPoint(p2))); + this.ctx.stroke(); + }); + } + + polyline(...points) { + this.sequence.push(() => { + this.ctx.beginPath(); + this.ctx.moveTo(...this.pixel(points[0])); + for (let i = 1; i < points.length; i++) { + this.ctx.lineTo(...this.pixel(points[i])); + } + this.ctx.stroke(); + }); + } + + oscillatingValue(x1, x2, period) { + return x1 + (x2 - x1) * Math.sin(2*Math.PI*this.t/period); + } + + oscillatingPoint([x1, y1], [x2, y2], period) { + const x = this.oscillatingValue(x1, x2, period); + const y = this.oscillatingValue(y1, y2, period); + return [x, y]; + } + + square(p) { + this.sequence.push(() => { + const [x, y] = this.getPoint(p); + this.ctx.fillRect(...this.pixel([x - 5, y - 5]), 10, 10); + }) + } + + circle(p) { + this.sequence.push(() => { + this.ctx.beginPath(); + this.ctx.ellipse(...this.pixel(this.getPoint(p)), 5, 5, 0, 0, 2*Math.PI); + this.ctx.fill(); + }) + } + + definePoint(name, fn) { + this.points[name] = fn; + } + + getPoint(p) { + if (typeof p === 'string') { + const fn = this.points[p]; + if (!fn) { + const e = new Error; + e.message = `Point '${p}' is not defined`; + throw e; + } + return fn(); + } else { + return p; + } + } + + render() { + for (let action of this.sequence) { + action(); + } + } + + animate() { + this.ctx.reset(); + this.render(); + if (!this.stopped) { + requestAnimationFrame((prevt) => { + const rt = document.timeline.currentTime; + const elapsed = rt - prevt; + if (this.elideInterval) { + this.dt = 0; + this.elideInterval = false; + } else { + this.dt = rt - this.rt + elapsed; + } + this.t += this.dt; + this.rt = rt; + this.animate(); + }); + } + } + + stop() { + this.stopped = true; + } + + start() { + if (this.stopped) { + this.elideInterval = true; + this.stopped = false; + } + this.animate(); + } +} \ No newline at end of file diff --git a/main.css b/main.css new file mode 100644 index 0000000..85c401d --- /dev/null +++ b/main.css @@ -0,0 +1,6 @@ +body { + margin: 50px; +} +canvas { + border: 1px red dashed; +} \ No newline at end of file diff --git a/test.html b/test.html new file mode 100644 index 0000000..c5b21d0 --- /dev/null +++ b/test.html @@ -0,0 +1,32 @@ + + + +
+