1+ <template >
2+   <div  class =" canvas-container"  >
3+     <canvas  ref =" canvas"   :width =" width"   :height =" height"   @mousedown =" startDrawing"   @mousemove =" draw"   @mouseup =" stopDrawing"   @mouseleave =" stopDrawing"  ></canvas >
4+     <div  class =" canvas-controls"  >
5+       <button  @click =" clearCanvas"   class =" btn btn-secondary"  >Clear</button >
6+       <input  type =" color"   v-model =" currentColor"   title =" Choose color"  >
7+       <input  type =" range"   v-model =" lineWidth"   min =" 1"   max =" 20"   title =" Line width"  >
8+     </div >
9+   </div >
10+ </template >
11+ 
12+ <script >
13+ export  default  {
14+   name:  ' CanvasInput'  , 
15+   props:  { 
16+     value:  { 
17+       type:  String , 
18+       default:  ' '  
19+     }, 
20+     width:  { 
21+       type:  Number , 
22+       default:  800  
23+     }, 
24+     height:  { 
25+       type:  Number , 
26+       default:  400  
27+     }, 
28+     backgroundImage:  { 
29+       type:  String , 
30+       default:  ' '  
31+     } 
32+   }, 
33+   data () { 
34+     return  { 
35+       canvas:  null , 
36+       ctx:  null , 
37+       isDrawing:  false , 
38+       currentColor:  ' #000000'  , 
39+       lineWidth:  5 , 
40+       lastX:  0 , 
41+       lastY:  0 , 
42+       image:  null  
43+     } 
44+   }, 
45+   mounted () { 
46+     this .canvas  =  this .$refs .canvas ; 
47+     this .ctx  =  this .canvas .getContext (' 2d'  ); 
48+     this .ctx .lineCap  =  ' round'  ; 
49+     this .ctx .lineJoin  =  ' round'  ; 
50+      
51+     //  Load background image if provided 
52+     if  (this .backgroundImage ) { 
53+       this .image  =  new  Image (); 
54+       this .image .onload  =  () =>  { 
55+         //  Draw the image maintaining aspect ratio 
56+         const  scale  =  Math .min ( 
57+           this .width  /  this .image .width , 
58+           this .height  /  this .image .height  
59+         ); 
60+         const  x  =  (this .width  -  this .image .width  *  scale) /  2 ; 
61+         const  y  =  (this .height  -  this .image .height  *  scale) /  2 ; 
62+         this .ctx .drawImage ( 
63+           this .image , 
64+           x, y, 
65+           this .image .width  *  scale, 
66+           this .image .height  *  scale 
67+         ); 
68+       }; 
69+       this .image .src  =  this .backgroundImage ; 
70+     } 
71+ 
72+     //  Load existing drawing if value exists 
73+     if  (this .value ) { 
74+       const  img  =  new  Image (); 
75+       img .onload  =  () =>  { 
76+         this .ctx .drawImage (img, 0 , 0 ); 
77+       }; 
78+       img .src  =  this .value ; 
79+     } 
80+   }, 
81+   methods:  { 
82+     startDrawing (event ) { 
83+       this .isDrawing  =  true ; 
84+       const  rect  =  this .canvas .getBoundingClientRect (); 
85+       this .lastX  =  event .clientX  -  rect .left ; 
86+       this .lastY  =  event .clientY  -  rect .top ; 
87+     }, 
88+     draw (event ) { 
89+       if  (! this .isDrawing ) return ; 
90+        
91+       const  rect  =  this .canvas .getBoundingClientRect (); 
92+       const  currentX  =  event .clientX  -  rect .left ; 
93+       const  currentY  =  event .clientY  -  rect .top ; 
94+        
95+       this .ctx .beginPath (); 
96+       this .ctx .strokeStyle  =  this .currentColor ; 
97+       this .ctx .lineWidth  =  this .lineWidth ; 
98+       this .ctx .moveTo (this .lastX , this .lastY ); 
99+       this .ctx .lineTo (currentX, currentY); 
100+       this .ctx .stroke (); 
101+        
102+       this .lastX  =  currentX; 
103+       this .lastY  =  currentY; 
104+        
105+       this .$emit (' input'  , this .canvas .toDataURL ()); 
106+     }, 
107+     stopDrawing () { 
108+       this .isDrawing  =  false ; 
109+     }, 
110+     clearCanvas () { 
111+       this .ctx .clearRect (0 , 0 , this .width , this .height ); 
112+       //  Redraw background image if it exists 
113+       if  (this .image ) { 
114+         const  scale  =  Math .min ( 
115+           this .width  /  this .image .width , 
116+           this .height  /  this .image .height  
117+         ); 
118+         const  x  =  (this .width  -  this .image .width  *  scale) /  2 ; 
119+         const  y  =  (this .height  -  this .image .height  *  scale) /  2 ; 
120+         this .ctx .drawImage ( 
121+           this .image , 
122+           x, y, 
123+           this .image .width  *  scale, 
124+           this .image .height  *  scale 
125+         ); 
126+       } 
127+       this .$emit (' input'  , this .canvas .toDataURL ()); 
128+     } 
129+   } 
130+ } 
131+  </script >
132+ 
133+ <style  scoped>
134+ .canvas-container  {
135+   display  : flex ; 
136+   flex-direction  : column ; 
137+   align-items  : center ; 
138+   gap  : 1rem  ; 
139+   margin  : 1rem   0 ; 
140+ } 
141+ 
142+ canvas  {
143+   border  : 1px   solid  #ccc ; 
144+   background  : white ; 
145+   cursor  : crosshair ; 
146+ } 
147+ 
148+ .canvas-controls  {
149+   display  : flex ; 
150+   gap  : 1rem  ; 
151+   align-items  : center ; 
152+ } 
153+ 
154+ input [type = " color"  ] {
155+   width  : 50px  ; 
156+   height  : 30px  ; 
157+   padding  : 0 ; 
158+   border  : none ; 
159+   border-radius  : 4px  ; 
160+   cursor  : pointer ; 
161+ } 
162+ 
163+ input [type = " range"  ] {
164+   width  : 100px  ; 
165+ } 
166+ 
167+ .btn  {
168+   padding  : 0.5rem   1rem  ; 
169+   border  : none ; 
170+   border-radius  : 4px  ; 
171+   cursor  : pointer ; 
172+   font-size  : 0.9rem  ; 
173+ } 
174+ 
175+ .btn-secondary  {
176+   background-color  : #6c757d ; 
177+   color  : white ; 
178+ } 
179+ 
180+ .btn-secondary :hover  {
181+   background-color  : #5a6268 ; 
182+ } 
183+  </style > 
0 commit comments