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.
359 lines
12 KiB
359 lines
12 KiB
2 years ago
|
function on_touchstart(e) {
|
||
|
e.preventDefault();
|
||
|
|
||
|
if (storage.touch.drawing) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// First finger(s) down?
|
||
|
if (storage.touch.ids.length === 0) {
|
||
|
// We only handle 1 and 2
|
||
|
if (e.changedTouches.length > 2) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
storage.touch.ids.length = 0;
|
||
|
|
||
|
for (const touch of e.changedTouches) {
|
||
|
storage.touch.ids.push(touch.identifier);
|
||
|
}
|
||
|
|
||
|
if (e.changedTouches.length === 1) {
|
||
|
const touch = e.changedTouches[0];
|
||
|
const x = Math.round((touch.clientX + storage.canvas.offset_x) / storage.canvas.zoom);
|
||
|
const y = Math.round((touch.clientY + storage.canvas.offset_y) / storage.canvas.zoom);
|
||
|
|
||
|
storage.touch.position.x = x;
|
||
|
storage.touch.position.y = y;
|
||
|
|
||
|
// We give a bit of time to add a second finger
|
||
|
storage.touch.waiting_for_second_finger = true;
|
||
|
storage.touch.moves = 0;
|
||
|
storage.touch.buffered.length = 0;
|
||
|
storage.ruler_origin.x = x;
|
||
|
storage.ruler_origin.y = y;
|
||
|
|
||
|
setTimeout(() => {
|
||
|
storage.touch.waiting_for_second_finger = false;
|
||
|
}, config.second_finger_timeout);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// There are touches already
|
||
|
if (storage.touch.waiting_for_second_finger) {
|
||
|
if (e.changedTouches.length === 1) {
|
||
|
const changed_touch = e.changedTouches[0];
|
||
|
|
||
|
storage.touch.screen_position.x = changed_touch.clientX;
|
||
|
storage.touch.screen_position.y = changed_touch.clientY;
|
||
|
|
||
|
storage.touch.ids.push(e.changedTouches[0].identifier);
|
||
|
|
||
|
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 x = touch.clientX;
|
||
|
const y = touch.clientY;
|
||
|
|
||
|
if (touch.identifier === storage.touch.ids[0]) {
|
||
|
first_finger_position = {'x': x, 'y': y};
|
||
|
}
|
||
|
|
||
|
if (touch.identifier === storage.touch.ids[1]) {
|
||
|
second_finger_position = {'x': x, 'y': y};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
storage.touch.finger_distance = dist_v2(
|
||
|
first_finger_position, second_finger_position);
|
||
|
|
||
|
// console.log(storage.touch.finger_distance);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function on_touchmove(e) {
|
||
|
if (storage.touch.ids.length === 1 && !storage.touch.moving) {
|
||
|
storage.touch.moves += 1;
|
||
|
|
||
|
if (storage.touch.moves > config.buffer_first_touchmoves) {
|
||
|
storage.touch.waiting_for_second_finger = false; // Immediately start drawing on move
|
||
|
storage.touch.drawing = true;
|
||
|
|
||
|
if (storage.ctx1.lineWidth !== storage.cursor.width) {
|
||
|
storage.ctx1.lineWidth = storage.cursor.width;
|
||
|
}
|
||
|
} else {
|
||
|
let drawing_touch = null;
|
||
|
|
||
|
for (const touch of e.changedTouches) {
|
||
|
if (touch.identifier === storage.touch.ids[0]) {
|
||
|
drawing_touch = touch;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!drawing_touch) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const last_x = storage.touch.position.x;
|
||
|
const last_y = storage.touch.position.y;
|
||
|
|
||
|
const x = Math.max(Math.round((drawing_touch.clientX + storage.canvas.offset_x) / storage.canvas.zoom), 0);
|
||
|
const y = Math.max(Math.round((drawing_touch.clientY + storage.canvas.offset_y) / storage.canvas.zoom), 0);
|
||
|
|
||
|
storage.touch.buffered.push({
|
||
|
'last_x': last_x,
|
||
|
'last_y': last_y,
|
||
|
'x': x,
|
||
|
'y': y,
|
||
|
});
|
||
|
|
||
|
storage.touch.position.x = x;
|
||
|
storage.touch.position.y = y;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (storage.touch.drawing) {
|
||
|
let drawing_touch = null;
|
||
|
|
||
|
for (const touch of e.changedTouches) {
|
||
|
if (touch.identifier === storage.touch.ids[0]) {
|
||
|
drawing_touch = touch;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!drawing_touch) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const last_x = storage.touch.position.x;
|
||
|
const last_y = storage.touch.position.y;
|
||
|
|
||
|
const x = storage.touch.position.x = Math.max(Math.round((drawing_touch.clientX + storage.canvas.offset_x) / storage.canvas.zoom), 0);
|
||
|
const y = storage.touch.position.y = Math.max(Math.round((drawing_touch.clientY + storage.canvas.offset_y) / storage.canvas.zoom), 0);
|
||
|
|
||
|
if (storage.tools.active === 'pencil') {
|
||
|
if (storage.touch.buffered.length > 0) {
|
||
|
for (const p of storage.touch.buffered) {
|
||
|
storage.ctx1.beginPath();
|
||
|
|
||
|
storage.ctx1.moveTo(p.last_x, p.last_y);
|
||
|
storage.ctx1.lineTo(p.x, p.y);
|
||
|
|
||
|
storage.ctx1.stroke();
|
||
|
|
||
|
const predraw = predraw_event(p.x, p.y);
|
||
|
storage.current_stroke.push(predraw);
|
||
|
|
||
|
fire_event(predraw);
|
||
|
}
|
||
|
|
||
|
storage.touch.buffered.length = 0;
|
||
|
}
|
||
|
|
||
|
storage.ctx1.beginPath();
|
||
|
|
||
|
storage.ctx1.moveTo(last_x, last_y);
|
||
|
storage.ctx1.lineTo(x, y);
|
||
|
|
||
|
storage.ctx1.stroke();
|
||
|
|
||
|
const predraw = predraw_event(x, y);
|
||
|
storage.current_stroke.push(predraw);
|
||
|
|
||
|
fire_event(predraw);
|
||
|
|
||
|
storage.touch.position.x = x;
|
||
|
storage.touch.position.y = y;
|
||
|
|
||
|
return;
|
||
|
} else if (storage.tools.active === 'eraser') {
|
||
|
const erase_step = (last_x, last_y, x, y) => {
|
||
|
const erased = strokes_intersect_line(last_x, last_y, x, y);
|
||
|
storage.erased.push(...erased);
|
||
|
|
||
|
if (erased.length > 0) {
|
||
|
for (const other_event of storage.events) {
|
||
|
for (const stroke_id of erased) {
|
||
|
if (stroke_id === other_event.stroke_id) {
|
||
|
if (!other_event.deleted) {
|
||
|
other_event.deleted = true;
|
||
|
const stats = stroke_stats(other_event.points, storage.cursor.width);
|
||
|
redraw_region(stats.bbox);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
if (storage.touch.buffered.length > 0) {
|
||
|
for (const p of storage.touch.buffered) {
|
||
|
erase_step(p.last_x, p.last_y, p.x, p.y);
|
||
|
}
|
||
|
|
||
|
storage.touch.buffered.length = 0;
|
||
|
}
|
||
|
|
||
|
erase_step(last_x, last_y, x, y);
|
||
|
} else if (storage.tools.active === 'ruler') {
|
||
|
const old_ruler = [
|
||
|
{'x': storage.ruler_origin.x, 'y': storage.ruler_origin.y},
|
||
|
{'x': last_x, 'y': last_y}
|
||
|
];
|
||
|
|
||
|
const stats = stroke_stats(old_ruler, storage.cursor.width);
|
||
|
const bbox = stats.bbox;
|
||
|
|
||
|
storage.ctx1.clearRect(bbox.xmin, bbox.ymin, bbox.xmax - bbox.xmin, bbox.ymax - bbox.ymin);
|
||
|
|
||
|
storage.ctx1.beginPath();
|
||
|
|
||
|
storage.ctx1.moveTo(storage.ruler_origin.x, storage.ruler_origin.y);
|
||
|
storage.ctx1.lineTo(x, y);
|
||
|
|
||
|
storage.ctx1.stroke();
|
||
|
} else {
|
||
|
console.error('fuck');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (storage.touch.ids.length === 2) {
|
||
|
storage.touch.moving = true;
|
||
|
|
||
|
let first_finger_position_screen = null;
|
||
|
let second_finger_position_screen = null;
|
||
|
|
||
|
let first_finger_position_canvas = null;
|
||
|
let second_finger_position_canvas = 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 x = touch.clientX;
|
||
|
const y = touch.clientY;
|
||
|
|
||
|
const xc = Math.max(Math.round((touch.clientX + storage.canvas.offset_x) / storage.canvas.zoom), 0);
|
||
|
const yc = Math.max(Math.round((touch.clientY + storage.canvas.offset_y) / storage.canvas.zoom), 0);
|
||
|
|
||
|
if (touch.identifier === storage.touch.ids[0]) {
|
||
|
first_finger_position_screen = {'x': x, 'y': y};
|
||
|
first_finger_position_canvas = {'x': xc, 'y': yc};
|
||
|
}
|
||
|
|
||
|
if (touch.identifier === storage.touch.ids[1]) {
|
||
|
second_finger_position_screen = {'x': x, 'y': y};
|
||
|
second_finger_position_canvas = {'x': xc, 'y': yc};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const new_finger_distance = dist_v2(
|
||
|
first_finger_position_screen, second_finger_position_screen);
|
||
|
|
||
|
const zoom_center = {
|
||
|
'x': (first_finger_position_canvas.x + second_finger_position_canvas.x) / 2.0,
|
||
|
'y': (first_finger_position_canvas.y + second_finger_position_canvas.y) / 2.0
|
||
|
};
|
||
|
|
||
|
for (const touch of e.changedTouches) {
|
||
|
// The second finger to be down is considered the "main" one
|
||
|
// Movement of the second finger is ignored
|
||
|
if (touch.identifier === storage.touch.ids[1]) {
|
||
|
const x = Math.round(touch.clientX);
|
||
|
const y = Math.round(touch.clientY);
|
||
|
|
||
|
const dx = x - storage.touch.screen_position.x;
|
||
|
const dy = y - storage.touch.screen_position.y;
|
||
|
|
||
|
const old_zoom = storage.canvas.zoom;
|
||
|
const old_offset_x = storage.canvas.offset_x;
|
||
|
const old_offset_y = storage.canvas.offset_y;
|
||
|
|
||
|
storage.canvas.offset_x -= dx;
|
||
|
storage.canvas.offset_y -= dy;
|
||
|
|
||
|
// console.log(new_finger_distance, storage.touch.finger_distance);
|
||
|
|
||
|
const scale_by = new_finger_distance / storage.touch.finger_distance;
|
||
|
const dz = storage.canvas.zoom * (scale_by - 1.0);
|
||
|
|
||
|
const zoom_offset_y = Math.round(dz * zoom_center.y);
|
||
|
const zoom_offset_x = Math.round(dz * zoom_center.x);
|
||
|
|
||
|
if (storage.min_zoom <= storage.canvas.zoom * scale_by && storage.canvas.zoom * scale_by <= storage.max_zoom) {
|
||
|
storage.canvas.zoom *= scale_by;
|
||
|
storage.canvas.offset_x += zoom_offset_x;
|
||
|
storage.canvas.offset_y += zoom_offset_y;
|
||
|
}
|
||
|
|
||
|
storage.touch.finger_distance = new_finger_distance;
|
||
|
|
||
|
|
||
|
if (storage.canvas.offset_x !== old_offset_x || storage.canvas.offset_y !== old_offset_y || old_zoom !== storage.canvas.zoom) {
|
||
|
move_canvas();
|
||
|
}
|
||
|
|
||
|
storage.touch.screen_position.x = x;
|
||
|
storage.touch.screen_position.y = y;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async function on_touchend(e) {
|
||
|
for (const touch of e.changedTouches) {
|
||
|
if (storage.touch.drawing) {
|
||
|
if (storage.touch.ids[0] == touch.identifier) {
|
||
|
storage.touch.drawing = false;
|
||
|
|
||
|
if (storage.tools.active === 'pencil') {
|
||
|
const event = stroke_event();
|
||
|
storage.current_stroke = [];
|
||
|
await queue_event(event);
|
||
|
} else if (storage.tools.active === 'eraser') {
|
||
|
const events = eraser_events();
|
||
|
storage.erased = [];
|
||
|
if (events.length > 0) {
|
||
|
for (const event of events) {
|
||
|
await queue_event(event);
|
||
|
}
|
||
|
}
|
||
|
} else if (storage.tools.active === 'ruler') {
|
||
|
const event = ruler_event(storage.touch.position.x, storage.touch.position.y);
|
||
|
await queue_event(event);
|
||
|
} else {
|
||
|
console.error('fuck');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const index = storage.touch.ids.indexOf(touch.identifier);
|
||
|
|
||
|
if (index !== -1) {
|
||
|
storage.touch.ids.splice(index, 1);
|
||
|
}
|
||
|
|
||
|
if (storage.touch.moving && storage.touch.ids.length === 0) {
|
||
|
// Only allow drawing again when ALL fingers have been lifted
|
||
|
storage.touch.moving = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (storage.touch.ids.length === 0) {
|
||
|
waiting_for_second_finger = false;
|
||
|
}
|
||
|
}
|