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