Paint
Материал из Department of Theoretical and Applied Mechanics
Описание[править]
Реализация растрового графического редактора компании Microsoft Paint на языке JavaScript.
Исполнители: Михайлов Михаил, Шульга Михаил
Группа: 3630103/90003 Кафедра Теоретической механики.
Файл с отчётом:[[1]]
Визуализация[править]
Скрипт[править]
Код программы на языке JavaScript:
1 /**
2 * Комментарии к глобальным переменным:
3 * myColor - актуальный цвет.
4 * R - радиус точки / толщина линии.
5 * flag - пока нужен только для нормальной работы функции linedraw (повторное определение точек).
6 * mode_flag - для смены режимов.
7 * defImg - дефолтный background холста (для очистки холста).
8 */
9 var canvas = document.getElementById('c1');
10 var ctx = canvas.getContext('2d');
11 var myColor = '#000000';
12 var R = 5;
13 var defImg = new Image('holst.jpg');
14 var flag ;
15 var mode_flag ;
16
17
18
19
20
21
22
23 /**
24 * Начальная заливка холста белым цветом.
25 */
26 ctx.fillStyle = "#ffffff";
27 ctx.fillRect(0,0,900,600);
28
29 /**
30 * Считывание значения ползунка.
31 */
32 document.getElementById('lineWidth').oninput = function(){
33 R = this.value;
34 }
35
36 /**
37 * Выбор цвета (считывание с виджета).
38 */
39 document.getElementById('color').oninput = function(){
40 myColor = this.value;
41 }
42
43 /**
44 * ФУНКЦИЯ ОЧИСТИТЬ ВСЕ.
45 * Переопределяем на дефолтные значения.
46 */
47 function def(){
48 ctx.clearRect(0,0,900,600);
49 flag = true;
50 mode_flag = false;
51 ctx.fillStyle = "#ffffff";
52 ctx.fillRect(0,0,900,600);
53
54 defImg.onload = function () {
55 ctx.drawImage(defImg,0,0,900,600);
56 }
57 }
58
59 /**
60 * ФУНКЦИЯ СОХРАНИТЬ РИСУНОК.
61 */
62 function saveImage(){
63 const a = document.createElement("a");
64 document.body.appendChild(a);
65 a.href = canvas.toDataURL();
66 a.download = "canvas-image/png";
67 a.click();
68 document.body.removeChild(a);
69 console.log('you save the image');
70 }
71
72 /**
73 * ФУНКЦИЯ СМЕНЫ РЕЖИМА НА ЗАЛИВКУ.
74 */
75 function changeMode_fill(){
76 mode_flag = true;
77 console.log('change mod to fill');
78 }
79
80 /**
81 * ФУНКЦИЯ СМЕНЫ РЕЖИМА НА ОБВОДКУ.
82 */
83 function changeMode_stroke(){
84 mode_flag = false;
85 console.log('change mod to stroke');
86 }
87
88
89 /* *
90 * ФУНКЦИЯ РИСОВАНИЯ ПО УМОЛЧАНИЮ.
91 * Радиус R использован как толщина линии, он считывается с ползунка.
92 * Вывод координат в консоль (console.log) (позже можно убрать).
93 * canvas.onmousemove = null даёт возможность остановить рисование когда отпсукаешь мышку.
94 * */
95 canvas.onmousedown = function(event){
96 var x = event.offsetX;
97 var y = event.offsetY;
98 ctx.beginPath();
99 ctx.arc(x, y, R, 0, 2 * Math.PI, true);
100 ctx.fillStyle = myColor ;
101 ctx.fill();
102
103 canvas.onmousemove = function(event){
104 var x1 = event.offsetX;
105 var y1 = event.offsetY;
106 console.log('cor x:',' ',x,'cor y:',' ',y);
107 console.log('cor x1:',' ',x1,'cor y1:',' ',y1);
108 ctx.beginPath();
109 ctx.arc(x1, y1, R, 0, 2 * Math.PI, true);
110 ctx.fillStyle = myColor ;
111 ctx.fill();
112 ctx.closePath();
113
114 ctx.beginPath();
115 ctx.moveTo(x,y);
116 ctx.lineTo(x1,y1);
117 ctx.lineWidth = 2*R;
118 ctx.strokeStyle = myColor;
119 ctx.stroke();
120 x = x1;
121 y = y1;
122 if(y1>599){
123 canvas.onmousemove = null;
124 }
125 if(x1<1){
126 canvas.onmousemove = null;
127 }
128 if(y1<1){
129 canvas.onmousemove = null;
130 }
131
132 if(x1>895){
133 canvas.onmousemove = null;
134 }
135
136 }
137 }
138
139 canvas.onmouseup = function(){
140 canvas.onmousemove = null;
141 }
142
143 /**
144 * ФУНКЦИЯ ЛИНИИ И ТОЧКИ.
145 * Аналогично функции рисования по умолчанию.
146 */
147 function draw(){
148
149 canvas.onmousedown = function(event){
150 var x = event.offsetX;
151 var y = event.offsetY;
152 ctx.beginPath();
153 ctx.arc(x, y, R, 0, 2 * Math.PI, true);
154 ctx.fillStyle = myColor ;
155 ctx.fill();
156
157 canvas.onmousemove = function(event){
158 var x1 = event.offsetX;
159 var y1 = event.offsetY;
160 console.log('cor x:',' ',x,'cor y:',' ',y);
161 console.log('cor x1:',' ',x1,'cor y1:',' ',y1);
162 ctx.beginPath();
163 ctx.arc(x1, y1, R, 0, 2 * Math.PI, true);
164 ctx.fillStyle = myColor ;
165 ctx.fill();
166 ctx.closePath();
167
168 ctx.beginPath();
169 ctx.moveTo(x,y);
170 ctx.lineTo(x1,y1);
171 ctx.lineWidth = 2*R;
172 ctx.strokeStyle = myColor;
173 ctx.stroke();
174 x = x1;
175 y = y1;
176 if(y1>599){
177 canvas.onmousemove = null;
178 }
179 if(x1<1){
180 canvas.onmousemove = null;
181 }
182 if(y1<1){
183 canvas.onmousemove = null;
184 }
185
186 if(x1>895){
187 canvas.onmousemove = null;
188 }
189
190
191 }
192
193 canvas.onmouseup = function(){
194 canvas.onmousemove = null;
195 }
196 }
197 }
198
199 /**
200 * ФУНКЦИЯ ЛАСТИК.
201 * Аналогична функции линии и точки, но цвет const white.
202 */
203 function eraser(){
204
205 canvas.onmousedown = function(event){
206 var x = event.offsetX;
207 var y = event.offsetY;
208 ctx.beginPath();
209 ctx.arc(x, y, R, 0, 2 * Math.PI, true);
210 ctx.fillStyle = "#ffffff" ;
211 ctx.fill();
212
213 canvas.onmousemove = function(event){
214 var x1 = event.offsetX;
215 var y1 = event.offsetY;
216 console.log('cor x:',' ',x,'cor y:',' ',y);
217 console.log('cor x1:',' ',x1,'cor y1:',' ',y1);
218 ctx.beginPath();
219 ctx.arc(x1, y1, R, 0, 2 * Math.PI, true);
220 ctx.fillStyle = "#ffffff" ;
221 ctx.fill();
222 ctx.closePath();
223
224 ctx.beginPath();
225 ctx.moveTo(x,y);
226 ctx.lineTo(x1,y1);
227 ctx.lineWidth = 2*R;
228 ctx.strokeStyle = "#ffffff";
229 ctx.stroke();
230 x = x1;
231 y = y1;
232 if(y1>599){
233 canvas.onmousemove = null;
234 }
235 if(x1<1){
236 canvas.onmousemove = null;
237 }
238 if(y1<1){
239 canvas.onmousemove = null;
240 }
241
242 if(x1>895){
243 canvas.onmousemove = null;
244 }
245
246
247 }
248
249 canvas.onmouseup = function(){
250 canvas.onmousemove = null;
251 }
252 }
253 }
254
255 /**
256 * ФУНКЦИЯ ДЛЯ ОТРИСОВКИ ОКРУЖНОСТИ.
257 * dataURL - получение ссылки на изображение холста.
258 * backgr - для сохранения изображения холста.
259 * ctx.drawImage(backgr,0,0,900,600); отрисовка холста на "предыдущем шаге".
260 * dataURL = canvas.toDataURL(); (в onmousup) сохраняем холст и ссылку на его изображение для следующего рисования.
261 */
262 function drawCircle(){
263
264 canvas.onmousedown = function(event){
265 var dataURL = canvas.toDataURL();
266 var backgr = new Image();
267 backgr.src = dataURL;
268 console.log('save parametrs of canvas');
269 var x = event.offsetX;
270 var y = event.offsetY;
271
272 canvas.onmousemove = function(event){
273 var x1 = event.offsetX;
274 var y1 = event.offsetY;
275 var r = Math.sqrt(Math.pow(x-x1,2)+Math.pow(y-y1,2));
276 console.log('cor x:',' ',x,'cor y:',' ',y);
277 console.log('cor x1:',' ',x1,'cor y1:',' ',y1);
278
279 ctx.beginPath();
280 ctx.clearRect(0,0,900,600);
281 ctx.drawImage(backgr,0,0,900,600);
282 ctx.arc(x, y, r, 0, 2 * Math.PI, true);
283 if (mode_flag){
284 ctx.fillStyle = myColor;
285 ctx.fill();
286 } else {
287 ctx.strokeStyle = myColor;
288 ctx.lineWidth = 2*R;
289 ctx.stroke();
290 }
291 if(y1>599){
292 canvas.onmousemove = null;
293 }
294 if(x1<1){
295 canvas.onmousemove = null;
296 }
297 if(y1<1){
298 canvas.onmousemove = null;
299 }
300
301 if(x1>895){
302 canvas.onmousemove = null;
303 }
304
305 }
306
307 canvas.onmouseup = function(){
308 dataURL = canvas.toDataURL();
309 backgr = new Image();
310 backgr.src = dataURL;
311 canvas.onmousemove = null;
312 }
313 }
314 }
315
316
317 /**
318 * ФУНКЦИЯ ДЛЯ РИСОВАНИЯ ПРЯМОУГОЛЬНИКА.
319 * Аналогична функции рисования окружности.
320 * Рисование окружности заменено на рисование прямоугольника.
321 */
322 function drawRect(){
323
324 canvas.onmousedown = function(event){
325 var dataURL = canvas.toDataURL();
326 var backgr = new Image();
327 backgr.src = dataURL;
328 console.log('save parametrs of canvas');
329 var x = event.offsetX;
330 var y = event.offsetY;
331
332 canvas.onmousemove = function(event){
333 var x1 = event.offsetX;
334 var y1 = event.offsetY;
335 var width = x1-x;
336 var height = y1-y;
337 ctx.beginPath();
338 ctx.clearRect(0,0,900,600);
339 ctx.drawImage(backgr,0,0,900,600);
340 if (mode_flag){
341 ctx.fillStyle = myColor;
342 ctx.fillRect(x, y, width, height);
343 } else {
344 ctx.strokeStyle = myColor;
345 ctx.lineWidth = 2*R;
346 ctx.strokeRect(x, y, width, height);
347 }
348 if(y1>599){
349 canvas.onmousemove = null;
350 }
351 if(x1<1){
352 canvas.onmousemove = null;
353 }
354 if(y1<1){
355 canvas.onmousemove = null;
356 }
357
358 if(x1>895){
359 canvas.onmousemove = null;
360 }
361 }
362
363 canvas.onmouseup = function(){
364 dataURL = canvas.toDataURL();
365 backgr = new Image();
366 backgr.src = dataURL;
367 canvas.onmousemove = null;
368 }
369 }
370 }
371
372 /**
373 * ФУНКЦИЯ ДЛЯ ОТРИСОВКИ ОВАЛА (ЭЛЛИПСА).
374 *
375 */
376 function drawEllips(){
377
378 canvas.onmousedown = function(event){
379 var dataURL = canvas.toDataURL();
380 var backgr = new Image();
381 backgr.src = dataURL;
382 console.log('save parametrs of canvas');
383 var x = event.offsetX;
384 var y = event.offsetY;
385
386 canvas.onmousemove = function(event){
387 var x1 = event.offsetX;
388 var y1 = event.offsetY;
389 var width = Math.abs(x1-x);
390 var height = Math.abs(y1-y);
391 ctx.beginPath();
392 ctx.clearRect(0,0,900,600);
393 ctx.drawImage(backgr,0,0,900,600);
394 ctx.ellipse(x, y, width, height, 0, 0, 2 * Math.PI, true);
395 if (mode_flag){
396 ctx.fillStyle = myColor;
397 ctx.fill();
398 } else {
399 ctx.strokeStyle = myColor;
400 ctx.lineWidth = 2*R;
401 ctx.stroke();
402 }
403 if(y1>599){
404 canvas.onmousemove = null;
405 }
406 if(x1<1){
407 canvas.onmousemove = null;
408 }
409 if(y1<1){
410 canvas.onmousemove = null;
411 }
412
413 if(x1>895){
414 canvas.onmousemove = null;
415 }
416 }
417
418 canvas.onmouseup = function(){
419 dataURL = canvas.toDataURL();
420 backgr = new Image();
421 backgr.src = dataURL;
422 canvas.onmousemove = null;
423 }
424 }
425 }
426
427 /**
428 * ФУНКЦИЯ ДЛЯ ОТРИСОВКИ КВАДРАТА.
429 * Аналогично функции для отрисовки прямоугольника+проверки
430 */
431 function drawSquare(){
432
433 canvas.onmousedown = function(event){
434 var dataURL = canvas.toDataURL();
435 var backgr = new Image();
436 backgr.src = dataURL;
437 console.log('save parametrs of canvas');
438 var x = event.offsetX;
439 var y = event.offsetY;
440
441 canvas.onmousemove = function(event){
442
443 var x1 = event.offsetX;
444 var y1 = event.offsetY;
445 var width = x1-x;
446 var height = y-y1;
447 if(y1>599){
448 canvas.onmousemove = null;
449 }
450 if(x1<1){
451 canvas.onmousemove = null;
452 }
453 if(y1<1){
454 canvas.onmousemove = null;
455 }
456
457 if(x1>890){
458 canvas.onmousemove = null;
459 }
460
461
462
463 if((y1-y<0) && (x1-x<0)){
464 ctx.beginPath();
465 ctx.clearRect(0,0,900,600);
466 ctx.drawImage(backgr,0,0,900,600);
467 if (mode_flag){
468 ctx.fillStyle = myColor;
469 ctx.fillRect(x, y, -Math.abs(width), -Math.abs(width));
470 } else {
471 ctx.strokeStyle = myColor;
472 ctx.lineWidth = 2*R;
473 ctx.strokeRect(x, y, -Math.abs(width), -Math.abs(width));
474 }
475 }
476
477 if((y1-y<0) && (x1-x>0)){
478 ctx.beginPath();
479 ctx.clearRect(0,0,900,600);
480 ctx.drawImage(backgr,0,0,900,600);
481 if (mode_flag){
482 ctx.fillStyle = myColor;
483 ctx.fillRect(x, y, Math.abs(width), -Math.abs(width));
484 } else {
485 ctx.strokeStyle = myColor;
486 ctx.lineWidth = 2*R;
487 ctx.strokeRect(x, y, Math.abs(width), -Math.abs(width));
488 }
489 }
490
491 if((y1-y>0) && (x1-x>0)){
492 ctx.beginPath();
493 ctx.clearRect(0,0,900,600);
494 ctx.drawImage(backgr,0,0,900,600);
495 if (mode_flag){
496 ctx.fillStyle = myColor;
497 ctx.fillRect(x, y, Math.abs(width), Math.abs(width));
498 } else {
499 ctx.strokeStyle = myColor;
500 ctx.lineWidth = 2*R;
501 ctx.strokeRect(x, y, Math.abs(width), Math.abs(width));
502 }
503 }
504
505 if((y1-y>0) && (x1-x<0)){
506 ctx.beginPath();
507 ctx.clearRect(0,0,900,600);
508 ctx.drawImage(backgr,0,0,900,600);
509
510 if (mode_flag){
511 ctx.fillStyle = myColor;
512 ctx.fillRect(x, y, -Math.abs(width), Math.abs(width));
513 } else {
514 ctx.strokeStyle = myColor;
515 ctx.lineWidth = 2*R;
516 ctx.strokeRect(x, y, -Math.abs(width), Math.abs(width));
517 }
518
519
520 }
521 }
522
523 canvas.onmouseup = function(){
524 dataURL = canvas.toDataURL();
525 backgr = new Image();
526 backgr.src = dataURL;
527 canvas.onmousemove = null;
528 }
529 }
530 }
531
532 /**
533 * ФУНКЦИЯ ДЛЯ РИСОВАНИЯ ЛОМАННОЙ.
534 * flag здесь нужен для рисования от последних координат.
535 */
536 function linedraw(){
537 flag = true;
538 var x;
539 var y;
540
541 canvas.onmousedown = function(event){
542 var dataURL = canvas.toDataURL();
543 var backgr = new Image();
544 backgr.src = dataURL;
545
546 if (flag){
547 x = event.offsetX;
548 y = event.offsetY;
549 console.log('x: ',x,'y: ',y);
550 } else {
551 var x1 = event.offsetX;
552 var y1 = event.offsetY;
553
554 console.log('x1: ',x1,'y1: ',y1);
555 ctx.beginPath();
556 ctx.moveTo(x,y);
557 ctx.lineTo(x1,y1);
558 ctx.lineWidth = 2*R;
559 ctx.strokeStyle = myColor;
560 ctx.stroke();
561 ctx.closePath();
562
563 ctx.beginPath();
564 ctx.arc(x, y, R, 0, 2 * Math.PI, true);
565 ctx.arc(x1, y1, R, 0, 2 * Math.PI, true);
566 ctx.fillStyle = myColor;
567 ctx.fill();
568 x = x1;
569 y = y1;
570 }
571 canvas.onmousemove = function(event){
572 var x1 = event.offsetX;
573 var y1 = event.offsetY;
574 console.log('x: ',x1,'y: ',y1);
575 ctx.beginPath();
576 ctx.clearRect(0,0,900,600);
577 ctx.drawImage(backgr,0,0,900,600);
578 ctx.moveTo(x,y);
579 ctx.lineTo(x1,y1);
580 ctx.lineWidth = 2*R;
581 ctx.strokeStyle = myColor;
582 ctx.stroke();
583 ctx.closePath();
584 ctx.beginPath();
585 ctx.arc(x, y, R, 0, 2 * Math.PI, true);
586 ctx.arc(x1, y1, R, 0, 2 * Math.PI, true);
587 ctx.fillStyle = myColor;
588 ctx.fill();
589
590 if(y1>599){
591 canvas.onmousemove = null;
592 }
593 if(x1<1){
594 canvas.onmousemove = null;
595 }
596 if(y1<1){
597 canvas.onmousemove = null;
598 }
599
600 if(x1>890){
601 canvas.onmousemove = null;
602 }
603 }
604
605 canvas.onmouseup = function(){
606
607 dataURL = canvas.toDataURL();
608 backgr = new Image();
609 backgr.src = dataURL;
610 canvas.onmousemove = null;
611 }
612 }
613 }
614
615 /**
616 * ФУНКЦИЯ ПИПЕТКА.
617 * При нажатии считывает с координат мышки цвет пикеля в формате rgb.
618 * Устанавливает считанный цвет пикселя в качестве текущего.
619 */
620 function pipette(){
621 canvas.onmousedown = function(event){
622 var x = event.offsetX;
623 var y = event.offsetY;
624 var pix_data = ctx.getImageData(x,y,1,1).data;
625 var rgb_base = 'rgb(' + pix_data[0] + ','+ pix_data[1] + ','+ pix_data[2] +')';
626 var r_rgb = pix_data[0];
627 var g_rgb = pix_data[1];
628 var b_rgb = pix_data[2];
629 myColor = 'rgb('+r_rgb+','+g_rgb+','+b_rgb+')';
630 console.log(rgb_base);
631 }
632 }
633
634 /**
635 * ФУНКЦИЯ ЗАЛИВКА.
636 */
637 function filling(){
638 canvas.onmousedown = function(event){
639 var x = event.offsetX;
640 var y = event.offsetY;
641 var pix_data = ctx.getImageData(x,y,1,1).data;
642
643 function rgb_hex(r, g, b){
644 return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
645 }
646
647 var boardColor = rgb_hex(pix_data[0], pix_data[1], pix_data[2]);
648 var stack = [[x,y]];
649 var pixel;
650 ctx.fillStyle = myColor;
651
652 while (stack.length > 0){
653 pixel = stack.pop();
654 if (pixel[0] < 0 || pixel[0] >= 900){
655 continue;}
656 if (pixel[1] < 0 || pixel[1] >= 600){
657 continue;}
658
659 pix_data = ctx.getImageData(pixel[0], pixel[1], 1,1).data;
660 pixColor = rgb_hex(pix_data[0], pix_data[1], pix_data[2]);
661
662 if (pixColor == boardColor && pixColor !== myColor){
663 ctx.fillRect(pixel[0]-1,pixel[1]-1,3,3);
664
665 stack.push([
666 pixel[0] -2,
667 pixel[1]
668 ]);
669 stack.push([
670 pixel[0] +2,
671 pixel[1]
672 ]);
673 stack.push([
674 pixel[0],
675 pixel[1] -2
676 ]);
677 stack.push([
678 pixel[0],
679 pixel[1] +2
680 ]);
681 }
682 }
683 }
684
685 }