You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
4.9 KiB
183 lines
4.9 KiB
// canvas-app.js
|
|
// Copyright (C) 2022 Rob Colbert @[email protected]
|
|
// License: Apache-2.0
|
|
|
|
'use strict';
|
|
|
|
window.dtp = window.dtp || { };
|
|
|
|
const IMAGE_W = 32;
|
|
const IMAGE_H = 32;
|
|
|
|
const DTP_COMPONENT_NAME = 'canvas-app';
|
|
import '../less/style.less';
|
|
|
|
import { NiceAudio, NiceLog } from 'dtp-nice-game';
|
|
import CanvasSocket from './canvas-socket.js';
|
|
|
|
import Color from 'color';
|
|
import ColorConvert from 'color-convert';
|
|
|
|
export default class CanvasApp {
|
|
|
|
constructor ( ) {
|
|
this.audio = new NiceAudio();
|
|
|
|
this.log = new NiceLog(DTP_COMPONENT_NAME);
|
|
this.log.info('init', 'Canvas app online');
|
|
|
|
this.initCanvas();
|
|
this.initColorPalette();
|
|
|
|
document.addEventListener('mpcvs:socket-connected', this.onSocketConnected.bind(this));
|
|
|
|
this.socket = new CanvasSocket();
|
|
this.socket.connect({
|
|
withRetry: true,
|
|
withError: false,
|
|
connectToken: window.dtp.connectToken,
|
|
});
|
|
}
|
|
|
|
async onSocketConnected ( ) {
|
|
console.log('APP: socket connected');
|
|
|
|
const { socket } = this.socket;
|
|
socket.on('canvas-error', this.onCanvasError.bind(this));
|
|
socket.on('canvas-image-data', this.onCanvasImageData.bind(this));
|
|
socket.on('canvas-setpixel', this.onCanvasSetPixel.bind(this));
|
|
|
|
socket.emit('getimagedata');
|
|
}
|
|
|
|
async onCanvasError (message) {
|
|
console.log('onCanvasError', { message });
|
|
}
|
|
|
|
async onCanvasImageData (message) {
|
|
console.log('onCanvasImageData', { message });
|
|
this.imageData = new ImageData( // jshint ignore:line
|
|
new Uint8ClampedArray(message.data),
|
|
message.width,
|
|
message.height,
|
|
);
|
|
this.ctx.putImageData(this.imageData, 0, 0);
|
|
}
|
|
|
|
async onCanvasSetPixel (message) {
|
|
console.log('onCanvasSetPixel', message);
|
|
this.setPixel(message);
|
|
this.ctx.putImageData(this.imageData, 0, 0);
|
|
}
|
|
|
|
initCanvas ( ) {
|
|
this.canvas = document.querySelector('canvas');
|
|
this.canvas.onclick = this.onCanvasClick.bind(this);
|
|
this.ctx = this.canvas.getContext('2d');
|
|
}
|
|
|
|
initColorPalette ( ) {
|
|
const setDrawColor = this.setDrawColor.bind(this);
|
|
const palette = document.querySelector('.color-palette');
|
|
const colors = [ ];
|
|
|
|
for (let h = 0; h <= 360; h += 36) {
|
|
for (let v = 25; v <= 75; v += 25) {
|
|
for (let s = 25; s <= 100; s += 25) {
|
|
colors.push('#' + ColorConvert.hsl.hex(h, s, v));
|
|
}
|
|
}
|
|
}
|
|
|
|
// for (let r = 0; r <= 255; r+= 64) {
|
|
// for (let g = 0; g <= 255; g += 64) {
|
|
// for (let b = 0; b <= 255; b += 64) {
|
|
// const c = Color.hsl() Color({ r, g, b });
|
|
// colors.push(c.hex());
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
colors.push("#ffffff"); // white
|
|
colors.push("#000000"); // black
|
|
colors.push("#ff0000"); // red
|
|
colors.push("#00ff00"); // green
|
|
colors.push("#0000ff"); // blue
|
|
|
|
this.colorButtons = [ ];
|
|
let idx = 0;
|
|
colors.forEach((color) => {
|
|
const button = document.createElement('button');
|
|
|
|
button.classList.add('color-select-btn');
|
|
button.setAttribute('data-idx', idx++);
|
|
button.setAttribute('data-color', color);
|
|
button.onclick = setDrawColor;
|
|
button.style.backgroundColor = color;
|
|
|
|
this.colorButtons.push(button);
|
|
palette.appendChild(button);
|
|
});
|
|
|
|
this.colorButtons[0].click();
|
|
}
|
|
|
|
setDrawColor (event) {
|
|
const target = event.target || event.currentTarget;
|
|
const color = target.getAttribute('data-color');
|
|
|
|
this.log.info('setDrawColor', 'setting draw color', { color });
|
|
this.currentColor = Color(color);
|
|
|
|
this.colorButtons.forEach((button) => {
|
|
button.classList.remove('active');
|
|
});
|
|
|
|
target.classList.add('active');
|
|
}
|
|
|
|
updatePixels (message) {
|
|
for (const pixel of message.pixels) {
|
|
if ((pixel.x < 0) ||
|
|
(pixel.x > (IMAGE_W - 1)) ||
|
|
(pixel.y < 0) ||
|
|
(pixel.y > (IMAGE_H - 1))) {
|
|
this.log.info('updatePixels', 'rejecting invalid pixel update', pixel);
|
|
return;
|
|
}
|
|
}
|
|
this.ctx.putImageData(this.imageData, 0, 0);
|
|
}
|
|
|
|
setPixel (pixel) {
|
|
let pixelIdx = ((pixel.y * IMAGE_W ) + pixel.x) * 4;
|
|
this.log.info('updatePixels', 'updating pixel', { pixelIdx, pixel });
|
|
|
|
this.imageData.data[pixelIdx++] = pixel.r;
|
|
this.imageData.data[pixelIdx++] = pixel.g;
|
|
this.imageData.data[pixelIdx++] = pixel.b;
|
|
this.imageData.data[pixelIdx++] = 255;
|
|
}
|
|
|
|
async onCanvasClick (event) {
|
|
const cellPixels = this.canvas.clientWidth / 32;
|
|
if (cellPixels === 0) {
|
|
return; // prevent a divide by zero
|
|
}
|
|
const x = Math.floor(event.offsetX / cellPixels);
|
|
const y = Math.floor(event.offsetY / cellPixels);
|
|
console.log(x, y, this.currentColor.hex());
|
|
|
|
const pixel = {
|
|
x, y,
|
|
r: this.currentColor.red(),
|
|
g: this.currentColor.green(),
|
|
b: this.currentColor.blue(),
|
|
};
|
|
this.socket.socket.emit('setpixel', pixel);
|
|
}
|
|
}
|
|
|
|
window.addEventListener('load', async ( ) => {
|
|
window.dtp.app = new CanvasApp();
|
|
});
|