Browse Source

Move images around

master
A.Olokhtonov 1 year ago
parent
commit
70d8ab883d
  1. 76
      client/cursor.js
  2. 16
      client/default.css
  3. 5
      client/draw.js
  4. 67
      client/index.js
  5. 33
      client/recv.js
  6. 4
      client/send.js
  7. 3
      server/deserializer.js
  8. 1
      server/enums.js
  9. 1
      server/recv.js
  10. 3
      server/send.js
  11. 3
      server/serializer.js

76
client/cursor.js

@ -1,48 +1,73 @@ @@ -1,48 +1,73 @@
function on_down(e) {
const x = Math.round((e.clientX + storage.canvas.offset_x) / storage.canvas.zoom);
const y = Math.round((e.clientY + storage.canvas.offset_y) / storage.canvas.zoom);
if (e.button === 1) {
elements.canvas0.classList.add('moving');
storage.state.moving = true;
storage.state.mousedown = true;
return;
}
if (e.button != 0) {
if (e.button === 2) {
const image_hit = image_at(x, y);
activate_image(image_hit);
e.preventDefault();
return;
}
if (storage.state.moving) {
storage.state.mousedown = true;
return;
}
if (e.button === 0) {
const image_hit = image_at(x, y);
const x = Math.round((e.clientX + storage.canvas.offset_x) / storage.canvas.zoom);
const y = Math.round((e.clientY + storage.canvas.offset_y) / storage.canvas.zoom);
if (elements.active_image !== null && image_hit !== null) {
storage.state.moving_image = true;
return;
}
storage.state.drawing = true;
if (storage.state.moving) {
storage.state.mousedown = true;
return;
}
if (storage.ctx1.lineWidth !== storage.cursor.width) {
storage.ctx1.lineWidth = storage.cursor.width;
}
storage.state.drawing = true;
storage.cursor.x = x;
storage.cursor.y = y;
if (storage.ctx1.lineWidth !== storage.cursor.width) {
storage.ctx1.lineWidth = storage.cursor.width;
}
const predraw = predraw_event(x, y);
storage.current_stroke.push(predraw);
storage.cursor.x = x;
storage.cursor.y = y;
fire_event(predraw);
const predraw = predraw_event(x, y);
storage.current_stroke.push(predraw);
fire_event(predraw);
}
}
function on_move(e) {
const last_x = storage.cursor.x;
const last_y = storage.cursor.y;
const x = storage.cursor.x = Math.round((e.clientX + storage.canvas.offset_x) / storage.canvas.zoom);
const y = storage.cursor.y = Math.round((e.clientY + storage.canvas.offset_y) / storage.canvas.zoom);
const x = storage.cursor.x = Math.max(Math.round((e.clientX + storage.canvas.offset_x) / storage.canvas.zoom), 0);
const y = storage.cursor.y = Math.max(Math.round((e.clientY + storage.canvas.offset_y) / storage.canvas.zoom), 0);
const old_offset_x = storage.canvas.offset_x;
const old_offset_y = storage.canvas.offset_y;
if (elements.active_image && storage.state.moving_image) {
const dx = Math.round(e.movementX / storage.canvas.zoom);
const dy = Math.round(e.movementY / storage.canvas.zoom);
const image_id = elements.active_image.getAttribute('data-image-id');
const ix = storage.images[image_id].x += dx;
const iy = storage.images[image_id].y += dy;
elements.active_image.style.transform = `translate(${ix}px, ${iy}px)`;
return;
}
if (storage.state.drawing) {
const width = storage.cursor.width;
@ -75,10 +100,18 @@ function on_move(e) { @@ -75,10 +100,18 @@ function on_move(e) {
}
async function on_up(e) {
if (storage.state.moving_image && e.button === 0) {
storage.state.moving_image = false;
const image_id = elements.active_image.getAttribute('data-image-id');
const position = storage.images[image_id];
const event = image_move_event(image_id, position.x, position.y);
await queue_event(event);
return;
}
if (storage.state.moving && (e.button === 1 || e.button === 0)) {
storage.state.mousedown = false;
if (!storage.state.spacedown) {
elements.canvas0.classList.remove('moving');
storage.state.moving = false;
return;
}
@ -95,7 +128,6 @@ async function on_up(e) { @@ -95,7 +128,6 @@ async function on_up(e) {
function on_keydown(e) {
if (e.code === 'Space' && !storage.state.drawing) {
elements.canvas0.classList.add('moving');
storage.state.moving = true;
storage.state.spacedown = true;
return;
@ -110,7 +142,6 @@ function on_keydown(e) { @@ -110,7 +142,6 @@ function on_keydown(e) {
function on_keyup(e) {
if (e.code === 'Space' && storage.state.moving) {
elements.canvas0.classList.remove('moving');
storage.state.moving = false;
storage.state.spacedown = false;
}
@ -118,7 +149,6 @@ function on_keyup(e) { @@ -118,7 +149,6 @@ function on_keyup(e) {
function on_leave(e) {
if (storage.state.moving) {
elements.canvas0.classList.remove('moving');
storage.state.moving = false;
storage.state.holding = false;
return;

16
client/default.css

@ -9,23 +9,19 @@ html, body { @@ -9,23 +9,19 @@ html, body {
}
.canvas {
cursor: crosshair;
position: absolute;
top: 0;
left: 0;
opacity: 1;
transition: opacity .2s;
transform-origin: top left;
pointer-events: none;
}
.canvas.white {
opacity: 0;
}
.canvas.moving {
cursor: move;
}
#canvas-images {
z-index: 0;
}
@ -38,7 +34,6 @@ html, body { @@ -38,7 +34,6 @@ html, body {
#canvas1 {
z-index: 2;
pointer-events: none;
opacity: 0.3;
}
@ -90,5 +85,12 @@ html, body { @@ -90,5 +85,12 @@ html, body {
.floating-image {
position: absolute;
pointer-events: none;
user-drag: none;
user-select: none;
}
.floating-image.activated {
outline: 5px solid #5286ff;
z-index: 999999 !important;
cursor: grab;
}

5
client/draw.js

@ -55,11 +55,6 @@ function redraw_region(bbox) { @@ -55,11 +55,6 @@ function redraw_region(bbox) {
if (stroke_intersects_region(event.points, bbox)) {
draw_stroke(event);
}
} else if (event.type === EVENT.IMAGE && !event.deleted) {
const image_bbox = bitmap_bbox(event);
if (rectangles_intersect(image_bbox, bbox)) {
storage.ctx0.drawImage(event.bitmap, image_bbox.xmin, image_bbox.ymin);
}
}
}

67
client/index.js

@ -9,6 +9,7 @@ const EVENT = Object.freeze({ @@ -9,6 +9,7 @@ const EVENT = Object.freeze({
UNDO: 30,
REDO: 31,
IMAGE: 40,
IMAGE_MOVE: 41,
});
const MESSAGE = Object.freeze({
@ -31,6 +32,7 @@ const storage = { @@ -31,6 +32,7 @@ const storage = {
'state': {
'drawing': false,
'moving': false,
'moving_image': false,
'mousedown': false,
'spacedown': false,
},
@ -51,6 +53,8 @@ const storage = { @@ -51,6 +53,8 @@ const storage = {
'max_zoom': 4,
'min_zoom': 0.2,
'images': {},
'canvas': {
'zoom': 1,
'width': 4096,
@ -70,6 +74,7 @@ const elements = { @@ -70,6 +74,7 @@ const elements = {
'cursor': null,
'canvas0': null,
'canvas1': null,
'active_image': null,
};
function event_size(event) {
@ -91,7 +96,8 @@ function event_size(event) { @@ -91,7 +96,8 @@ function event_size(event) {
break;
}
case EVENT.IMAGE: {
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE: {
size += 4 + 2 + 2; // file id + x + y
break;
}
@ -110,6 +116,53 @@ function move_canvas() { @@ -110,6 +116,53 @@ function move_canvas() {
elements.images.style.transform = `translate(${-storage.canvas.offset_x}px, ${-storage.canvas.offset_y}px) scale(${storage.canvas.zoom})`;
}
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');
}
function predraw_event(x, y) {
return {
'type': EVENT.PREDRAW,
@ -144,6 +197,15 @@ function image_event(image_id, x, y) { @@ -144,6 +197,15 @@ function image_event(image_id, x, y) {
}
}
function image_move_event(image_id, x, y) {
return {
'type': EVENT.IMAGE_MOVE,
'image_id': image_id,
'x': x,
'y': y,
}
}
function main() {
const url = new URL(window.location.href);
const parts = url.pathname.split('/');
@ -183,11 +245,12 @@ function main() { @@ -183,11 +245,12 @@ function main() {
window.addEventListener('pointermove', on_move)
window.addEventListener('pointerup', on_up);
window.addEventListener('pointercancel', on_up);
window.addEventListener('touchstart', (e) => e.preventDefault());
window.addEventListener('keydown', on_keydown);
window.addEventListener('keyup', on_keyup);
window.addEventListener('resize', on_resize);
window.addEventListener('wheel', on_wheel);
window.addEventListener('touchstart', cancel);
window.addEventListener('contextmenu', cancel);
elements.brush_color.addEventListener('input', update_brush);
elements.brush_width.addEventListener('input', update_brush);

33
client/recv.js

@ -71,7 +71,8 @@ function des_event(d) { @@ -71,7 +71,8 @@ function des_event(d) {
break;
}
case EVENT.IMAGE: {
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE: {
event.image_id = des_u32(d);
event.x = des_u16(d);
event.y = des_u16(d);
@ -127,13 +128,13 @@ async function handle_event(event) { @@ -127,13 +128,13 @@ async function handle_event(event) {
const other_event = storage.events[i];
if (other_event.type === EVENT.STROKE && other_event.user_id === event.user_id && !other_event.deleted) {
other_event.deleted = true;
// const stats = stroke_stats(other_event.points, storage.cursor.width);
// redraw_region(stats.bbox);
const stats = stroke_stats(other_event.points, storage.cursor.width);
redraw_region(stats.bbox);
break;
} else if (other_event.type === EVENT.IMAGE && other_event.user_id === event.user_id && !other_event.deleted) {
// other_event.deleted = true;
// const bbox = bitmap_bbox(other_event);
// redraw_region(bbox);
other_event.deleted = true;
const item = document.querySelector(`img[data-image-id="${other_event.image_id}"]`);
if (item) item.remove();
break;
}
}
@ -144,11 +145,17 @@ async function handle_event(event) { @@ -144,11 +145,17 @@ async function handle_event(event) {
case EVENT.IMAGE: {
const url = config.image_url + event.image_id;
const item = document.createElement('img');
item.classList.add('floating-image');
item.style.left = `${event.x}px`;
item.style.top = `${event.y}px`;
item.style['z-index'] = storage.events.length;
item.setAttribute('data-image-id', event.image_id);
item.setAttribute('src', url);
item.style.transform = `translate(${event.x}px, ${event.y}px)`;
elements.images.appendChild(item);
storage.images[event.image_id] = {
'x': event.x, 'y': event.y
};
// const r = await fetch(config.image_url + event.image_id);
// const blob = await r.blob();
// const bitmap = await createImageBitmap(blob);
@ -159,6 +166,16 @@ async function handle_event(event) { @@ -159,6 +166,16 @@ async function handle_event(event) {
break;
}
case EVENT.IMAGE_MOVE: {
const image_id = event.image_id;
const item = document.querySelector(`.floating-image[data-image-id="${image_id}"]`);
item.style.transform = `translate(${event.x}px, ${event.y}px)`;
storage.images[event.image_id] = {
'x': event.x, 'y': event.y
};
break;
}
default: {
console.error('fuck');
}

4
client/send.js

@ -50,7 +50,8 @@ function ser_event(s, event) { @@ -50,7 +50,8 @@ function ser_event(s, event) {
break;
}
case EVENT.IMAGE: {
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE: {
const image_id = parseInt(event.image_id);
ser_u32(s, image_id);
ser_u16(s, event.x);
@ -145,6 +146,7 @@ function push_event(event) { @@ -145,6 +146,7 @@ function push_event(event) {
}
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE:
case EVENT.UNDO:
case EVENT.REDO: {
storage.queue.push(event);

3
server/deserializer.js

@ -55,7 +55,8 @@ export function event(d) { @@ -55,7 +55,8 @@ export function event(d) {
break;
}
case EVENT.IMAGE: {
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE: {
event.image_id = u32(d);
event.x = u16(d);
event.y = u16(d);

1
server/enums.js

@ -10,6 +10,7 @@ export const EVENT = Object.freeze({ @@ -10,6 +10,7 @@ export const EVENT = Object.freeze({
UNDO: 30,
REDO: 31,
IMAGE: 40,
IMAGE_MOVE: 41,
});
export const MESSAGE = Object.freeze({

1
server/recv.js

@ -26,6 +26,7 @@ function handle_event(session, event) { @@ -26,6 +26,7 @@ function handle_event(session, event) {
}
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE:
case EVENT.UNDO: {
storage.put_event(event);
break;

3
server/send.js

@ -21,7 +21,8 @@ function event_size(event) { @@ -21,7 +21,8 @@ function event_size(event) {
break;
}
case EVENT.IMAGE: {
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE: {
size += 4 + 2 + 2; // file id + x + y
break;
}

3
server/serializer.js

@ -51,7 +51,8 @@ export function event(s, event) { @@ -51,7 +51,8 @@ export function event(s, event) {
break;
}
case EVENT.IMAGE: {
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE: {
u32(s, event.image_id);
u16(s, event.x);
u16(s, event.y);

Loading…
Cancel
Save