A.Olokhtonov
2 years ago
19 changed files with 928 additions and 72 deletions
Before Width: | Height: | Size: 957 B After Width: | Height: | Size: 957 B |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
function tools_switch(tool) { |
||||
if (storage.tools.active_element) { |
||||
storage.tools.active_element.classList.remove('active'); |
||||
} |
||||
|
||||
storage.tools.active = tool; |
||||
storage.tools.active_element = document.querySelector(`.tool[data-tool="${tool}"`); |
||||
storage.tools.active_element.classList.add('active'); |
||||
} |
||||
|
||||
function tools_init() { |
||||
const pencil = document.querySelector('.tool[data-tool="pencil"]'); |
||||
const ruler = document.querySelector('.tool[data-tool="ruler"]'); |
||||
const eraser = document.querySelector('.tool[data-tool="eraser"]'); |
||||
const undo = document.querySelector('.tool[data-tool="undo"]'); |
||||
|
||||
pencil.addEventListener('click', () => tools_switch('pencil')); |
||||
ruler.addEventListener('click', () => tools_switch('ruler')); |
||||
eraser.addEventListener('click', () => tools_switch('eraser')); |
||||
undo.addEventListener('click', queue_undo); |
||||
|
||||
tools_switch('pencil'); |
||||
} |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
@media (pointer:none), (pointer:coarse) { |
||||
} |
@ -0,0 +1,359 @@
@@ -0,0 +1,359 @@
|
||||
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; |
||||
} |
||||
} |
Loading…
Reference in new issue