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 }