canvas20181114
1. canvas 描边、填充、画线、闭合路径、非零环绕原则
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>canvas</title> </head> <style> canvas { border: 1px solid red; } </style> <body> <canvas id="can" width="300" height="300"></canvas> <p class="juxing">矩形</p> <p class="triangle">三角形锯齿问题</p> <p class="triangle2">三角形闭合没锯齿,填充色</p> <p class="middleEmpty">中空矩形</p> <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <script> let cvs = document.getElementById('can') let ctx = cvs.getContext('2d') $('.juxing').click(function () { a(ctx) }) $('.triangle').click(function () { b(ctx) }) $('.triangle2').click(function () { c(ctx) }) $('.middleEmpty').click(function () { middleEmpty(ctx) }) /** * 矩形 * @param ctx */ function a (ctx) { ctx.beginPath() // 初始位置 ctx.moveTo(10, 10) // 线条 ctx.lineTo(110, 10) ctx.lineTo(110, 110) ctx.lineTo(10, 110) ctx.lineTo(10, 10) // 描边 ctx.stroke() } /** * 等腰三角形 ,继续画解决锯齿 * @param ctx */ function b (ctx) { ctx.beginPath() ctx.moveTo(110, 10) ctx.lineTo(160, 60) /* 这些线条接着画不会出现锯齿*/ ctx.lineTo(60, 60) ctx.lineTo(110, 10) // 解决锯齿 ctx.lineTo(160, 60) /* 这里也要接下继续画取消底点锯齿*/ // 线条颜色设置,必须放在绘制之前 ctx.strokeStyle = css任意的颜色表示 ctx.strokeStyle = 'blue' // 线宽设置,必须放在绘制之前 ctx.lineWidth = 6 ctx.stroke() } /** * 闭合路径: * 从当前路径的起点点到结束点连一条路径。 * ctx.closePath() * 图形可以省去最后一条边,也解决了锯齿 * @param ctx */ function c (ctx) { ctx.beginPath() // 清除当前路径,开启新路径,解决了所有图片颜色覆盖问题 ctx.moveTo(110, 10) ctx.lineTo(160, 60) ctx.lineTo(60, 60) // 有了closePath,绘图直线图形时,最后一条边就可以省去了 ctx.closePath() ctx.strokeStyle = 'yellow' // 线宽设置,必须放在绘制之前 ctx.lineWidth = 6 ctx.fillStyle = 'red' // 填充色 ctx.fill() // 填充 默认黑色 ctx.stroke() } /* * 非零环绕原则: * 是用来判断哪些区域属于路径内( 计算结果非0,即为路径内,有填充色 )。 * 在路径包围的区域中,随便找一点,向外发射一条射线, * 和所有围绕它的边相交, * 然后开启一个计数器,从0计数, * 如果这个射线遇到顺时针围绕,那么+1, * 如果遇到逆时针围绕,那么-1, * 如果最终值非0,则这块区域在路径内。 * * 备注:基数边的区域一定在路径内,有填充色。(画线过程体现出图形的方向) * */ // 封装一个绘制矩形的函数 function juXing (startX, startY, width, height, lineWidth, strokeStyle, fillStyle) { ctx.moveTo(startX, startY) ctx.lineTo(startX + width, startY) ctx.lineTo(startX + width, startY + height) ctx.lineTo(startX, startY + height) ctx.closePath() ctx.lineWidth = lineWidth ctx.strokeStyle = strokeStyle ctx.fillStyle = fillStyle /* * 描边的时候,会占用原图形的一部分( 线宽的一半 )。 * 所以,日常开发中,为了让线宽符合要求, * 最好先填充,再描边,防止填充时覆盖掉线宽的一半。 * */ ctx.fill() ctx.stroke() } /** * 中间空的填充矩形 * 两个矩形 一个逆时针画,一个顺时针画, * 最中间部分,根据非零环绕原则等于0,则是在路径外,无填充色 * @param ctx */ function middleEmpty (ctx) { // 顺时针画一个大矩形 juXing(10, 10, 100, 100, 6, 'skyblue') // 逆时针画一个小矩形 ctx.moveTo(35, 35) ctx.lineTo(35, 55) ctx.lineTo(55, 55) ctx.lineTo(55, 35) // 顺 -> 两个都是顺时针的话,全都被填充了 /* ctx.moveTo( 35, 35 ); ctx.lineTo( 55, 35 ); ctx.lineTo( 55, 55 ); ctx.lineTo( 35, 55 );*/ ctx.lineWidth = 3 ctx.strokeStyle = 'blue' ctx.closePath() ctx.stroke() // 一起填充 ctx.fillStyle = 'red' ctx.fill() } </script> </body> </html>
2.canvas 线帽(线顶点)、线连接点、矩形api,清空画布,渐变矩形(一堆线加上 rgb线条颜色 )
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>canvas</title> </head> <style> canvas { border: 1px solid red; } p { border: 2px solid blue; padding:10px; margin-right: 10px; } </style> <body> <canvas id="can" width="300" height="300"></canvas> <div style="display: flex;"> <p class="juxing">线帽(线顶点)lineCap</p> <p class="triangle">线连接点 lineJoin</p> <p class="triangle2">矩形api</p> <p class="middleEmpty">清空画布</p> <p class="bian">渐变矩形</p> </div> <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <script> let cvs = document.getElementById('can') let ctx = cvs.getContext('2d') $('.juxing').click(function () { a(ctx) }) $('.triangle').click(function () { b(ctx) }) $('.triangle2').click(function () { c(ctx) }) $('.middleEmpty').click(function () { d(ctx, cvs) }) $('.bian').click(function () { lineGarden(ctx) }) /* * 设置线帽样式: * ctx.lineCap = ‘butt' 、'round'、'square' * butt是默认值, * round线头是圆的,多了两个半径出来,比默认的长 * square线头两段各增加线宽的一半,比默认的长 * */ function a (ctx) { ctx.beginPath() // 防止 stroke 重绘之前的路径 默认 stroke 会生成所有路径 // canvas在绘制线条的时候,会向左向右偏移线宽的一半,然后进行绘制。 // 如果线宽为奇数,那么边缘的颜色值,会缩减一半(调整线条粗细看效果)。 ctx.lineWidth = 10 // 默认线头 ctx.moveTo(10, 10) ctx.lineTo(210, 10) ctx.stroke() ctx.beginPath() ctx.lineCap = 'square' // 增长线头,两端各增长线宽的一半 // ctx.lineCap = 'round'; // 圆线头,两端的圆半径为线宽的一半 ctx.moveTo(10, 30) ctx.lineTo(210, 30) ctx.stroke() } /* * 设置线连接点样式: * ctx.lineJoin = ‘miter' 、'round'、'bevel' * miter是默认值,两边向外延伸相交为尖尖角, * round是圆头, * bevel两边相连为一个斜面。 * */ function b (ctx) { ctx.beginPath() ctx.lineWidth = 10 // 默认交点为尖尖角 ctx.lineJoin = 'miter' ctx.moveTo(10, 10) ctx.lineTo(60, 110) ctx.lineTo(110, 10) ctx.stroke() ctx.beginPath() ctx.lineJoin = 'round' // 交点为圆头 // ctx.lineJoin = 'bevel'; // 交点为斜面 ctx.moveTo(10, 50) ctx.lineTo(60, 150) ctx.lineTo(110, 50) ctx.stroke() } function c (ctx) { ctx.beginPath() /* * 画一个矩形路径: * ctx.rect( 起点x轴坐标,起点y轴坐标,宽,高 ); * */ ctx.rect(30, 10, 50, 50) ctx.stroke() /* * 绘制一个描边矩形,这个方法不会产生任何路径: * ctx.strokeRect( 起点x轴坐标,起点y轴坐标,宽,高 ) * */ ctx.strokeRect(30, 100, 50, 50) /* * 绘制一个填充矩形,这个方法不会产生任何路径: * ctx.fillRect( 起点x轴坐标,起点y轴坐标,宽,高 ) * */ ctx.fillRect(30, 170, 50, 50) } function d (ctx, cvs) { /* * 按照矩形的大小来清除画布中指定位置的内容: * ctx.clearRect( 起点x轴坐标,起点y轴坐标,宽,高 ); * */ ctx.clearRect(0, 0, cvs.width, cvs.height) } /* 渐变矩形 画一堆横线,加上rgb 线条*/ function lineGarden (ctx) { // 需求,在50,50点绘制一个宽高各100的渐变矩形 var i = 0, len = 100 for (; i < len; i++) { // 为了防止重绘 ctx.beginPath() ctx.moveTo(50, 50 + i) ctx.lineTo(150, 50 + i) // 红色通道值依次累加 ctx.strokeStyle = 'rgb(' + 0 + ', ' + Math.floor(255 / 99 * i) + ', ' + 255 + ' )' // ctx.strokeStyle = 'rgb(' + 0 + ', ' + i + ', ' + 255 + ' )'; ctx.stroke() } } </script> </body> </html>
3. canvas 折线图
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid red; } </style> </head> <body> 0.各部分都可单独研究绘画细节 1.定义坐标轴距离画布上下左右的边距 定义线条粗细 2.求横坐标原点,x轴顶点,y轴顶点坐标,并画x/y轴 3.定义箭头的宽高,把箭头当成一个矩形,放大可看细节,并填充 4.画任意点,把点当成矩形来画 fillRect() 宽高为2,为1的话颜色太浅, 宽高可以适当变大变小 5.画折线,连接的点实际上连接的是小矩形的左上角顶点,要偏移一下坐标,坐标偏移小矩形宽高的一半,使线穿过小矩形的中心 6.计算 x/y轴的长度,计算数据的缩放比例 7.[1,5640,654,2,2,6,5,3,56] 根据y轴的缩放比例来修改原数据,根据x轴的缩放比例来显示各个点的间距 <canvas id="cvs" width="500" height="500"></canvas> <script> var cvs = document.getElementById('cvs'); var ctx = cvs.getContext('2d'); ctx.lineWidth = 2; // 坐标轴距离画布上右下左的边距 var padding = { top: 20, right: 20, bottom: 20, left: 20 } // 坐标轴中箭头的宽和高 var arrow = { width: 12, height: 20 } // 求坐标轴上顶点的坐标 var vertexTop = { x: padding.left, y: padding.top } // 求坐标轴原点的坐标 var origin = { x: padding.left, y: cvs.height - padding.bottom } // 求坐标轴右顶点的坐标 var vertexRight = { x: cvs.width - padding.right, y: cvs.height - padding.bottom } // 画坐标轴中的两条线 ctx.moveTo( vertexTop.x, vertexTop.y ); ctx.lineTo( origin.x, origin.y ); ctx.lineTo( vertexRight.x, vertexRight.y ); ctx.stroke(); // 画上顶点箭头 ctx.beginPath(); ctx.moveTo( vertexTop.x, vertexTop.y ); ctx.lineTo( vertexTop.x - arrow.width / 2, vertexTop.y + arrow.height ); ctx.lineTo( vertexTop.x, vertexTop.y + arrow.height / 2 ); ctx.lineTo( vertexTop.x + arrow.width / 2, vertexTop.y + arrow.height ); ctx.closePath(); ctx.fill(); // 画右顶点箭头 ctx.beginPath(); ctx.moveTo( vertexRight.x, vertexRight.y ); ctx.lineTo( vertexRight.x - arrow.height, vertexRight.y - arrow.width / 2 ); ctx.lineTo( vertexRight.x - arrow.height / 2, vertexRight.y ); ctx.lineTo( vertexRight.x - arrow.height, vertexRight.y + arrow.width / 2 ); ctx.closePath(); ctx.fill(); // 求坐标轴默认可显示数据的最大值 coordMaxX = cvs.width - padding.left - padding.right - arrow.height; coordMaxY = cvs.height - padding.top - padding.bottom - arrow.height; /* * 在坐标轴中指定位置画点,坐标算法: * 点的x轴:原点x坐标 + 点到原点的水平距离 * 点的y轴:原点y坐标 - 点到原点的垂直距离 * */ // 需求,利用折线图的方式展示一下门口大爷酱香饼每日销售量 // [ 10, 20, 50, 80, 120, 300, 100, 50, 2 ]; var data = [ 100, 200, 400, 600, 1200, 1800, 1000, 500, 20 ]; // 求数据缩放的比例 var ratioX = coordMaxX / data.length; var ratioY = coordMaxY / Math.max.apply( null, data ); // 根据比例,对元数据进行缩放 var ratioData = data.map( function( val, i ) { return val * ratioY; }); // 画点 ratioData.forEach( function( val, index ) { ctx.fillRect( origin.x + ( index * ratioX) - 2, origin.y - val - 2, 4, 4 ); }); // 画折线 ctx.beginPath(); ratioData.forEach( function( val, index ) { ctx.lineTo( origin.x + ( index * ratioX), origin.y - val ); }); ctx.stroke(); </script> </body> </html>
4. canvas 饼图
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid red; } p{ line-height: 30px; margin:0; } </style> </head> <body> <p>混入式继承,换种方式的原型继续 Person.prototype={}</p> 画扇形 <br/> <p>1.计算数据占的比例 i/sum*360度</p> <p>2. 度数转为弧度</p> <p>3.每一个扇形的开始弧度等于上一个扇形的结束弧度 每一个扇形的结束弧度等于上一个扇形的结束弧度+所占的弧度</p> <p>4. 画扇形 填充</p> <p>5.0计算扇形平分线的坐标(即圆上点的坐标)</p> <p>5.1画平分线,并延长(延长半径,则平分钱也延长了)并填充颜色,覆盖点扇形内的线</p> <p>6绘制文字</p> <canvas id="cvs" width="500" height="500"></canvas> <script> (function (w) { // 把角度转换为弧度 function angleToRadian (angle) { return Math.PI / 180 * angle } // 混入式继承 function extend (o1, o2) { for (var key in o2) { // 只有o2自己的属性才会copy到o1身上 if (o2.hasOwnProperty(key)) { o1[key] = o2[key] } } } /* * constrcutor { Pipe } 饼图构造函数 * param { x: number } 圆心x轴坐标 * param { y: number } 圆心y轴坐标 * param { r: number } 圆半径 * param { data: Array } 绘制饼图所需的数据 * */ function Pipe (x, y, r, data) { this.x = x this.y = y this.r = r this.data = data // 一组颜色 this.colors = ['orange', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'peru', 'pink'] } // 给原型扩充方法 extend(Pipe.prototype, { // 绘制饼图 draw: function () { // 在外面保存一下this var self = this // 数据的总和 var num = 0 this.data.forEach(function (obj) { num += obj.val }) // 一个数据值所占用的角度 var baseAngle = 360 / num // 假设一开始就绘制了一个起始为0,结束为0的扇形 var startAngle = 0, endAngle = 0, lineAngle = 0, lineX, lineY // 画扇形 this.data.forEach(function (obj, i) { // 每次进来,计算当前扇形的起始角度和结束角度 // 下一个扇形的起始角度,是当前扇形的结束角度 startAngle = endAngle // 这个结束角度 = 上一个扇形的结束角度 + 当前数值所对应的角度 endAngle = endAngle + baseAngle * obj.val // 求扇形中间线的角度 lineAngle = startAngle + baseAngle * obj.val / 2; /* * 根据中间线的角度,求中间的线的x和y坐标: * x = 圆心x + r * Math.cos( angleToRadian( pointAngle ) ) * y = 圆心y + r * Math.sin( angleToRadian( pointAngle ) ) * */ lineX = self.x + ( self.r + 20 ) * Math.cos( angleToRadian( lineAngle ) ); lineY = self.y + ( self.r + 20 ) * Math.sin( angleToRadian( lineAngle ) ); // 第一个扇形 ctx.beginPath() ctx.moveTo(self.x, self.y) ctx.arc(self.x, self.y, self.r, angleToRadian(startAngle), angleToRadian(endAngle)) ctx.closePath() ctx.fillStyle = self.colors[i] ctx.fill() // 画每一个扇形的平分线 ctx.beginPath(); ctx.moveTo( self.x, self.y ); ctx.lineTo( lineX, lineY ); ctx.strokeStyle = self.colors[ i ]; ctx.stroke(); // 绘制文字 if ( lineAngle >= 90 && lineAngle <= 270 ) { ctx.textAlign = 'right'; }else { ctx.textAlign = 'left'; } ctx.fillText( obj.msg, lineX, lineY ); }) } }) // 把构造函数暴露到全局 w.Pipe = Pipe }(window)); var cvs = document.getElementById('cvs') var ctx = cvs.getContext('2d') // var pipe = new Pipe( 200, 200, 80, [ 10, 30, 50, 60, 20 ] ); // [ 10, 30, 50, 60, 20 ] var pipe = new Pipe(200, 200, 80, [ { val: 10, msg: '米饭' }, { val: 30, msg: '面条' }, { val: 50, msg: '馒头' }, { val: 50, msg: '豆腐脑' }, { val: 50, msg: '饺子' }, { val: 90, msg: '汤圆' }, ]) pipe.draw() </script> </body> </html>
5.canvas 生成图片、画弧、扇形、文字
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>canvas</title> </head> <style> canvas { border: 1px solid red; } p { border: 2px solid blue; padding: 10px; margin-right: 10px; } </style> <body> <canvas id="can" width="300" height="300"></canvas> <div style="display: flex;"> <p class="juxing">虚线 setLineDash 生成图片 canvas.toDataUrl('image/png')</p> <p class="triangle">画弧</p> <p class="triangle2">扇形</p> <p class="middleEmpty">清空画布</p> <p class="bian">文字</p> </div> <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <script> let cvs = document.getElementById('can') let ctx = cvs.getContext('2d') $('.juxing').click(function () { a(ctx, cvs) }) $('.triangle').click(function () { b(ctx) }) $('.triangle2').click(function () { c(ctx) }) $('.middleEmpty').click(function () { d(ctx, cvs) }) $('.bian').click(function () { e(ctx) }) /* * 设置画线的时候空白部分和实线部分的大小。 * ctx.setLineDash( [ 5, 3 ] ) * 参数可以任意多个 * */ function a (ctx, cvs) { ctx.beginPath() ctx.lineDashOffset = 3 // 设置偏移量,进行微调用,效果不明显 ctx.setLineDash([5, 3]) // 5像素实线,3像素空白,则变成了虚线 ctx.moveTo(10, 10) ctx.lineTo(210, 10) ctx.stroke() let url = cvs.toDataURL('image/png') let img = document.createElement('img') img.src = url document.body.appendChild(img) /* * 获取线条绘制规则。 * ctx.getLineDash() * */ console.log(ctx.getLineDash()) } /* * 画弧( 画的是路径 ) * ctx.arc( 圆心x轴坐标,圆心y轴坐标,半径, 起点弧度,结束点弧度,是否逆时针画(可选) ) * arc方法内部会先从路径结束点到弧的起点画一条路径线。 * 起点弧度、结束点弧度以及弧度的方向共同决定了弧的大小。 * */ // 把角度转换为弧度 function angleToRadian (angle) { return Math.PI / 180 * angle } function b (ctx) { ctx.beginPath() // 顺时针画一段弧 从x轴水平线右侧开始 默认顺时针 ctx.arc(100, 100, 30, angleToRadian(90), angleToRadian(270)) ctx.stroke() // 逆时针画一段弧 ctx.beginPath() ctx.arc(200, 100, 30, angleToRadian(90), angleToRadian(270), true) ctx.stroke() } function c (ctx) { ctx.beginPath() /* * 画扇形: * 1、先设置路径起点为圆心 * 2、画弧 * 3、闭合路径 * */ ctx.beginPath() ctx.moveTo(100, 100) // arc方法内部会先从路径结束点到弧的起点画一条路径线。 ctx.arc(100, 100, 90, angleToRadian(220), angleToRadian(310)) ctx.closePath() ctx.stroke() } function d (ctx, cvs) { ctx.clearRect(0, 0, cvs.width, cvs.height) ctx.setLineDash([]) // 清空虚线 } /* * 设置文字的属性 * ctx.font = 和css语法一样。 * 注意:这里设置字体大小时必须带单位,单位支持css的所有表示方式。 * 注意:单独设置字体大小不会生效,必须要加一个额外属性样式。 ctx.strokeText( 描边文字, 参考x轴坐标,参考y轴坐标,限制文字的最大长度(可选) ) ctx.fillText( 填充文字, 参考x轴坐标,参考y轴坐标,限制文字的最大长度(可选) ) * 设置文字的水平对其方式: * ctx.textAlign = 'left || start' 、 'right || end' 、 'center' * 默认值为start。 * 设置文字的垂直对其方式: * ctx.textBaseline = 'top' 、'bottom'、'middle'、'alphabetic'、'hanging'、'ideographic' * 默认值为alphabetic。 * */ function e (ctx) { ctx.beginPath() // ctx.font = '3rem 微软雅黑' ctx.font = '28px 微软雅黑' ctx.fillStyle = 'deeppink'; ctx.textAlign = 'left' // 水平对齐 ctx.textBaseline = 'middle' // 垂直对齐 ctx.strokeText('描边字', 100, 100) // 描边文字,空心字 // ctx.strokeText('中文字',100,100,100) // 第一个参数限制文字宽度,小了则压缩字体 ctx.fillText('中文字', 200, 200) // 填充文字,一般字 // 绘制文字的参考点 ctx.beginPath() ctx.arc(100, 100, 4, 0, Math.PI * 2) ctx.fill() // 在画布的左上角绘制一段文字 ctx.textAlign = 'left'; ctx.textBaseline = 'top'; ctx.fillText( '左上角', 0, 0 ); // 在画布的右上角绘制一段文字 ctx.textAlign = 'right'; ctx.textBaseline = 'top'; ctx.fillText( '右上角', cvs.width, 0 ); } </script> </body> </html>
6. canvas drawImage() 三参数、五参数、九参数、
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>canvas</title> </head> <style> canvas { border: 1px solid red; } </style> <body> <img src="NPC5.png" alt="" id="img"> <!-- 160*260 --> <canvas id="can" width="300" height="360"></canvas> <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <script> let cvs = document.getElementById('can') let ctx = cvs.getContext('2d') // 直接画,避开函数调用产生的异步问题 /* * ctx.drawImage() * 绘制图像,有三种使用方式。 * 三参数 图像、坐标x,坐标y * 五参数 图像、坐标x,坐标y, 图片宽,图片高 * 九参数 把裁剪到的部分图像绘制到指定的坐标,并指定其大小。 * drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) * sx, sy, sw, sh 切原图一小块的坐标宽高 * dx,dy,dw,dh 是最终图片的坐标,宽高 * */ let img = document.getElementById('img') img.onload = function () { // ctx.drawImage(img, 10, 10) // ctx.drawImage(img, 10, 100, img.width, img.height) // drawImage(image, dx, dy) // drawImage(image, dx, dy, dw, dh) // drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) ctx.drawImage(img, 0, img.height / 4 * 2, img.width / 4, img.height / 4, 40, 140, img.width / 4, img.height / 4) let i = 0, step = 0 let interval = setInterval(() => { ctx.clearRect(80, 140, cvs.width, cvs.height) ctx.drawImage(img, img.width / 4 * i, img.height / 4, img.width / 4, img.height / 4, 180 - step * 5, 140, img.width / 4, img.height / 4) i++ step++ if (i > 3) { i = 0 } if (180 - step * 5 < 80) { window.clearInterval(interval) } }, 100) } </script> </body> </html>
7.canvas 平移 translate、旋转 rotate、缩放 scale
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid red; } </style> </head> <body> <canvas width="300" height="300" id="can"></canvas> <script> let cvs = document.getElementById('can') let ctx = cvs.getContext('2d') /* * 平移,移动原来的矩形 * 平移、缩放、旋转会叠加 * */ ctx.fillRect(30, 30, 20, 20) ctx.translate(20, 20) ctx.fillStyle = 'blue' ctx.fillRect(30, 30, 20, 20) /* * 旋转 相对于原点的旋转 * 先平移到中间某位置 * */ ctx.translate(100, 100) ctx.fillRect(0, 0, 30, 30) // 从 (0,0)开始 ctx.rotate(Math.PI / 6) ctx.fillStyle = 'red' ctx.fillRect(0, 0, 30, 30) ctx.rotate(Math.PI / 6) ctx.fillStyle = 'pink' ctx.fillRect(0, 0, 30, 30) /* * 缩放 相对于原点的缩放 * 先平移到中间某位置 * 最好单独试,免得叠加计算出错 * * */ ctx.rotate(-Math.PI / 6*2) // 旋转回来 ctx.translate(60, 60) ctx.fillRect(0, 0, 40, 40) ctx.scale(0.5, 0.5) ctx.fillStyle = 'blue' ctx.fillRect(0, 0, 40, 40) </script> </body> </html>
8.工厂模式 函数返回时 return new Person(name,age)
/* 工厂模式 */ function Person (name, age) { this.name = name this.age = age } // 工厂 // 工厂模式相当于省去new关键字 function getPerson (name, age) { return new Person(name, age) } console.log(getPerson('女神', 18))
9.状态保存、回滚 ctx.save() ctx.restore() 判断点在画布中, 旋转图形绘制(第9不太懂)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>canvas</title> </head> <style> canvas { border: 1px solid red; } p { border: 2px solid blue; padding: 10px; margin-right: 10px; } </style> <body> <canvas id="can" width="300" height="360"></canvas> <div style="display: flex;"> <p class="juxing">状态保存、回滚 ctx.save() restore()</p> <p class="triangle">判断点在画布中</p> </div> <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <script> let cvs = document.getElementById('can') let ctx = cvs.getContext('2d') /* * 绘制一个旋转图形的步骤: * 1、先平移坐标轴到图形的中心 * 2、旋转坐标轴 * 3、绘制图形( 需要注意,平移旋转之后,坐标体系变化,不能按照之前定好的坐标来绘制旋转图形 ) * */ /* // 正常情况下的参考矩形 ctx.fillStyle = 'pink'; ctx.fillRect( 100, 100, 50, 50 ); // 原来的图形 // 平移到矩形的中心 ctx.translate( 125, 125 ); // 旋转坐标系 ctx.rotate( Math.PI / 180 * 30 ); // 绘制图形 ctx.fillStyle = 'blue'; ctx.fillRect( -25, -25, 50, 50 ); // 新坐标为宽高的一半,不懂。*/ // 定时器 // 先统一平移到矩形的中心 ctx.translate(125, 125) // 基于这个中心不断绘制旋转矩形 setInterval(function () { // 清除上一次的矩形 ctx.clearRect(-50, -50, cvs.width, cvs.height) // 旋转坐标系 ctx.rotate(Math.PI / 180 * 4) // 绘制图形 ctx.fillRect(-25, -25, 50, 50) }, 50) /* 工厂模式 */ function Person (name, age) { this.name = name this.age = age } // 工厂 // 工厂模式相当于省去new关键字 function getPerson (name, age) { return new Person(name, age) } console.log(getPerson('女神', 18)) /** * 状态保存: ctx.save(); * 状态回滚: ctx.restore(); * */ $('.juxing').click(function () { // 保存下面的状态 ctx.save() ctx.lineWidth = 10 ctx.strokeStyle = 'blue' ctx.moveTo(10, 10) ctx.lineTo(210, 10) ctx.stroke() // 再保存下面的状态 ctx.save() ctx.strokeStyle = 'green' ctx.beginPath() ctx.moveTo(10, 60) ctx.lineTo(210, 60) ctx.stroke() // 回滚 ctx.restore() ctx.beginPath() ctx.moveTo(10, 90) ctx.lineTo(210, 90) ctx.stroke() }) $('.triangle').click(function () { /* * 判断点在不在路径中: * ctx.isPointInPath( 要判断的点x轴坐标,要判断的点y轴坐标 ) * */ ctx.rect(10, 10, 50, 50) ctx.stroke() // 点击画布,判断点击的位置在不在路径中 cvs.addEventListener('click', function (e) { var x = e.pageX - cvs.offsetLeft var y = e.pageY - cvs.offsetTop alert(ctx.isPointInPath(x, y)) }) }) </script> </body> </html>
10 监听者模式(不懂)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> // 一个监听者 服务与 多个听众 // 监听者 var jianTingZhe = { // 听众列表 listeners: { birdOver: [], birdFlappy: [], birdRotate: [] }, // 小鸟死亡触发时,告知所有监听死亡的听众 triggerBirdOver: function() { this.listeners.birdOver.forEach( function( listen ) { listen(); }); }, // 小鸟飞翔的时候,告知所有监听飞翔的听众 triggerBirdFlappy: function() { this.listeners.birdFlappy.forEach( function( listen ) { listen(); }); }, // 小鸟飞翔的时候,告知所有监听飞翔的听众 triggerBirdRotate: function() { this.listeners.birdRotate.forEach( function( listen ) { listen(); }); } }; // 这是一个模块,整体可以认为是一个听众监听N多事件 (function( w ) { // 小鸟死亡听众 jianTingZhe.listeners.birdOver.push( function() { console.log( '小鸟死了,我就哭' ); } ); jianTingZhe.listeners.birdOver.push( function() { console.log( '小鸟死了,我很伤心' ); } ); jianTingZhe.listeners.birdOver.push( function() { console.log( '小鸟死了,我要埋葬它' ); } ); // 小鸟飞翔听众 jianTingZhe.listeners.birdFlappy.push( function() { console.log( '小鸟飞了,我很搞笑' ); }); jianTingZhe.listeners.birdFlappy.push( function() { console.log( '小鸟飞了,我要把射下来' ); }); // 小鸟旋转听众 jianTingZhe.listeners.birdRotate.push( function() { console.log( '小鸟转了,呱唧呱唧!' ); }); }( window )); // 这是另一个模块,整体可以认为是一个听众 (function( w ) { // 小鸟旋转听众 jianTingZhe.listeners.birdRotate.push( function() { console.log( '我是模块2' ); }); }( window )); // 监听到小鸟死了 jianTingZhe.triggerBirdOver(); // 监听到小鸟转了 jianTingZhe.triggerBirdRotate() // 监听到小鸟飞翔了 jianTingZhe.triggerBirdFlappy() </script> </body> </html>
11.动画帧函数 requestAnimationFrame 函数回调的触发是由浏览器来控制的,行就会比较稳定,适合用来做流畅的动画。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> div { height: 20px; background-color: black; } </style> </head> <body> <div id="div"></div> <script> /* * 请求动画帧函数,这个函数和setTimeout方法使用类似, * 他都是定时器,却别在于setTimeout可以自由指定回调的触发时间, * 而requestAnimationFrame函数回调的触发是由浏览器来控制的。 * * requestAnimationFrame( callback ) * 备注:当浏览器重绘页面的时候,就会调用这个callback, * 这样callbackg的执行就会比较稳定,适合用来做流畅的动画。 * */ /*setInterval( function() { console.log(111); }, 50);*/ /*function con() { console.log(111); setTimeout( con, 50); } setTimeout( con, 50);*/ // 简化setTimeout不断执行回调的方式 /*(function con() { console.log(111); setTimeout( con, 50); }());*/ // 把setTimeout改为requestAnimationFrame (function con() { console.log(111); requestAnimationFrame( con ); }()); </script> </body> </html>
12. ctx2.drawImage( cvs1, 0, 0 ); // 把第一个canvas的内容绘制到第二个canvas中
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> div { height: 20px; background-color: black; } </style> </head> <body> <canvas id="cvs1"></canvas> <canvas id="cvs2"></canvas> <script> /* * drawImage的第一个参数可以是img、canvas或video * */ var cvs1 = document.querySelector( '#cvs1' ); var cvs2 = document.querySelector( '#cvs2' ); var ctx1 = cvs1.getContext( '2d' ); var ctx2 = cvs2.getContext( '2d' ); // 给第一个画布绘制内容 ctx1.fillRect( 10, 10, 100, 100 ); // 把第一个canvas的内容绘制到第二个canvas中 ctx2.drawImage( cvs1, 0, 0 ); ctx2.fillStyle = 'blue'; ctx2.fillRect( 0, 0, 50, 50 ); </script> </body> </html>