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 填充: 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 透明: 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 模式: <span id="mode"></span> 167 </div> 168 <div class="state"> 169 x: <span id="x"></span> 170 </div> 171 <div class="state"> 172 y: <span id="y"></span> 173 </div> 174 <div class="state" id="div_text_to_draw"> 175 文本: <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>
之后要是有空,会加上复制粘贴剪切,保存文件载入文件等功能。
(没空就鸽了。)
欢迎转载,转载请注明出处。