[js高手之路]html5 canvas动画教程 - 自己动手做一个类似windows的画图软件
Posted ghostwu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[js高手之路]html5 canvas动画教程 - 自己动手做一个类似windows的画图软件相关的知识,希望对你有一定的参考价值。
这个绘图工具,我还没有做完,不过已经实现了总架构,以及常见的简易图形绘制功能:
1,可以绘制直线,圆,矩形,正多边形【已完成】
2,填充颜色和描边颜色的选择【已完成】
3,描边和填充功能的选择【已完成】
后续版本:
橡皮擦,坐标系,线形设置,箭头,其他流程图形,裁剪与调整图形。。。。。
终极目标:
流程绘制软件
我是之前看见一位朋友在我的博客中留言说:
非常感谢这个朋友,今天终于抽出时间完成非常非常小的雏形!
完整的雏形代码,请自行打开,复制到本地测试.
1 <head> 2 <meta charset="UTF-8"> 3 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 4 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 5 <title>windows简易画图工具 - by ghostwu</title> 6 </head> 7 8 <body> 9 <div class="paint"> 10 <div class="paint-header"> 11 <ul> 12 <li class="active">形状</li> 13 <li>颜色</li> 14 <li>绘制类型</li> 15 <li>线条宽度</li> 16 <li>橡皮擦</li> 17 </ul> 18 </div> 19 <div class="paint-body"> 20 <div class="siderbar"> 21 <div class="item active" data-type="paint-shape"> 22 <ul> 23 <li class="active" data-role="line">线条</li> 24 <li data-role="circle">圆形</li> 25 <li data-role="rect">矩形</li> 26 <li data-role="polygon">正多边形</li> 27 <li data-role="arrow">箭头</li> 28 </ul> 29 </div> 30 <div class="item" data-type="paint-color"> 31 <ul> 32 <li data-role="strokeStyle"> 33 <input type="color" data-role="strokeStyle"> 34 </li> 35 <li data-role="fillStyle"> 36 <input type="color" data-role="fillStyle"> 37 </li> 38 </ul> 39 </div> 40 <div class="item" data-type="paint-type"> 41 <ul> 42 <li data-role="stroke">描边</li> 43 <li data-role="fill">填充</li> 44 </ul> 45 </div> 46 <div class="item" data-type="paint-line"> 47 <ul> 48 <li data-role="1">小号</li> 49 <li data-role="4">中号</li> 50 <li data-role="7">大号</li> 51 <li> 52 <input type="number" data-role="line-size" placeholder="请输入数字"> 53 </li> 54 </ul> 55 </div> 56 <div class="item" data-type="paint-erase"> 57 <ul> 58 <li> 59 <input type="number" data-role="erase-size" placeholder="请输入数字"> 60 </li> 61 </ul> 62 </div> 63 </div> 64 </div> 65 </div> 66 <script>// <![CDATA[ 67 var oPaintBody = document.querySelector( \'.paint-body\' ); 68 var oC = document.createElement( \'canvas\' ); 69 oC.setAttribute( \'width\', \'830\' ); 70 oC.setAttribute( \'height\', \'500\' ); 71 oPaintBody.appendChild( oC ); 72 var aHeaderLi = document.querySelectorAll(\'.paint-header li\'), 73 aItem = document.querySelectorAll(\'.paint-body .item\'), 74 oCanvas = document.querySelector(\'.paint canvas\'), 75 oGc = oCanvas.getContext(\'2d\'), 76 cWidth = oCanvas.width, cHeight = oCanvas.height, 77 curItem = aItem[0], 78 aItemLi = curItem.querySelectorAll(\'li\'); 79 80 for (let i = 0, len = aHeaderLi.length; i < len; i++) { //头部选项卡切换功能 81 aHeaderLi[i].onclick = function () { 82 for (let j = 0; j < len; j++) { 83 aHeaderLi[j].classList.remove(\'active\'); 84 aItem[j].style.display = \'none\'; 85 } 86 aItem[i].style.display = "block"; 87 this.classList.add(\'active\'); 88 curItem = aItem[i]; 89 aItemLi = curItem.querySelectorAll(\'li\'); 90 activeItem(aItemLi); 91 } 92 } 93 activeItem(aItemLi); 94 var role = null; 95 function activeItem(aItemLi) { //canvas左侧选项卡切换功能 96 for (let i = 0, len = aItemLi.length; i < len; i++) { 97 aItemLi[i].onclick = function () { 98 checkPaintType(this); //绘制类型 99 for (let j = 0; j < len; j++) { 100 aItemLi[j].classList.remove(\'active\'); 101 } 102 this.classList.add(\'active\'); 103 } 104 } 105 } 106 107 function Shape(canvasObj, cxtObj, w, h) { 108 this.oCanvas = canvasObj; 109 this.oGc = cxtObj; 110 this.oCanvas.width = w; 111 this.oCanvas.height = h; 112 this.fillStyle = \'#000\'; 113 this.storkeStyle = \'#000\'; 114 this.lineWidth = 1; 115 this.drawType = \'line\'; 116 this.paintType = \'stroke\'; 117 this.nums = 6; //正多边形的边数 118 } 119 120 Shape.prototype = { 121 init: function () { 122 this.oGc.fillStyle = this.fillStyle; 123 this.oGc.strokeStyle = this.strokeStyle; 124 this.oGc.lineWidth = this.lineWidth; 125 }, 126 draw: function () { 127 var _this = this; 128 this.oCanvas.onmousedown = function (ev) { 129 _this.init(); 130 var oEvent = ev || event, 131 startX = oEvent.clientX - _this.oCanvas.offsetLeft, 132 startY = oEvent.clientY - _this.oCanvas.offsetTop; 133 _this.oCanvas.onmousemove = function (ev) { 134 _this.oGc.clearRect(0, 0, _this.oCanvas.width, _this.oCanvas.height); 135 var oEvent = ev || event, 136 endX = oEvent.clientX - _this.oCanvas.offsetLeft, 137 endY = oEvent.clientY - _this.oCanvas.offsetTop; 138 _this[_this.drawType](startX, startY, endX, endY); 139 }; 140 _this.oCanvas.onmouseup = function () { 141 _this.oCanvas.onmousemove = null; 142 _this.oCanvas.onmouseup = null; 143 } 144 } 145 }, 146 line: function (x1, y1, x2, y2) { 147 this.oGc.beginPath(); 148 this.oGc.moveTo(x1, y1); 149 this.oGc.lineTo(x2, y2); 150 this.oGc.closePath(); 151 this.oGc.stroke(); 152 }, 153 circle: function (x1, y1, x2, y2) { 154 this.oGc.beginPath(); 155 var r = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); 156 this.oGc.arc(x1, y1, r, 0, 2 * Math.PI, false); 157 this.oGc.closePath(); 158 this.oGc[this.paintType](); 159 }, 160 rect: function (x1, y1, x2, y2) { 161 this.oGc.beginPath(); 162 this.oGc.rect(x1, y1, x2 - x1, y2 - y1); 163 this.oGc[this.paintType](); 164 }, 165 polygon: function (x1, y1, x2, y2) { 166 var angle = 360 / this.nums * Math.PI / 180;//边对应的角的弧度 167 var r = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); 168 this.oGc.beginPath(); 169 for (var i = 0; i < this.nums; i++) { 170 this.oGc.lineTo(x1 + r * Math.cos(angle * i), y1 + r * Math.sin(angle * i)); 171 } 172 this.oGc.closePath(); 173 this.oGc[this.paintType](); 174 } 175 } 176 177 var oShape = new Shape(oCanvas, oGc, cWidth, cHeight); 178 function checkPaintType(liType) { 179 var dataType = liType.parentNode.parentNode.dataset.type; 180 var curType = liType.dataset.role; 181 switch (dataType) { 182 case \'paint-shape\': //形状 183 oShape.drawType = curType; 184 if (curType == \'polygon\') { 185 oShape.nums = prompt("请输入边数", 6); 186 } 187 oShape.draw(); 188 break; 189 case \'paint-color\': //绘制颜色 190 liType.children[0].onchange = function () { 191 oShape[this.dataset.role] = this.value; 192 } 193 oShape.draw(); 194 break; 195 case \'paint-type\': //绘制类型 196 oShape.paintType = curType; 197 oShape.draw(); 198 break; 199 } 200 } 201 // ]]></script> 202 <style> 203 .paint * { 204 margin: 0; 205 padding: 0; 206 } 207 208 .paint ul, 209 .paint li { 210 list-style: none; 211 } 212 213 .paint li:hover { 214 cursor: pointer; 215 } 216 217 .paint { 218 width: 980px; 219 margin: 20px auto; 220 border: 1px solid #ccc; 221 overflow: hidden; 222 } 223 224 .paint .paint-header ul { 225 width: 980px; 226 height: 40px; 227 line-height: 40px; 228 border-bottom: 1px solid #ccc; 229 } 230 231 .paint .paint-header li { 232 float: left; 233 width: 120px; 234 height: 40px; 235 line-height: 40px; 236 text-align: center; 237 } 238 239 .paint li.active { 240 box-shadow: #666 0px 1px 8px inset; 241 } 242 243 .paint .paint-body .siderbar { 244 float: left; 245 width: 150px; 246 height: 500px; 247 } 248 249 .paint .paint-body .item { 250 width: 150px; 251 overflow: hidden; 252 display: none; 253 height: 500px; 254 border-right: 1px solid #ccc; 255 } 256 257 .paint .paint-body canvas { 258 float: right; 259 } 260 261 .paint .paint-body .item li { 262 height: 40px; 263 text-align: center; 264 border-bottom: 1px solid #ccc; 265 line-height: 40px; 266 } 267 268 .paint .paint-body .active { 269 display: block; 270 } 271 </style> 272 </body>
关于流程设计,后期要做的功能,思路基本上已经有了,好了,圆规正传,想要完成这个终极目标,完成一个画图工具应该就能接近目标了。先体验下目前的简易功能,下面是可以正常画图的,【需要你的浏览器支持canvas才可以额】
- 形状
- 颜色
- 绘制类型
- 线条宽度
- 橡皮擦
主要来讲下目标的雏形架构:
1,图形绘制部分,我封装了一个类Shape
1 function Shape(canvasObj, cxtObj, w, h) { 2 this.oCanvas = canvasObj; 3 this.oGc = cxtObj; 4 this.oCanvas.width = w; 5 this.oCanvas.height = h; 6 this.fillStyle = \'#000\'; 7 this.storkeStyle = \'#000\'; 8 this.lineWidth = 1; 9 this.drawType = \'line\'; 10 this.paintType = \'stroke\'; 11 this.nums = 6; //正多边形的边数 12 }
canvasObj: 就是canvas画布对象
cxtObj: 就是上下文绘图环境
w: canvas的宽度
h: canvas的高度
fillStyle: 填充颜色
strokeStyle: 描边颜色
lineWidth: 线宽
drawType: 默认为画直线
paintType: stroke/fill 两种选择( 描边/填充)
2,在原型对象上扩展一个公共方法draw用来绘制图形
draw方法,主要获取起始点坐标(startX, startY),以及终点坐标( endX, endY );
然后调用init方法来获取绘制状态,绘制具体的图形靠下面这个关键方法:
_this[_this.drawType](startX, startY, endX, endY)
这个方法的drawType会根据界面的实时选择,变换对应的绘制类型,如:
_this[\'line\']( startX, startY, endX, endY )
调用的就是oShape对象中的line,画直线的方法
1 Shape.prototype = { 2 init: function () { 3 this.oGc.fillStyle = this.fillStyle; 4 this.oGc.strokeStyle = this.strokeStyle; 5 this.oGc.lineWidth = this.lineWidth; 6 }, 7 draw: function () { 8 var _this = this; 9 this.oCanvas.onmousedown = function ( ev ) { 10 _this.init(); 11 var oEvent = ev || event, 12 startX = oEvent.clientX - _this.oCanvas.offsetLeft, 13 startY = oEvent.clientY - _this.oCanvas.offsetTop; 14 _this.oCanvas.onmousemove = function ( ev ) { 15 _this.oGc.clearRect( 0, 0, _this.oCanvas.width, _this.oCanvas.height ); 16 var oEvent = ev || event, 17 endX = oEvent.clientX - _this.oCanvas.offsetLeft, 18 endY = oEvent.clientY - _this.oCanvas.offsetTop; 19 _this[_this.drawType](startX, startY, endX, endY); 20 }; 21 _this.oCanvas.onmouseup = function(){ 22 _this.oCanvas.onmousemove = null; 23 _this.oCanvas.onmouseup = null; 24 } 25 } 26 }, 27 line: function ( x1, y1, x2, y2 ) { 28 this.oGc.beginPath(); 29 this.oGc.moveTo( x1, y1 ); 30 this.oGc.lineTo( x2, y2 ); 31 this.oGc.closePath(); 32 this.oGc.stroke(); 33 }, 34 circle : function( x1, y1, x2, y2 ){ 35 this.oGc.beginPath(); 36 var r = Math.sqrt( Math.pow( x2 - x1, 2 ) + Math.pow( y2 - y1, 2 ) ); 37 this.oGc.arc( x1, y1, r, 0, 2 * Math.PI, false ); 38 this.oGc.closePath(); 39 this.oGc[this.paintType](); 40 }, 41 rect : function( x1, y1, x2, y2 ){ 42 this.oGc.beginPath(); 43 this.oGc.rect( x1, y1, x2 - x1, y2 - y1 ); 44 this.oGc[this.paintType](); 45 }, 46 polygon : function( x1, y1, x2, y2 ){ 47 var angle = 360 / this.nums * Math.PI / 180;//边对应的角的弧度 48 var r = Math.sqrt( Math.pow( x2 - x1, 2 ) + Math.pow( y2 - y1, 2 ) ); 49 this.oGc.beginPath(); 50 for( var i = 0; i < this.nums; i ++ ){ 51 this.oGc.lineTo( x1 + r * Math.cos( angle * i ), y1 + r * Math.sin( angle * i ) ); 52 } 53 this.oGc.closePath(); 54 this.oGc[this.paintType](); 55 } 56 }
3,界面操作很简单,基本是选项卡的操作+html5的自定义属性+classList的应用
以上是关于[js高手之路]html5 canvas动画教程 - 自己动手做一个类似windows的画图软件的主要内容,如果未能解决你的问题,请参考以下文章
[js高手之路]html5 canvas动画教程 - 下雪效果
[js高手之路] html5 canvas系列教程 - 像素操作(反色,黑白,亮度,复古,蒙版,透明)
[js高手之路] html5 canvas动画教程 - 匀速运动
[js高手之路] html5 canvas系列教程 - 文本样式(strokeText,fillText,measureText,textAlign,textBaseline)