HTML Canvas Drawing With Mouse and Touch

Introduction

HTML canvas can be used for sketching. In this blog post, I would like to quickly discuss how to use JavaScript to implement a canvas with both mouse and touch sketching.

Canvas

The canvas can be used with any devices that use mouse or touch screen.

Line width : Color :

Source Code

The source code was created by combining the HTML canvas mouse drawing implementation and the HTML canvas touch drawing implementation from my previous blog posts.

canvas.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
<style>
#canvas_div {
text-align: center;
display: block;
margin-left: auto;
margin-right: auto;
}
canvas {
border: 2px solid black;
}
</style>

<div id="canvas_div" style="overflow-x: auto;">
<canvas id="canvas" width="900" height="360"></canvas>
<button onclick="javascript:clearArea();return false;">Clear Area</button>
Line width : <select id="selWidth">
<option value="11">11</option>
<option value="13" selected="selected">13</option>
<option value="15">15</option>
</select>
Color : <select id="selColor">
<option value="black">black</option>
<option value="blue" selected="selected">blue</option>
<option value="red">red</option>
<option value="green">green</option>
<option value="yellow">yellow</option>
<option value="gray">gray</option>
</select>
</div>

<script>
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
let isDrawing = false;
let x = 0;
let y = 0;
var offsetX;
var offsetY;

function startup() {
canvas.addEventListener('touchstart', handleStart);
canvas.addEventListener('touchend', handleEnd);
canvas.addEventListener('touchcancel', handleCancel);
canvas.addEventListener('touchmove', handleMove);
canvas.addEventListener('mousedown', (e) => {
x = e.offsetX;
y = e.offsetY;
isDrawing = true;
});

canvas.addEventListener('mousemove', (e) => {
if (isDrawing) {
drawLine(context, x, y, e.offsetX, e.offsetY);
x = e.offsetX;
y = e.offsetY;
}
});

canvas.addEventListener('mouseup', (e) => {
if (isDrawing) {
drawLine(context, x, y, e.offsetX, e.offsetY);
x = 0;
y = 0;
isDrawing = false;
}
});
}

document.addEventListener("DOMContentLoaded", startup);

const ongoingTouches = [];

function handleStart(evt) {
evt.preventDefault();
const touches = evt.changedTouches;
offsetX = canvas.getBoundingClientRect().left;
offsetY = canvas.getBoundingClientRect().top;
for (let i = 0; i < touches.length; i++) {
ongoingTouches.push(copyTouch(touches[i]));
}
}

function handleMove(evt) {
evt.preventDefault();
const touches = evt.changedTouches;
for (let i = 0; i < touches.length; i++) {
const color = document.getElementById('selColor').value;
const idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
context.beginPath();
context.moveTo(ongoingTouches[idx].clientX - offsetX, ongoingTouches[idx].clientY - offsetY);
context.lineTo(touches[i].clientX - offsetX, touches[i].clientY - offsetY);
context.lineWidth = document.getElementById('selWidth').value;
context.strokeStyle = color;
context.lineJoin = "round";
context.closePath();
context.stroke();
ongoingTouches.splice(idx, 1, copyTouch(touches[i])); // swap in the new touch record
}
}
}

function handleEnd(evt) {
evt.preventDefault();
const touches = evt.changedTouches;
for (let i = 0; i < touches.length; i++) {
const color = document.getElementById('selColor').value;
let idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
context.lineWidth = document.getElementById('selWidth').value;
context.fillStyle = color;
ongoingTouches.splice(idx, 1); // remove it; we're done
}
}
}

function handleCancel(evt) {
evt.preventDefault();
const touches = evt.changedTouches;
for (let i = 0; i < touches.length; i++) {
let idx = ongoingTouchIndexById(touches[i].identifier);
ongoingTouches.splice(idx, 1); // remove it; we're done
}
}

function copyTouch({ identifier, clientX, clientY }) {
return { identifier, clientX, clientY };
}

function ongoingTouchIndexById(idToFind) {
for (let i = 0; i < ongoingTouches.length; i++) {
const id = ongoingTouches[i].identifier;
if (id === idToFind) {
return i;
}
}
return -1; // not found
}

function drawLine(context, x1, y1, x2, y2) {
context.beginPath();
context.strokeStyle = document.getElementById('selColor').value;
context.lineWidth = document.getElementById('selWidth').value;
context.lineJoin = "round";
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.closePath();
context.stroke();
}

function clearArea() {
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
}
</script>

References

Author

Lei Mao

Posted on

12-08-2022

Updated on

12-08-2022

Licensed under


Comments