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.

384 lines
12 KiB

2 years ago
function init_listeners(state, context) {
2 years ago
window.addEventListener('keydown', (e) => keydown(e, state, context));
window.addEventListener('keyup', (e) => keyup(e, state, context));
2 years ago
2 years ago
context.canvas.addEventListener('mousedown', (e) => mousedown(e, state, context));
context.canvas.addEventListener('mousemove', (e) => mousemove(e, state, context));
context.canvas.addEventListener('mouseup', (e) => mouseup(e, state, context));
2 years ago
context.canvas.addEventListener('mouseleave', (e) => mouseup(e, state, context));
2 years ago
context.canvas.addEventListener('wheel', (e) => wheel(e, state, context));
2 years ago
context.canvas.addEventListener('touchstart', (e) => touchstart(e, state, context));
context.canvas.addEventListener('touchmove', (e) => touchmove(e, state, context));
context.canvas.addEventListener('touchend', (e) => touchend(e, state, context));
context.canvas.addEventListener('touchcancel', (e) => touchend(e, state, context));
2 years ago
context.canvas.addEventListener('drop', (e) => on_drop(e, state, context));
context.canvas.addEventListener('dragover', (e) => mousemove(e, state, context));
}
function cancel(e) {
e.preventDefault();
return false;
2 years ago
}
2 years ago
function keydown(e, state, context) {
if (e.code === 'Space' && !state.drawing) {
2 years ago
state.spacedown = true;
2 years ago
context.canvas.classList.add('movemode');
2 years ago
} else if (e.code === 'KeyD') {
}
}
2 years ago
function keyup(e, state, context) {
if (e.code === 'Space' && state.spacedown) {
2 years ago
state.spacedown = false;
state.moving = false;
2 years ago
context.canvas.classList.remove('movemode');
2 years ago
}
}
function mousedown(e, state, context) {
2 years ago
if (e.button !== 0) {
return;
}
2 years ago
if (state.spacedown) {
state.moving = true;
2 years ago
context.canvas.classList.add('moving');
2 years ago
return;
}
2 years ago
const screenp = {'x': e.clientX, 'y': e.clientY};
const canvasp = screen_to_canvas(state, screenp);
2 years ago
clear_dynamic_stroke(state, context, state.me);
update_dynamic_stroke(state, context, state.me, canvasp);
2 years ago
state.drawing = true;
window.requestAnimationFrame(() => draw(state, context));
}
function mousemove(e, state, context) {
2 years ago
e.preventDefault();
2 years ago
let do_draw = false;
if (state.moving) {
state.canvas.offset.x += e.movementX;
state.canvas.offset.y += e.movementY;
do_draw = true;
}
2 years ago
const screenp = {'x': e.clientX, 'y': e.clientY};
2 years ago
const canvasp = screen_to_canvas(state, screenp);
2 years ago
2 years ago
state.cursor = screenp;
2 years ago
2 years ago
if (state.drawing) {
2 years ago
update_dynamic_stroke(state, context, state.me, canvasp);
fire_event(predraw_event(canvasp.x, canvasp.y));
2 years ago
do_draw = true;
}
if (do_draw) {
window.requestAnimationFrame(() => draw(state, context));
}
2 years ago
return false;
2 years ago
}
function mouseup(e, state, context) {
2 years ago
if (e.button !== 0) {
return;
}
if (state.moving) {
2 years ago
state.moving = false;
2 years ago
context.canvas.classList.remove('moving');
2 years ago
return;
}
if (state.drawing) {
const stroke = {
'color': state.colors.active,
2 years ago
'width': state.stroke_width,
'points': process_stroke(state.current_strokes[state.me].points),
'user_id': state.me,
2 years ago
};
add_static_stroke(state, context, stroke);
2 years ago
queue_event(state, stroke_event(state));
clear_dynamic_stroke(state, context, state.me);
2 years ago
state.drawing = false;
window.requestAnimationFrame(() => draw(state, context));
return;
}
}
function wheel(e, state, context) {
2 years ago
const screenp = {'x': e.clientX, 'y': e.clientY};
const canvasp = screen_to_canvas(state, screenp);
2 years ago
const dz = (e.deltaY < 0 ? 0.1 : -0.1);
const old_zoom = state.canvas.zoom;
state.canvas.zoom *= (1.0 + dz);
2 years ago
if (state.canvas.zoom > config.max_zoom) {
2 years ago
state.canvas.zoom = old_zoom;
return;
}
2 years ago
if (state.canvas.zoom < config.min_zoom) {
2 years ago
state.canvas.zoom = old_zoom;
return;
}
2 years ago
const zoom_offset_x = Math.round((dz * old_zoom) * canvasp.x);
const zoom_offset_y = Math.round((dz * old_zoom) * canvasp.y);
2 years ago
state.canvas.offset.x -= zoom_offset_x;
state.canvas.offset.y -= zoom_offset_y;
window.requestAnimationFrame(() => draw(state, context));
2 years ago
}
function touchstart(e, state) {
e.preventDefault();
if (state.touch.drawing) {
// Ingore subsequent touches if we are already drawing
return;
}
2 years ago
2 years ago
// First finger(s) down?
if (state.touch.ids.length === 0) {
if (e.changedTouches.length === 1) {
2 years ago
2 years ago
// We give a bit of time to add a second finger
state.touch.waiting_for_second_finger = true;
state.touch.moves = 0;
state.touch.buffered.length = 0;
state.touch.ids.push(e.changedTouches[0].identifier);
setTimeout(() => {
state.touch.waiting_for_second_finger = false;
}, config.second_finger_timeout);
} else {
console.error('Two touchstarts at the same time are not yet supported');
}
return;
}
// There are touches already
if (state.touch.waiting_for_second_finger) {
if (e.changedTouches.length === 1) {
state.touch.ids.push(e.changedTouches[0].identifier);
for (const touch of e.touches) {
const screenp = {'x': window.devicePixelRatio * touch.clientX, 'y': window.devicePixelRatio * touch.clientY};
if (touch.identifier === state.touch.ids[0]) {
state.touch.first_finger_position = screenp;
} else if (touch.identifier === state.touch.ids[1]) {
state.touch.second_finger_position = screenp;
2 years ago
}
2 years ago
}
}
return;
}
2 years ago
}
2 years ago
function touchmove(e, state, context) {
if (state.touch.ids.length === 1) {
const touch = find_touch(e.changedTouches, state.touch.ids[0]);
2 years ago
if (!touch) {
return;
}
2 years ago
const screenp = {'x': window.devicePixelRatio * touch.clientX, 'y': window.devicePixelRatio * touch.clientY};
const canvasp = screen_to_canvas(state, screenp);
if (state.touch.moving) {
// Can happen if we have been panning the canvas and lifted one finger,
// but not the second one
return;
}
if (!state.touch.drawing) {
// Buffer this move
state.touch.moves += 1;
if (state.touch.moves > config.buffer_first_touchmoves) {
// Start drawing, no more waiting
state.touch.waiting_for_second_finger = false;
state.touch.drawing = true;
} else {
state.touch.buffered.push(canvasp);
}
} else {
// Handle buffered moves
if (state.touch.buffered.length > 0) {
2 years ago
clear_dynamic_stroke(state, context, state.me);
2 years ago
// BUG: can't see these on other clients!!
2 years ago
for (const p of state.touch.buffered) {
2 years ago
update_dynamic_stroke(state, context, state.me, p);
fire_event(predraw_event(canvasp.x, canvasp.y));
2 years ago
}
state.touch.buffered.length = 0;
}
2 years ago
update_dynamic_stroke(state, context, state.me, canvasp);
fire_event(predraw_event(canvasp.x, canvasp.y));
2 years ago
window.requestAnimationFrame(() => draw(state, context));
}
return;
}
if (state.touch.ids.length === 2) {
state.touch.moving = true;
let first_finger_position = null;
let second_finger_position = null;
// A separate loop because touches might be in different order ? (question mark)
// IMPORTANT: e.touches, not e.changedTouches!
for (const touch of e.touches) {
const screenp = {'x': window.devicePixelRatio * touch.clientX, 'y': window.devicePixelRatio * touch.clientY};
if (touch.identifier === state.touch.ids[0]) {
first_finger_position = screenp;
} else if (touch.identifier === state.touch.ids[1]) {
second_finger_position = screenp;
2 years ago
}
2 years ago
}
const old_finger_midpoint = mid_v2(state.touch.first_finger_position, state.touch.second_finger_position);
const new_finger_midpoint = mid_v2(first_finger_position, second_finger_position);
2 years ago
const new_finger_midpoint_canvas = mid_v2(
2 years ago
screen_to_canvas(state, first_finger_position),
2 years ago
screen_to_canvas(state, second_finger_position)
);
2 years ago
const old_finger_distance = dist_v2(state.touch.first_finger_position, state.touch.second_finger_position);
const new_finger_distance = dist_v2(first_finger_position, second_finger_position);
const dx = new_finger_midpoint.x - old_finger_midpoint.x;
const dy = new_finger_midpoint.y - old_finger_midpoint.y;
const old_zoom = state.canvas.zoom;
state.canvas.offset.x += dx;
state.canvas.offset.y += dy;
// console.log(new_finger_distance, state.touch.finger_distance);
const scale_by = new_finger_distance / old_finger_distance;
const dz = state.canvas.zoom * (scale_by - 1.0);
2 years ago
const zoom_offset_x = dz * new_finger_midpoint_canvas.x;
const zoom_offset_y = dz * new_finger_midpoint_canvas.y;
2 years ago
if (config.min_zoom <= state.canvas.zoom * scale_by && state.canvas.zoom * scale_by <= config.max_zoom) {
state.canvas.zoom *= scale_by;
state.canvas.offset.x -= zoom_offset_x;
state.canvas.offset.y -= zoom_offset_y;
}
state.touch.first_finger_position = first_finger_position;
state.touch.second_finger_position = second_finger_position;
window.requestAnimationFrame(() => draw(state, context));
return;
}
}
function touchend(e, state, context) {
for (const touch of e.changedTouches) {
if (state.touch.drawing) {
if (state.touch.ids[0] == touch.identifier) {
// const event = stroke_event();
// await queue_event(event);
const stroke = {
'color': state.colors.active,
2 years ago
'width': state.stroke_width,
'points': process_stroke(state.current_strokes[state.me].points),
'user_id': state.me,
2 years ago
};
add_static_stroke(state, context, stroke);
2 years ago
queue_event(state, stroke_event(state));
clear_dynamic_stroke(state, context, state.me);
2 years ago
state.touch.drawing = false;
window.requestAnimationFrame(() => draw(state, context))
}
}
const index = state.touch.ids.indexOf(touch.identifier);
if (index !== -1) {
state.touch.ids.splice(index, 1);
}
}
if (state.touch.ids.length === 0) {
// Only allow drawing again when ALL fingers have been lifted
state.touch.moving = false;
waiting_for_second_finger = false;
}
2 years ago
}
async function on_drop(e, state, context) {
e.preventDefault();
if (e.dataTransfer.files.length !== 1) {
return;
}
const file = e.dataTransfer.files[0];
const bitmap = await createImageBitmap(file);
const p = { 'x': state.cursor.x, 'y': state.cursor.y };
const canvasp = screen_to_canvas(state, p);
canvasp.x -= bitmap.width / 2;
canvasp.y -= bitmap.height / 2;
add_image(context, bitmap, canvasp);
// storage.ctx0.drawImage(bitmap, x, y);
const form_data = new FormData();
form_data.append('file', file);
const resp = await fetch(`/api/image?deskId=333`, {
method: 'post',
body: form_data,
})
if (resp.ok) {
// const image_id = await resp.text();
// const event = image_event(image_id, x, y);
// await queue_event(event);
}
window.requestAnimationFrame(() => draw(state, context));
return false;
2 years ago
}