Browse Source

Move images around

master
A.Olokhtonov 2 years ago
parent
commit
70d8ab883d
  1. 52
      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

52
client/cursor.js

@ -1,12 +1,25 @@
function on_down(e) { 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) { if (e.button === 1) {
elements.canvas0.classList.add('moving');
storage.state.moving = true; storage.state.moving = true;
storage.state.mousedown = true; storage.state.mousedown = true;
return; return;
} }
if (e.button != 0) { if (e.button === 2) {
const image_hit = image_at(x, y);
activate_image(image_hit);
e.preventDefault();
return;
}
if (e.button === 0) {
const image_hit = image_at(x, y);
if (elements.active_image !== null && image_hit !== null) {
storage.state.moving_image = true;
return; return;
} }
@ -15,9 +28,6 @@ function on_down(e) {
return; return;
} }
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);
storage.state.drawing = true; storage.state.drawing = true;
if (storage.ctx1.lineWidth !== storage.cursor.width) { if (storage.ctx1.lineWidth !== storage.cursor.width) {
@ -32,17 +42,32 @@ function on_down(e) {
fire_event(predraw); fire_event(predraw);
} }
}
function on_move(e) { function on_move(e) {
const last_x = storage.cursor.x; const last_x = storage.cursor.x;
const last_y = storage.cursor.y; const last_y = storage.cursor.y;
const x = storage.cursor.x = Math.round((e.clientX + storage.canvas.offset_x) / 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.round((e.clientY + storage.canvas.offset_y) / storage.canvas.zoom); 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_x = storage.canvas.offset_x;
const old_offset_y = storage.canvas.offset_y; 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) { if (storage.state.drawing) {
const width = storage.cursor.width; const width = storage.cursor.width;
@ -75,10 +100,18 @@ function on_move(e) {
} }
async function on_up(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)) { if (storage.state.moving && (e.button === 1 || e.button === 0)) {
storage.state.mousedown = false; storage.state.mousedown = false;
if (!storage.state.spacedown) { if (!storage.state.spacedown) {
elements.canvas0.classList.remove('moving');
storage.state.moving = false; storage.state.moving = false;
return; return;
} }
@ -95,7 +128,6 @@ async function on_up(e) {
function on_keydown(e) { function on_keydown(e) {
if (e.code === 'Space' && !storage.state.drawing) { if (e.code === 'Space' && !storage.state.drawing) {
elements.canvas0.classList.add('moving');
storage.state.moving = true; storage.state.moving = true;
storage.state.spacedown = true; storage.state.spacedown = true;
return; return;
@ -110,7 +142,6 @@ function on_keydown(e) {
function on_keyup(e) { function on_keyup(e) {
if (e.code === 'Space' && storage.state.moving) { if (e.code === 'Space' && storage.state.moving) {
elements.canvas0.classList.remove('moving');
storage.state.moving = false; storage.state.moving = false;
storage.state.spacedown = false; storage.state.spacedown = false;
} }
@ -118,7 +149,6 @@ function on_keyup(e) {
function on_leave(e) { function on_leave(e) {
if (storage.state.moving) { if (storage.state.moving) {
elements.canvas0.classList.remove('moving');
storage.state.moving = false; storage.state.moving = false;
storage.state.holding = false; storage.state.holding = false;
return; return;

16
client/default.css

@ -9,23 +9,19 @@ html, body {
} }
.canvas { .canvas {
cursor: crosshair;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
opacity: 1; opacity: 1;
transition: opacity .2s; transition: opacity .2s;
transform-origin: top left; transform-origin: top left;
pointer-events: none;
} }
.canvas.white { .canvas.white {
opacity: 0; opacity: 0;
} }
.canvas.moving {
cursor: move;
}
#canvas-images { #canvas-images {
z-index: 0; z-index: 0;
} }
@ -38,7 +34,6 @@ html, body {
#canvas1 { #canvas1 {
z-index: 2; z-index: 2;
pointer-events: none;
opacity: 0.3; opacity: 0.3;
} }
@ -90,5 +85,12 @@ html, body {
.floating-image { .floating-image {
position: absolute; 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) {
if (stroke_intersects_region(event.points, bbox)) { if (stroke_intersects_region(event.points, bbox)) {
draw_stroke(event); 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({
UNDO: 30, UNDO: 30,
REDO: 31, REDO: 31,
IMAGE: 40, IMAGE: 40,
IMAGE_MOVE: 41,
}); });
const MESSAGE = Object.freeze({ const MESSAGE = Object.freeze({
@ -31,6 +32,7 @@ const storage = {
'state': { 'state': {
'drawing': false, 'drawing': false,
'moving': false, 'moving': false,
'moving_image': false,
'mousedown': false, 'mousedown': false,
'spacedown': false, 'spacedown': false,
}, },
@ -51,6 +53,8 @@ const storage = {
'max_zoom': 4, 'max_zoom': 4,
'min_zoom': 0.2, 'min_zoom': 0.2,
'images': {},
'canvas': { 'canvas': {
'zoom': 1, 'zoom': 1,
'width': 4096, 'width': 4096,
@ -70,6 +74,7 @@ const elements = {
'cursor': null, 'cursor': null,
'canvas0': null, 'canvas0': null,
'canvas1': null, 'canvas1': null,
'active_image': null,
}; };
function event_size(event) { function event_size(event) {
@ -91,7 +96,8 @@ function event_size(event) {
break; break;
} }
case EVENT.IMAGE: { case EVENT.IMAGE:
case EVENT.IMAGE_MOVE: {
size += 4 + 2 + 2; // file id + x + y size += 4 + 2 + 2; // file id + x + y
break; break;
} }
@ -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})`; 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) { function predraw_event(x, y) {
return { return {
'type': EVENT.PREDRAW, 'type': EVENT.PREDRAW,
@ -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() { function main() {
const url = new URL(window.location.href); const url = new URL(window.location.href);
const parts = url.pathname.split('/'); const parts = url.pathname.split('/');
@ -183,11 +245,12 @@ function main() {
window.addEventListener('pointermove', on_move) window.addEventListener('pointermove', on_move)
window.addEventListener('pointerup', on_up); window.addEventListener('pointerup', on_up);
window.addEventListener('pointercancel', on_up); window.addEventListener('pointercancel', on_up);
window.addEventListener('touchstart', (e) => e.preventDefault());
window.addEventListener('keydown', on_keydown); window.addEventListener('keydown', on_keydown);
window.addEventListener('keyup', on_keyup); window.addEventListener('keyup', on_keyup);
window.addEventListener('resize', on_resize); window.addEventListener('resize', on_resize);
window.addEventListener('wheel', on_wheel); window.addEventListener('wheel', on_wheel);
window.addEventListener('touchstart', cancel);
window.addEventListener('contextmenu', cancel);
elements.brush_color.addEventListener('input', update_brush); elements.brush_color.addEventListener('input', update_brush);
elements.brush_width.addEventListener('input', update_brush); elements.brush_width.addEventListener('input', update_brush);

33
client/recv.js

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

4
client/send.js

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

3
server/deserializer.js

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

1
server/enums.js

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

1
server/recv.js

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

3
server/send.js

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

3
server/serializer.js

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

Loading…
Cancel
Save