2020年5月17日-利用canvas标签做的白板

利用html5的canvas标签,做了一个网页白板。

效果图:

体验地址:

//-------------------------------------

//请复制代码到本地保存为html文件运行

源码:

  1 <!DOCTYPE html>
  2 <html>
  3     <head>
  4         <meta charset="UTF-8">
  5         <title>Canvas画板</title>
  6         <style>
  7             body{
  8                 background-color: #D1D1D1;
  9             }
 10             
 11             .option{
 12                 margin: 7px;
 13                 margin-left: auto;
 14                 margin-right: auto;
 15             }
 16         
 17             #input_transparency{
 18                 height: 10px;
 19                 width: 100px;
 20             }
 21         
 22             #input_line_width{
 23                 width: 100px;
 24             }
 25             
 26             #left{
 27                 margin-right: 40px;
 28                 float: left;
 29                 width: 240px;
 30                 background-color: pink;
 31                 border-right: 2px solid black;
 32             }
 33             
 34             #right{
 35                 float: left;
 36             }
 37             
 38             #bottom{
 39                 padding-top: 10px;
 40                 border-top: 2px solid black;
 41                 clear: left;
 42             }
 43             
 44             #my_canvas{
 45                 width: 900px;
 46                 height: 600px;
 47                 border: 1px solid black;
 48                 background-color: white;
 49             }
 50             
 51             .tool {
 52                 display: block;
 53                 border-radius: 8px;
 54                 background-color: #3e8e41;
 55                 border: none;
 56                 color: #FFFFFF;
 57                 text-align: center;
 58                 font-size: 14px;
 59                 padding: 20px;
 60                 width: 100px;
 61                 transition: 0.5s;
 62                 cursor: pointer;
 63                 margin: 5px;
 64                 margin-left: auto;
 65                 margin-right: auto;
 66             }
 67 
 68             .tool:hover {
 69                 background-color: #dd8e41;
 70                 transition: 0.1s;
 71             }
 72             
 73             .tool:active {
 74                 background-color: #f4511e;
 75                 box-shadow: 0 5px #666;
 76                 transform: scale(0.9,0.9);
 77                 transition: 0.1s;
 78             }
 79             
 80             .state{
 81                 float: left;
 82                 width: 6%;
 83                 margin-right: 18%;
 84             }
 85             
 86             #div_text_to_draw{
 87                 width: auto;
 88                 clear: both;
 89                 display: none;
 90             }
 91         </style>
 92         
 93     </head>
 94     
 95     <body>
 96         <div id="left">
 97             <div id="toolbar">
 98                 工具栏:
 99                 <br>
100                 <button class="tool" id="pencil">
101                     <span>铅笔</span>
102                 </button>
103                 <button class="tool" id="line">
104                     <span>直线</span>
105                 </button>
106                 <button class="tool" id="rect">
107                     <span>矩形</span>
108                 </button>            
109                 <button class="tool" id="oval">
110                     <span>椭圆</span>
111                 </button>
112                 <button class="tool" id="text">
113                     <span>文本</span>
114                 </button>
115                 <button class="tool" id="eraser">
116                     <span>橡皮</span>
117                 </button>
118                 <button class="tool" id="undo">
119                     <span>撤销</span>
120                 </button>
121                 <button class="tool" id="clear">
122                     <span>清空</span>
123                 </button>
124             </div>
125             <hr>
126             <div id="selection_area">
127                 选项栏:
128                 <br>
129                 <div class="option" id="line_width">
130                     线宽:
131                     <input id="input_line_width" type="text" value="5">
132                     px
133                 </div>
134                 <div class="option" id="color">
135                     颜色:
136                     <input id="input_color" type="color">
137                 </div>
138                 <div class="option" id="fill">
139                     填充:&nbsp;&nbsp;
140                     <input id="input_fill" type="radio" value="fill" name="fill">填充
141                     <input type="radio" value="not_fill" name="fill" checked>不填充
142                 </div>
143                 <div class="option" id="transparency">
144                     透明:&nbsp;&nbsp;
145                     透明<input id="input_transparency" type="range" name="transparency" min="0" max="1" value="0.8" step="0.01">不透明
146                 </div>
147             </div>
148         </div>
149         
150         <div id="right">
151             <div id="canvas_area">
152                 画板区域:
153                 <br>
154                 <br>
155                 <canvas id="my_canvas" width="900" height="600">
156                     若看到此文本,说明您的浏览器不支持canvas标签,请升级您的浏览器,推荐使用chrome。
157                 </canvas>
158             </div>
159         </div>
160         
161         <div id="bottom">
162             <div id="state_area">
163                 状态栏:
164                 <br>
165                 <div class="state">
166                     模式:&nbsp;<span id="mode"></span>
167                 </div>
168                 <div class="state">
169                     x:&nbsp;&nbsp;<span id="x"></span>
170                 </div>
171                 <div class="state">
172                     y:&nbsp;&nbsp;<span id="y"></span>
173                 </div>
174                 <div class="state"  id="div_text_to_draw">
175                     文本:&nbsp;<span id="text_to_draw"></span>
176                 </div>
177             </div>
178         </div>
179     </body>
180     <script>
181         //这个标签内的脚本使用严格模式
182         "use strict";
183 
184         //获得canvas对象
185         var canvas = document.getElementById('my_canvas');
186         //获得 2d 上下文对象
187         var ctx = canvas.getContext('2d');
188         
189         //保存原始style防止被下面覆写丢失
190         var original_tools_styles = {
191             "pencil":Object(document.getElementById('pencil').style), 
192             "line":Object(document.getElementById('line').style), 
193             "rect":Object(document.getElementById('rect').style), 
194             "oval":Object(document.getElementById('oval').style), 
195             "text":Object(document.getElementById('text').style), 
196             "eraser":Object(document.getElementById('eraser').style), 
197             "undo":Object(document.getElementById('undo').style),
198             "clear":Object(document.getElementById('clear').style),
199         }
200     
201         //方便获取各个元素
202         var tools = {
203             "pencil":document.getElementById('pencil'), 
204             "line":document.getElementById('line'), 
205             "rect":document.getElementById('rect'), 
206             "oval":document.getElementById('oval'), 
207             "text":document.getElementById('text'), 
208             "eraser":document.getElementById('eraser'),
209             "undo":document.getElementById('undo'),             
210             "clear":document.getElementById('clear'),
211         }
212         
213         //用于记录当前模式
214         var mode = null;
215         //用于记录鼠标是否按下
216         var mouse_is_down = false;
217         
218         //保存起点坐标
219         var start_x = 0;
220         var start_y = 0;
221             
222         //保存之前的canvas状态
223         var old_canvas_data = null;
224         
225         //保存要绘制的文本
226         var text_to_draw = null;
227         
228         //用于点亮对应按钮,参数传null则不点亮
229         function light_button(element){
230             for(let button in tools){
231                 tools[button].style = original_tools_styles[button];
232             }
233             if(element){
234                 tools[element].style.backgroundColor = "#dd8e41";
235             }
236             //状态栏中不显示文本
237             document.getElementById("div_text_to_draw").style.display = "none";
238         }
239         
240         //获取线宽,返回整数值
241         function get_line_width(){
242             return parseInt(document.getElementById('input_line_width').value);
243         }
244         
245         //获取颜色,返回css的颜色代码字符串
246         function get_color(){
247             return document.getElementById('input_color').value;
248         }
249         
250         //获取透明度,返回浮点数
251         function get_transparency(){
252             return Math.round(document.getElementById('input_transparency').value*100)/100;
253         }
254         
255         //获取是否填充,返回布尔值
256         function get_fill_or_not(){
257             return document.getElementById('input_fill').checked;
258         }
259         
260         //铅笔
261         tools["pencil"].onclick = function () {
262             light_button('pencil');
263             mode = "pencil";
264         };
265         
266         //直线
267         tools["line"].onclick = function () {
268             light_button('line');
269             mode = "line";
270         };
271         
272         //矩形
273         tools["rect"].onclick = function () {
274             light_button('rect');
275             mode = "rect";
276         };
277         
278         //椭圆
279         tools["oval"].onclick = function () {
280             light_button('oval');
281             mode = "oval";
282         };
283         
284         //文本
285         tools["text"].onclick = function () {
286             let prompt_result = prompt("请输入文本:","Hello, world!");
287 
288             text_to_draw = prompt_result;
289             
290             light_button('text');
291             mode = "text";
292             
293             //状态栏中显示文本
294             document.getElementById("div_text_to_draw").style.display = "inline";
295         };
296         
297         //橡皮
298         tools["eraser"].onclick = function () {
299             light_button('eraser');
300             mode = "eraser";
301         };
302         
303         //撤销
304         tools["undo"].onclick = function () {
305             light_button(null);
306             ctx.putImageData(old_canvas_data,0,0);
307             mode = null;
308         };
309         
310         //清除
311         tools["clear"].onclick = function () {
312             let confirm_result = confirm("确定要清除吗?");
313             
314             if(!confirm_result){
315                 return;
316             }
317             
318             light_button(null);
319             
320             //保存本次操作前的canvas状态
321             old_canvas_data = ctx.getImageData(0,0,900,600);
322             
323             ctx.clearRect(0,0,900,600);
324             mode = null;
325         };
326         
327         canvas.onmousedown = function (evt) {
328             mouse_is_down = true;
329             //获取画布内坐标作为起点,通过加减元素坐标和鼠标坐标得到
330             start_x = evt.clientX - canvas.getBoundingClientRect().left + canvas.scrollLeft;
331             start_y = evt.clientY - canvas.getBoundingClientRect().top + canvas.scrollTop;
332             
333             //保存本次操作前的canvas状态
334             old_canvas_data = ctx.getImageData(0,0,900,600);
335         }
336     
337         canvas.onmouseup = function () {
338             mouse_is_down = false;
339         }
340         
341         canvas.onmousemove = function(evt){
342             //获取选项
343             var red = parseInt(get_color()[1]+get_color()[2],16);
344             var green = parseInt(get_color()[3]+get_color()[4],16);
345             var blue = parseInt(get_color()[5]+get_color()[6],16);
346             var will_fill = get_fill_or_not();
347             
348             //设置样式
349             ctx.lineWidth = get_line_width();
350             ctx.fillStyle = "rgba("+red+", "+green+", "+blue+", "+get_transparency()+")";
351             ctx.strokeStyle = "rgba("+red+", "+green+", "+blue+", "+get_transparency()+")";
352             ctx.font = get_line_width()*4+"px Georgia";
353             
354             //获取画布内当前坐标作为新坐标,通过加减元素坐标和鼠标坐标得到
355             let x = evt.clientX - canvas.getBoundingClientRect().left + canvas.scrollLeft;
356             let y = evt.clientY - canvas.getBoundingClientRect().top + canvas.scrollTop;
357 
358             //填写状态栏
359             if(mode){
360                 document.getElementById("mode").innerHTML = mode;
361             }else{
362                 document.getElementById("mode").innerHTML = "";
363             }
364             document.getElementById("x").innerHTML = Math.round(x);
365             document.getElementById("y").innerHTML = Math.round(y);
366             document.getElementById("text_to_draw").innerHTML = text_to_draw;
367             
368             //若鼠标未按下则不执行具体的绘制
369             if(!mouse_is_down){
370                 return;
371             }
372 
373             if(mode === "pencil"){    
374                 //开始绘制
375                 ctx.beginPath();
376                 ctx.moveTo(start_x, start_y);
377                 ctx.lineTo(x,y);
378                 ctx.stroke();
379                 ctx.closePath();
380                 
381                 //读取新坐标
382                 start_x = x;
383                 start_y = y;
384             }else if(mode === "line"){
385                 //恢复到原来
386                 ctx.putImageData(old_canvas_data,0,0);
387                 
388                 //开始绘制
389                 ctx.beginPath();
390                 ctx.moveTo(start_x, start_y);
391                 ctx.lineTo(x,y);
392                 ctx.stroke();
393                 ctx.closePath();
394             }else if(mode === "rect"){
395                 //恢复到原来
396                 ctx.putImageData(old_canvas_data,0,0);
397                 
398                 //开始绘制
399                 if(will_fill){
400                     ctx.fillRect(start_x, start_y, x - start_x, y - start_y);
401                 }else{
402                     ctx.strokeRect(start_x, start_y, x - start_x, y - start_y);
403                 }
404             }else if(mode === "oval"){
405                 //恢复到原来
406                 ctx.putImageData(old_canvas_data,0,0);
407                 
408                 //开始绘制
409                 //利用两段bezier曲线实现
410                 //曲线段1
411                 ctx.beginPath();
412                 ctx.moveTo(start_x, (y + start_y) / 2);
413                 ctx.bezierCurveTo(start_x, start_y, x, start_y, x, (y + start_y) / 2);
414                 //曲线段2
415                 ctx.moveTo(start_x, (y + start_y) / 2);
416                 ctx.bezierCurveTo(start_x, y, x, y, x, (y + start_y) / 2);
417                 //注意要手动把终点移回起点,否则调用closePath会自动画一条连接起点与终点的横线
418                 ctx.moveTo(start_x, (y + start_y) / 2);
419                 ctx.closePath();
420                 
421                 if(will_fill){
422                     ctx.fill();
423                 }else{
424                     ctx.stroke();
425                 }
426             }else if(mode === "text"){
427                 //恢复到原来
428                 ctx.putImageData(old_canvas_data,0,0);
429 
430                 if(will_fill){
431                     ctx.fillText(text_to_draw, x, y);
432                 }else{
433                     //注意,strokeText的线宽通过lineWidth来设置,默认为5
434                     //此处必须重设为1,否则太宽了文本不成型
435                     ctx.lineWidth = 1;
436                     ctx.strokeText(text_to_draw, x, y);
437                 }
438             }else if(mode === "eraser"){                
439                 //设置橡皮的ctx状态
440                 ctx.lineWidth = get_line_width()*4;
441                 ctx.fillStyle = "rgba(255,255,255,1)";
442                 ctx.strokeStyle = "rgba(255,255,255,1)";
443                 
444                 //开始绘制
445                 ctx.beginPath();
446                 ctx.moveTo(start_x, start_y);
447                 ctx.lineTo(x,y);
448                 ctx.stroke();
449                 ctx.closePath();
450                 
451                 //读取新坐标
452                 start_x = x;
453                 start_y = y;
454             }
455         }
456     </script>
457 </html>

之后要是有空,会加上复制粘贴剪切,保存文件载入文件等功能。

(没空就鸽了。)

欢迎转载,转载请注明出处。

猜你喜欢

转载自www.cnblogs.com/IsuxizWorld/p/12903434.html