# base-canvas **Repository Path**: lisa_zhu2012/base-canvas ## Basic Information - **Project Name**: base-canvas - **Description**: canvas学习,threejs基础入门学习 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2020-03-30 - **Last Updated**: 2024-09-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## canvas ```html ``` canvas是一个矩形区域的画布,可以使用脚本javascript在其中绘制图形的HTML元素,是HTML5中新增的元素。 canvas自身并不具备绘制功能,canvas须依靠javascript才能绘制出图形;canvas也提供了多种绘制路径、矩形、圆形、字符以及添加图像的方法; canvas 的默认大小为300像素×150像素(宽×高,像素的单位是px)。 1. canvas不支持在ie9以下的浏览器中执行 2. canvas宽高设置问题 如果使用style/js(canvas.style.width/height)设置宽高,等同于把画布进行缩放,可能会使内容模糊 canvas正确设置宽高的方法:(属性单位必须是px) * **直接在canvas标签上使用width/height设置** * **canvas.width/height 在js中设置** ### canvas 主要应用领域 1. 游戏 2. 可视化数据 3. banner广告 ### context canvas 的上下文、绘制环境 context对象就是js操作canvas的接口:[canvasElement].getContext('2d')来获取2D绘图上下文 > 3d:webgl ### canvas 基本图形绘制 > 代码详见demo/canvas.html 1. 矩形:矩形是一个唯一一个可以直接绘制的形状 1. 绘制一个填充的矩形fillRect(x,y,w,h) 2. 绘制一个描边的矩形strokeRect(x,y,w,h) 3. 清除一个矩形区域clearRect(x,y,w,h) ``` // 清除之前的图片轨迹 canvas.width = canvas.width; // 一般不用这种方法 // 如果通过代码重新设置canvas画布的宽高,那么canvas画布里面的所有的内容都会被清空 ctx.clearRect(0,0,canvas.width,canvas.height); ``` > 了解:非零环绕规则,交叉路径的填充问题,运用“非零环绕规则”,顺逆时针穿插次数决定是否填充。 2. 线段 > 注意:绘制的线段会出现跨像素,颜色变浅,占2像素的问题(+.5) 3. 绘制椭圆&圆弧 arc(圆心x,圆心y,半径,起始弧度(x,水平方向),结束弧度,绘制方向counterClockWise(默认顺时针[false]); 弧度->角度:30 * Math.PI/180 圆形上面的点的坐标的计算公式:x = x0 + Math.cos(rad) * R // x0:起始点;R:距离,注意都是弧度 4. 绘制文字 canvas处理文本的能力很弱 fillText(text,x,y[,maxWidth]) strokeText() font 书写有顺序要求:大小不能写在最后 ctx.font = 'italic 50px blod Arial'; textBaseline = 'bottom' // 设置字体底线对齐绘制基线 顶线:最高位置;基线:x下边;中线:x中间;底线:最底下位置; 行高的定义:两行文本基线的距离就是行高。 textAlign = 'left' // 设置字体对齐的方式 ctx.measureText(item.title).width; // 获取文字所占宽度 5. 绘制图片&视频 > canvas 绘制图片效率比其他高 > 视频绘制其实就是一帧一帧的绘制 1. drawImage(image,x,y); 2. drawImage(image,x,y,w,h); 3. 图片切片后绘制:s 原图像 d 绘制画布 drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh); ``` // 1. 创建图片的dom对象 var img = new Image(); // 只要设置了src属性,当前img对象立即去加载图片 img.src = '../images/what.jpg'; // 等待图片加载完成后,把图片绘制到canvas上 img.onload = function(){ // 2. 绘制图片(3类) // 2.1 绘制图片默认大小 ctx.drawImage(img,50,400); // 2.2 绘制图片并设置大小(缩放) ctx.drawImage(img,50,600,100,100); // 2.3 图片切片后绘制 ctx.drawImage(img,60,50,60,60,200,600,60,60) } ``` ### 基于状态的绘图 默认上来就有一个状态 状态里面:当前状态里面的绘制样式 beginPath(); 开启一个新路径,开启新状态的绘图;来启一个新的状态,新状态可以继承之前状态的样式,但是当前状态设置所有的样式,只能作用于当前的状态。 ### canvas 内容样式设置 1. 颜色:接受颜色名,16进制数据,rgb值,rgba 1. fillStyle = color; 2. strokeStyle = color; 2. 透明度(整个画布的透明度)(了解) globalAlpha = value[0,1]; 3. 线型设置(了解) 1. 线宽,设置或返回当前的线条宽度 lineWidth = value 2. 两端样式,设置或返回线条的结束端点(线头、线冒)样式 lineCap = type butt:默认值,向线条的每个末端添加平直的边缘; round:圆形,向线条的每个末端添加圆形线冒; square:方形(会增加线的宽度),向仙台哦的每个末端添加方形线冒。 3. 拼接样式,设置或返回两条线相交时,所创建的拐角类型 lineJoin = type bevel:创建斜角; miter:默认,创建尖角; square round:创建圆角。 4. miterLimit 设置或返回最大斜接长度 + 斜接长度指的是在两条线交汇处内角和外角之间的距离。 + 一般用默认值,10就可以了。除非需要特别长的尖角时,是用此属性。 5. 绘制贝塞尔曲线(知道有) 6. 创建两条切线的弧(知道有) 类比css3中的圆角 arcTo 4. 渐变(了解) > 一般不用,都是用图片代替,canvas绘制图片效率更高;线性渐变可以用于矩形、圆形、文字等颜色样式。 线性渐变 ``` var linearGradient = ctx.createLinearGradient(50,850,150,950); linearGradient.addColorStop(0,'red') linearGradient.addColorStop(.2,'skyblue') linearGradient.addColorStop(.7,'green') linearGradient.addColorStop(1,'pink'); ctx.fillStyle = linearGradient; ctx.fillRect(50,850,100,100); ``` 径向渐变 ctx.createRadialGradient(x0,y0,r0,x1,y1,r1); x0:渐变的开始圆的x坐标;x1:渐变的结束圆的x坐标;r0:开始圆的半径 5. 设置阴影(了解,少用,性能差) > 设置png图片的阴影,图片透明部分不会被投影 ``` ctx.fillStyle = 'yellow'; ctx.shadowColor = 'red'; 设置或返回用于阴影的颜色 ctx.shadowBlur = 30; 设置或返回用于阴影模糊级别,大于1的正整数,树枝越高,模糊程度越大 ctx.shadowOffsetX = 20; 设置或返回阴影距形状的水平距离 ctx.shadowOffsetY = 20; 设置或返回阴影距形状的垂直距离 ctx.fillRect(50,850,100,100); ``` 6. 绘制背景图(了解) ctx.createPattern(img,pattern); 在制定的方向内重复制定的元素。 pattern:模式 repeat 默认。该模式在水平和垂直方向重复;repeat-x:该模式只在水平方向重复;repeat-y;no-repeat ### canvas 变换(重点) 变换:坐标系变换,与CSS3的2D转换类似 注意:原有内容不会受变换影响 1. 平移 ctx.translate(x,y) 2. 转转 ctx.rotate(angle);参数是弧度(PI) **如需将角度转换为弧度,请使用degrees*Math.PI/180公式进行计算。** 1弧度=180/π度,1度=π/180弧度 角度A1转换弧度A2:A2=A1*π/180 弧度A2转换角度A1:A1=A2*π/PI 3. 缩放 ctx.scale(x,y) [0,1] ### canvas 保存与恢复(重点) > 一般配合位移画布使用。 save() 保存当前画笔的状态,可以把当前绘制环境进行保存到缓存中。 restore() 返回之前保存过的路径状态和属性,获取最近缓存的ctx。 ``` ctx.fillRect(0,0,100,100); ctx.save(); // ctx.translate(100,100); ctx.rotate(30*Math.PI/180); ctx.fillStyle = 'tomato'; ctx.fillRect(0,0,100,100); ctx.restore(); ctx.fillRect(100,0,50,50); ``` ### canvas 动画 原理:是先定义一个初始的状态,然后定义一个定时器,定时器内执行一个方法,记得在这个方法中要对当前的画面清除,然后在这个方法中重新绘制需要变化的效果,由于人眼存在残影,所以,短时间内中断的变化可以看成是连续的变化。 动画其实是由不同的静态画面组成,每一幅静态画面我们叫做一帧(frame),当众多的静态画面按照一定的规则快速运动时,我们的眼睛就会欺骗我们的大脑,从而形成物体运动的假象。 ### 画布保存base64编码内容(重要) + 把canvas绘制的内容输出成base64内容 + 语法:canvas.toDataURL(type, encoderOptions); + 例如:canvas.toDataURL('image/jpg', 1); + 参数说明: * type:设置输出的类型,比如:image/png image/jpeg 等 * encoderOptions:0~1之间的数字,用于标识输出图片的质量,1表示无损压缩,类型为:image.jpeg 或者 image/webp才起作用。 ``` var canvas = document.getElementById('#canvas'); var dataURL = canvas.toDataURL(); console.log(dataURL); var img = document.querySelector('#img-demo'); // 拿到图片的dom对象 img.src = canvas.toDataURL('image/png'); // 将画布的内容给图片标签显示 ``` ### 画布渲染画布(重要) + context.drawImage(img,x,y); + img参数也可以是画布,也就是把一个画布整体的渲染到另外一个画布上。 + 优化性能,提高效率。在内存中批量绘制,一次性绘制到可见画布上。 ``` var canvas1 = document.querySelector('#cavsElem1'); var canvas2 = document.querySelector('#cavsElem2'); var ctx1 = canvas1.getContext('2d'); var ctx2 = canvas2.getContext('2d'); ctx1.fillRect(20,20,40,40); // 在第一个画布上绘制矩形 ctx2.drawImage(canvas1,10,10); // 将第一个画布整体绘制到第二个画布上 ``` ### canvas的像素化 我们使用canvas绘制了一个图形,一旦绘制成功了,canvas就像素化了他们。canvas没有能力从画布上再次得到这个图形,也就是说我们没有能力去修改已经在画布上的内容,这个就是canvas比较轻量的原因。flash重的原因之一就是它可以通过对应的api得到已经在“画布”上的内容然后进行绘制。 1. canvas的动画思想 如果想让canvas图形移动,必须按照 清屏(清除画布)-> 更新变量 -> 重新绘制渲染 的逻辑进行编程。总之就是重新再画一次。 canvas上的画布元素,就像被像素化了,所以不能通过style.left方法进行修改,而是必须重新绘制 代码演示部分 ```js // canvas方法参数智能提示 /* @type {HTMLCanvasElement} */ // 得到画布 var canvas = document.getElementById('canvas') // 获取2d的画笔对象 var ctx = canvas.getContext('2d') // 设置颜色 ctx.fillStyle = 'red' var left = 100 // 动画过程 setInterval(()=>{ // 清除画布;0,0代表从什么位置开始清除;500,600代表清除的宽度和高度 ctx.clearRect(0,0,500,600) // 更新信号量 left++ // 重新绘制 ctx.fillRect(left,100,100,100) }, 30) ``` 实际上,动画的生存就是相关静态画面连续播放了,这个就是动画的过程,我们把每一次绘制的静态画面叫做“一帧”,时间的间隔(定时器的间隔)就表示帧的间隔 动画过程在主定时器里面,每一帧都会调用实例的更新和渲染方法。 ## 面向对象 new 内部原理: 第一步:创建一个新的空对象(在内存中开辟一块空间); 第二部:把this指向到这个空对象; 第三部:把空对象的内部原型,指向构造函数的原型对象; 第四部:Cat 函数(函数对象):如果当前构造函数,执行到最后的时候,如果没有return,那么把构造函数中,构造的那个this对象返回给cat实例。 函数对象 Cat(this对象添加属性) ----> Cat.prototype show()(原型上添加方法) PI 对象的方法都放到原型上去定义(节省内存空间);原型上的属性只能通过原型对象自己去设置。尽量不要在原型中添加属性,公共的属性和常量的值可以放到原型对象中去。 | ^ V | cat = new Cat 实例对象 age cat.PI = 'abc' 如果实例对象设置一个跟原型对象相同属性的值时,会自动添加一个对象自己的属性。 读取取的是原型的属性 ### 面向对象实现canvas动画 因为canvas不能得到已经绘制的上一屏的对象,所以我们要维持对象的状态。在canvas中我们要使用面向对象的方式来编程,因为我们可以使用面向对象的方式来维持canvas的属性和状态; ```js var canvas = document.getElementById('canvas') var ctx = canvas.getContext('2d') function DrawRect(x, y, w, h, color) { this.x = x; this.y = y; this.w = w; this.h = h; this.color = color ? color:'red'; } // 更新方法 DrawRect.prototype.update = function() { this.x++; if(this.x > canvas.width) this.x = 0 } // 渲染 DrawRect.prototype.render = function() { // 设置颜色 ctx.fillStyle = this.color ctx.fillRect(this.x, this.y, this.w, this.h) } // 实例化 var p1 = new DrawRect(0,100,canvas.widht,canvas.height) setInterval(()=>{ ctx.clearRect(0,0,canvas.width,canvas.height) p1.update() p1.render() }, 30) // setTimeout(function run() { // ctx.clearRect(0,0,canvas.width,canvas.height) // p1.update() // p1.render() // setTimeout(run, 50) // }, 50) ``` ## canvas 库 国产的[egret](http://www.egret-labs.org/)引擎 比较火的3d引擎:[threejs](http://threejs.org) [Konva](http://konvajs.github.io/) 特点: * 小巧、使用方便、适合移动端和pc端 * 支持丰富的事件处理操作 * 支持类似jquery的操作方式 * 开源,可以随意更改 * 社区更新比较活跃,github托管源码 * 性能也不错 ### Konva 的使用快速上手 #### Konva的整体理念 * 舞台的概念的引入。整个视图看作是一个舞台stage * 舞台中可以绘制很多个层layer:一个舞台可以添加多个层,背景层,动画层,光照层 * layer下面可以有很多的group * group下面可以有矩形、图片、其他形状等 #### Konva 库的事件 * 跟jquery的表现一致 * on绑定事件,off解绑事件 #### Konva 库的动画系统 * to动画系统 * tween动画系统 * animate帧动画 ### Konva 库的使用案例 ##### 创建一个矩形 Konva.Rect(option); ``` // Konva使用基本案例 // 第一步:创建舞台 var stage = new Konva.Stage({ container: 'container', // 需要存放舞台的dom容器 width: window.innerWidth, // 设置全屏 height: window.innerWidth }); // 第二步:创建层 var layer = new Konva.Layer(); // 创建一个层 stage.add(layer); // 把层添加到舞台 // 第三步:创建矩形 var rect = new Konva.Rect({// 创建一个矩形 x: 100, // 矩形的x坐标,相对其父容器的坐标 y: 100, width: 100, height: 100, fill: 'gold', stroke: 'navy', strokeWidth: 4, opacity: .2, scale: 1.2, rotation: 30, // 旋转的角度,是deg不是弧度 cornerRadius: 10, // 圆角的大小(像素) draggable: true }) // 第四步:把矩形添加到层上去 layer.add(rect); // 第五步:把层渲染到舞台上 layer.draw(); ``` ##### Konva 进度条案例 #### webitcast案例 #### 柱状图 > 多用百分比设置大小,可以适配 #### 饼状图案例 #### 微信案例 需求:我们app画笔回显字体,字体包不能每个页面都重新引入,太大了,能根据接口按需引入么?