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