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