// vim: set ts=2 sw=2 et tw=80: class App { static get BRUSHES() { return { "PenBrush": new PenBrush(), "DiscBrush": new DiscBrush(), "StarBrush": new StarBrush(), }; } constructor(conf) { if (!(typeof conf === 'object' && conf)) { throw new Error('Argument conf different from specification'); } this.canvas = document.getElementById(conf.canvas); if (!this.canvas || this.canvas.tagName !== 'CANVAS') { throw new Error(`canvas is not a canvas`); } this.ctx = this.canvas.getContext('2d'); this.favourites = document.querySelector('div#favourites'); if (typeof conf.buttons === 'object' && conf.buttons) { this.buttons = {} for (const b of ['clear', 'undo', 'camera']) this.buttons[b] = document.getElementById(conf.buttons[b]); if (this.buttons.clear) this.buttons.clear.addEventListener('click', () => { this.erase(); history.clear(); }); if (this.buttons.undo) this.buttons.undo.addEventListener('click', () => history.pop(this)); if (this.buttons.camera) this.buttons.camera.addEventListener('click', () => { const base64 = this.canvas.toDataURL(); const img = document.createElement('img'); img.src = base64; const span = document.createElement('span'); span.contentEditable = true; this.favourites.appendChild(img); this.favourites.appendChild(span); }); } this.ctx.lineWidth = 1; this.strokeStyle = this.constructor.defaultStrokeStyle; this.brush = "PenBrush"; const brushToolbar = document.querySelector('#brush-toolbar'); for (const name in App.BRUSHES) { const b = document.createElement('button'); b.innerText = name; b.addEventListener('click', () => this.brush = name); brushToolbar.appendChild(b); } this.canvas.addEventListener('mousedown', this.startPath.bind(this)); this.canvas.addEventListener('mousemove', this.draw.bind(this)); this.canvas.addEventListener('mouseup', this.endPath.bind(this)); this.canvas.addEventListener('mouseout', this.endPath.bind(this)); } static get defaultStrokeStyle() { return 'black'; } get strokeStyle() { return this.ctx.strokeStyle; } set strokeStyle(style) { if (typeof style !== 'string') { throw new Error('style is not a string'); } this.ctx.strokeStyle = style; } get brush() { return this._brush.name; } set brush(brushName) { this._brush = App.BRUSHES[brushName]; } erase() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); } startPath(e, record = true) { this.ctx.beginPath(); this.ctx.moveTo(e.offsetX, e.offsetY); if (record) { history.initializeNewPath(); history.push(new Stroke(this.brush, e.offsetX, e.offsetY)); } this.mousedown = true; } draw(e, beginNew = true, record = true) { if (this.mousedown) { this._brush.draw(this.ctx, this.strokeStyle, e.offsetX, e.offsetY); if (record) { history.push(new Stroke(this.brush, e.offsetX, e.offsetY)); } if (beginNew) { this.ctx.beginPath(); this.ctx.moveTo(e.offsetX, e.offsetY); } else { this.mousedown = false; } } } endPath(e, record = true) { this.draw(e, false, record); } drawPath(path) { const last = path.length - 1; const lastBrush = this.brush; for (let i = 0; i <= last; i++) { this.brush = path[i].brushName; switch(i) { case 0: this.startPath(path[i], false); break; case last: this.endPath(path[i], false); break; default: this.draw(path[i], true, false); } } this.brush = lastBrush; } }