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
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; |
|
} |
|
} |