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.

311 lines
8.3 KiB

2 years ago
let ws = null;
let ls = window.localStorage;
document.addEventListener('DOMContentLoaded', main);
const EVENT = Object.freeze({
PREDRAW: 10,
STROKE: 20,
RULER: 21, /* gets re-written with EVENT.STROKE before sending to server */
2 years ago
UNDO: 30,
REDO: 31,
2 years ago
IMAGE: 40,
IMAGE_MOVE: 41,
2 years ago
ERASER: 50,
2 years ago
});
2 years ago
2 years ago
const MESSAGE = Object.freeze({
INIT: 100,
SYN: 101,
ACK: 102,
FULL: 103,
FIRE: 104,
JOIN: 105,
});
const config = {
ws_url: 'ws://192.168.100.2/ws/',
image_url: 'http://192.168.100.2/images/',
2 years ago
sync_timeout: 1000,
ws_reconnect_timeout: 2000,
second_finger_timeout: 500,
buffer_first_touchmoves: 5,
debug_print: false,
2 years ago
};
const storage = {
'state': {
'drawing': false,
'moving': false,
'moving_image': false,
2 years ago
'mousedown': false,
'spacedown': false,
2 years ago
},
'moving_image_original_x': null,
'moving_image_original_y': null,
'touch': {
'moves': 0,
'drawing': false,
'moving': false,
'waiting_for_second_finger': false,
'position': { 'x': null, 'y': null },
'screen_position': { 'x': null, 'y': null },
'finger_distance': null,
'buffered': [],
'ids': [],
},
'tools': {
'active': null,
'active_element': null,
},
'ruler_origin': {},
2 years ago
'erased': [],
2 years ago
'predraw': {},
'timers': {},
'me': {},
'sn': 0, // what WE think SERVER SN is (we tell this to the server, it uses to decide how much stuff to SYN to us)
'server_lsn': 0, // what SERVER said LSN is (used to decide how much stuff to SYN)
'lsn': 0, // what actual LSN is (can't just use length of local queue because it gets cleared)
'queue': [], // to server
'events': [], // from server
'current_stroke': [],
'desk_id': 123,
2 years ago
'max_zoom': 4,
'min_zoom': 0.2,
'images': {},
2 years ago
'canvas': {
2 years ago
'zoom': 1,
'width': 1500,
'height': 4000,
2 years ago
'offset_x': 0,
'offset_y': 0,
},
'cursor': {
'width': 8,
'color': 'rgb(0, 0, 0)',
2 years ago
'x': 0,
'y': 0,
}
};
const elements = {
'cursor': null,
'canvas0': null,
'canvas1': null,
'active_image': null,
2 years ago
};
2 years ago
function move_canvas() {
elements.canvas0.style.transform = `translate(${-storage.canvas.offset_x}px, ${-storage.canvas.offset_y}px) scale(${storage.canvas.zoom})`;
elements.canvas1.style.transform = `translate(${-storage.canvas.offset_x}px, ${-storage.canvas.offset_y}px) scale(${storage.canvas.zoom})`;
elements.images.style.transform = `translate(${-storage.canvas.offset_x}px, ${-storage.canvas.offset_y}px) scale(${storage.canvas.zoom})`;
2 years ago
}
function image_at(x, y) {
let image_hit = null;
for (let i = storage.events.length - 1; i >= 0; --i) {
if (!storage.events[i].deleted && storage.events[i].type === EVENT.IMAGE) {
const event = storage.events[i];
const item = document.querySelector(`img[data-image-id="${event.image_id}"]`);
if (item) {
const left = storage.images[event.image_id].x;
const right = left + item.width;
const top = storage.images[event.image_id].y;
const bottom = top + item.height;
if (left <= x && x <= right && top <= y && y <= bottom) {
return item;
}
}
}
}
return null;
}
function activate_image(item) {
if (item === null) {
elements.canvas1.classList.remove('disabled');
if (elements.active_image) {
elements.active_image.classList.remove('activated');
elements.active_image = null;
}
return;
}
elements.canvas1.classList.add('disabled');
if (elements.active_image) {
if (elements.active_image === item) {
return;
}
elements.active_image.classList.remove('activated');
}
elements.active_image = item;
item.classList.add('activated');
}
2 years ago
function predraw_event(x, y) {
return {
'type': EVENT.PREDRAW,
'x': x,
'y': y
};
}
function stroke_event() {
return {
'type': EVENT.STROKE,
'points': storage.current_stroke,
'width': storage.cursor.width,
'color': color_to_u32(storage.cursor.color),
2 years ago
};
}
function ruler_event(x, y) {
const points = [];
points.push(predraw_event(storage.ruler_origin.x, storage.ruler_origin.y));
points.push(predraw_event(x, y));
return {
'type': EVENT.RULER,
'points': points,
'width': storage.cursor.width,
'color': color_to_u32(storage.cursor.color),
};
}
2 years ago
function undo_event() {
return { 'type': EVENT.UNDO };
}
function redo_event() {
return { 'type': EVENT.REDO };
}
2 years ago
function image_event(image_id, x, y) {
return {
'type': EVENT.IMAGE,
'image_id': image_id,
'x': x,
'y': y,
}
}
function image_move_event(image_id, x, y) {
return {
'type': EVENT.IMAGE_MOVE,
'image_id': image_id,
'x': x,
'y': y,
}
}
2 years ago
function eraser_events() {
const result = [];
for (const stroke_id of storage.erased) {
result.push({
'type': EVENT.ERASER,
'stroke_id': stroke_id,
});
}
return result;
}
// Generally doesn't return null
function find_stroke_backwards(stroke_id) {
for (let i = storage.events.length - 1; i >= 0; --i) {
const event = storage.events[i];
if (event.type === EVENT.STROKE && event.stroke_id === stroke_id) {
return event;
}
}
return null;
}
function queue_undo() {
const event = undo_event();
queue_event(event);
}
2 years ago
function main() {
const url = new URL(window.location.href);
const parts = url.pathname.split('/');
storage.desk_id = parts.length > 0 ? parts[parts.length - 1] : 0;
ws_connect(true);
2 years ago
elements.canvas0 = document.getElementById('canvas0');
elements.canvas1 = document.getElementById('canvas1');
elements.images = document.getElementById('canvas-images');
tools_init();
// TODO: remove
elements.brush_color = document.getElementById('brush-color');
elements.brush_width = document.getElementById('brush-width');
elements.brush_preview = document.getElementById('brush-preview');
elements.toucher = document.getElementById('toucher');
elements.brush_color.value = storage.cursor.color;
elements.brush_width.value = storage.cursor.width;
update_brush();
2 years ago
// storage.canvas.offset_x = window.scrollX;
// storage.canvas.offset_y = window.scrollY;
2 years ago
// storage.canvas.max_scroll_x = storage.canvas.width - window.innerWidth;
// storage.canvas.max_scroll_y = storage.canvas.height - window.innerHeight;
2 years ago
storage.ctx0 = elements.canvas0.getContext('2d');
storage.ctx1 = elements.canvas1.getContext('2d');
on_resize();
// storage.ctx1.canvas.width = storage.ctx0.canvas.width = storage.canvas.width;
// storage.ctx1.canvas.height = storage.ctx0.canvas.height = storage.canvas.height;
2 years ago
storage.ctx1.lineJoin = storage.ctx1.lineCap = storage.ctx0.lineJoin = storage.ctx0.lineCap = 'round';
storage.ctx1.lineWidth = storage.ctx0.lineWidth = storage.cursor.width;
elements.toucher.addEventListener('mousedown', on_down)
elements.toucher.addEventListener('mousemove', on_move)
elements.toucher.addEventListener('mouseup', on_up);
elements.toucher.addEventListener('keydown', on_keydown);
elements.toucher.addEventListener('keyup', on_keyup);
elements.toucher.addEventListener('contextmenu', cancel);
elements.toucher.addEventListener('wheel', on_wheel);
elements.toucher.addEventListener('touchstart', on_touchstart);
elements.toucher.addEventListener('touchmove', on_touchmove);
elements.toucher.addEventListener('touchend', on_touchend);
elements.toucher.addEventListener('touchcancel', on_touchend);
2 years ago
elements.brush_color.addEventListener('input', update_brush);
elements.brush_width.addEventListener('input', update_brush);
2 years ago
elements.canvas0.addEventListener('dragover', on_move);
elements.canvas0.addEventListener('drop', on_drop);
elements.canvas0.addEventListener('mouseleave', on_leave);
window.addEventListener('resize', on_resize);
2 years ago
}