前端 | (十四)canvas基本用法 | 尚硅谷前端HTML5教程(html5入门经典)

文章目录

📚canvas基本用法

🐇什么是canvas(画布)

  • <canvas> 是 HTML5 新增的元素,可用于通过使用JavaScript中的脚本来绘制图形。例如,它可以用于绘制图形,创建动画。<canvas> 最早由Apple引入WebKit。
  • 我们可以使用<canvas>标签来定义一个canvas元素
  • 使用<canvas>标签时,建议要成对出现,不要使用闭合的形式。
  • canvas元素默认具有高宽:width: 300pxheight:150px

🐇替换内容

  • <canvas>很容易定义一些替代内容。由于某些较老的浏览器(尤其是IE9之前的IE浏览器)不支持HTML元素"canvas",但在这些浏览器上你应该要给用户展示些替代内容。

  • 我们只需要在<canvas>标签中提供替换内容就可以。

  • 支持<canvas>的浏览器将会忽略在容器中包含的内容,并且只是正常渲染canvas。不支持<canvas>的浏览器会显示代替内容。

    html 复制代码
    <!-- 如果浏览器不支持​<canvas>元素,那么它会显示"您的浏览器不支持画布元素 请您换成萌萌的谷歌"。 -->
    <canvas id="test" width="300" height="300">
    	<span>您的浏览器不支持画布元素 请您换成可爱的谷歌</span>
    </canvas>

🐇canvas标签的两个属性

  • <canvas> 看起来和 元素很相像,唯一的不同就是它并没有 src 和 alt 属性。
  • 实际上,<canvas> 标签只有两个属性------ width和height。这些都是可选的。
  • 当没有设置宽度和高度的时候,canvas会初始化宽度为300像素和高度为150像素。
  • 画布的高宽
    • html属性设置width height时只影响画布本身不影画布内容。
    • css属性设置width height时不但会影响画布本身的高宽,还会使画布中的内容等比例缩放(缩放参照于画布默认的尺寸)。

🐇渲染上下文

  • <canvas> 元素只是创造了一个固定大小的画布,要想在它上面去绘制内容,我们需要找到它的渲染上下文。

  • <canvas> 元素有一个叫做 getContext() 的方法,这个方法是用来获得渲染上下文和它的绘画功能。getContext()只有一个参数:上下文的格式

  • 获取方式

    css 复制代码
    var canvas = document.getElementById('box');
    var ctx = canvas.getContext('2d');
  • 检查支持性

    css 复制代码
    var canvas = document.getElementById('tutorial');
    if (canvas.getContext){
    	var ctx = canvas.getContext('2d');
    } 

📚绘制矩形

🐇绘制矩形

  • canvas提供了三种方法绘制矩形:
    • 绘制一个填充的矩形(填充色默认为黑色):fillRect(x, y, width, height)
    • 绘制一个矩形的边框(默认边框为:一像素实心黑色):strokeRect(x, y, width, height)
    • 清除指定矩形区域,让清除部分完全透明:clearRect(x, y, width, height)
  • x与y指定了在canvas画布上所绘制的矩形的左上角(相对于原点)的坐标。
  • width和height设置矩形的尺寸(存在边框的话,边框会在width上占据一个边框的宽度,height同理)。
html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			*{
				margin: 0;
				padding: 0;
			}
			html,body{
				height: 100%;
				overflow: hidden;
			}
			body{
				background: pink;
			}
			#test{
				background: gray;
				position: absolute;
				left: 0;
				top: 0;
				right: 0;
				bottom: 0;
				margin: auto;
			}
		</style>
	</head>
	<body>
		<canvas id="test" width="300" height="300">
			<span>您的浏览器不支持画布元素 请您换成可爱的谷歌</span>
		</canvas>
	</body>
	<script type="text/javascript">
		window.onload=function(){
			//拿到画布
			var canvas = document.querySelector("#test");
			if(canvas.getContext){
				var ctx = canvas.getContext("2d");
				//注意不加单位,填充的矩形
				ctx.fillRect(0,0,100,100)
				//带边框的矩形  
				ctx.strokeRect(100.5,100.5,100,100)
				// 清除指定矩形区域,让清除部分完全透明
				// ctx.clearRect(0,0,100,100)
			}
		}
	</script>
</html>

🐇strokeRect时,边框像素渲染问题

  • 按理渲染出的边框应该是1px的,canvas在渲染矩形边框时,边框宽度是平均分在偏移位置的两侧。
  • context.strokeRect(10,10,50,50):边框会渲染在10.5 和 9.5之间,浏览器是不会让一个像素只用自己的一半的,相当于边框会渲染在9到11之间。
  • context.strokeRect(10.5,10.5,50,50):边框会渲染在10到11之间。

🐇添加样式和颜色

  • fillStyle:设置图形的填充颜色。
  • strokeStyle:设置图形轮廓的颜色。
    • 默认情况下,线条和填充颜色都是黑色(CSS 颜色值 #000000)
  • lineWidth:这个属性设置当前绘线的粗细。属性值必须为正数。描述线段宽度的数字。 0、 负数、 Infinity 和 NaN 会被忽略。默认值是1.0。

🐇lineJoin

  • 设定线条与线条间接合处的样式(默认是 miter)

    • round : 圆角
    • bevel : 斜角
    • miter : 直角
    html 复制代码
    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="UTF-8">
    		<title></title>
    		<style type="text/css">
    			*{
    				margin: 0;
    				padding: 0;
    			}
    			html,body{
    				height: 100%;
    				overflow: hidden;
    			}
    			body{
    				background: pink;
    			}
    			#test{
    				background: gray;
    				position: absolute;
    				left: 0;
    				top: 0;
    				right: 0;
    				bottom: 0;
    				margin: auto;
    			}
    		</style>
    	</head>
    	<body>
    		<canvas id="test" width="300" height="300">
    			<span>您的浏览器不支持画布元素 请您换成可爱的谷歌</span>
    		</canvas>
    	</body>
    	<script type="text/javascript">
    		window.onload=function(){
    			//拿到画布
    			var canvas = document.querySelector("#test");
    			if(canvas.getContext){
    				var ctx = canvas.getContext("2d");
    				// 设置图形的填充颜色
    				ctx.fillStyle="deeppink";
    				// 设置图形轮廓的颜色
    				ctx.strokeStyle="pink";
    				// 当前绘线的粗细
    				ctx.lineWidth=25;
    				// 设定线条与线条间接合处的样式
    				ctx.lineJoin="round";
    				ctx.strokeRect(100,100,100,100)
    				ctx.fillRect(0,0,100,100)
                    //ctx.clearRect(100,100,100,100)
    			}
    		}
    	</script>
    </html>

  • rect画矩形

    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    	var ctx = canvas.getContext("2d");
    	ctx.rect(50,50,100,100);
    	ctx.fillStyle="skyblue";
    	ctx.strokeStyle="black";
    	ctx.fill();
    	ctx.stroke();
    }

📚绘制路径

🐇canvas绘制路径

图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。

  • 首先,创建路径起始点

    • beginPath():新建一条路径,生成之后,图形绘制命令被指向到路径上准备生成路径。
      • 本质上,路径是由很多子路径构成,这些子路径都是在一个列表中,所有的子路径(线、弧形、等等)构成图形。
      • 而每次这个方法调用之后,列表清空重置,然后我们就可以重新绘制新的图形。
  • 使用画图命令去画出路径

    • moveTo(x, y)
      • 将笔触移动到指定的坐标x以及y上
      • 当canvas初始化或者beginPath()调用后,你通常会使用moveTo()函数设置起点
    • lineTo(x, y)
      • 将笔触移动到指定的坐标x以及y上
      • 绘制一条从当前位置到指定x以及y位置的直线。
  • 之后你把路径封闭

    • closePath()
      • 闭合路径之后图形绘制命令又重新指向到上下文中。
      • 闭合路径closePath(),不是必需的。这个方法会通过绘制一条从当前点到开始点的直线来闭合图形。
      • 如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做。当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合。
      • stroke():通过线条来绘制图形轮廓。不会自动调用closePath()。
      • fill():通过填充路径的内容区域生成实心的图形。自动调用closePath()。
  • 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。

    html 复制代码
    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="UTF-8">
    		<title></title>
    		<style type="text/css">
    			*{
    				margin: 0;
    				padding: 0;
    			}
    			html,body{
    				height: 100%;
    				overflow: hidden;
    			}
    			body{
    				background: pink;
    			}
    			#test{
    				background: gray;
    				position: absolute;
    				left: 0;
    				top: 0;
    				right: 0;
    				bottom: 0;
    				margin: auto;
    			}
    		</style>
    	</head>
    	<body>
    		<canvas id="test" width="300" height="300">
    			<span>您的浏览器不支持画布元素 请您换成可爱的谷歌</span>
    		</canvas>
    	</body>
    	<script type="text/javascript">
    		window.onload=function(){
    			var canvas = document.querySelector("#test");
    			if(canvas.getContext){
    				var ctx = canvas.getContext("2d");
    				ctx.strokeStyle="deeppink";
    				ctx.lineWidth=10;
    				ctx.moveTo(100,100);
    				ctx.lineTo(100,200);
    				ctx.lineTo(200,200);
    				ctx.closePath();
    				ctx.stroke();
    				//fill方法会自动合并路径
    				ctx.fill();
    			}
    		}
    	</script>
    </html>

🐇lineCap

  • lineCap 是 Canvas 2D API 指定如何绘制每一条线段末端的属性。有3个可能的值,分别是:

    • butt:线段末端以方形结束。
    • round:线段末端以圆形结束
    • square:线段末端以方形结束,但是增加了一个宽度和线段相同,高度是线段厚度一半的矩形区域
    • 默认值是 butt。
    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    	var ctx = canvas.getContext("2d");
    	ctx.strokeStyle="pink";
    	ctx.lineWidth=10;
    	ctx.lineCap="round";	
    	ctx.moveTo(100,100);
    	ctx.lineTo(100,200);
    	ctx.stroke();
    }

🐇save

  • save() 是 Canvas 2D API 通过将当前状态放入栈中,保存 canvas 全部状态的方法。保存到栈中的绘制状态有下面部分组成:
    • 当前的变换矩阵。
    • 当前的剪切区域。
    • 当前的虚线列表。
    • 以下属性当前的值: strokeStyle, fillStyle, lineWidth, lineCap, lineJoin...

🐇restore

  • restore() 是 Canvas 2D API 通过在绘图状态栈中弹出顶端的状态,将 canvas 恢复到最近的保存状态的方法。

  • 如果没有保存状态,此方法不做任何改变。

    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    	var ctx = canvas.getContext("2d");
    	ctx.save();
    	ctx.fillStyle="pink";
    	ctx.save();
    	// 没保存的不入栈
    	ctx.fillStyle="deeppink";
    	ctx.fillStyle="blue";
    	ctx.save();
    	ctx.fillStyle="red";
    	ctx.save();
    	ctx.fillStyle="green";
    	ctx.save();
    	ctx.beginPath();
    	// 弹出,restore四个是pink,三个是blue,二个是red,一个是green
    	ctx.restore();
    	ctx.restore();
    	ctx.restore();
    	ctx.restore();
    	ctx.fillRect(50,50,100,100);
    }

🔥电子签名实例

html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			*{
				margin: 0;
				padding: 0;
			}
			body{
				background: #dbd0ff;
			}
			#test{
				position: absolute;
				left: 0;
				right: 0;
				top: 0;
				bottom: 0;
				margin: auto;
				background:white;
			}
		</style>
	</head>
	<body>
		<canvas id="test" width="500" height="500"></canvas>
	</body>
	<script type="text/javascript">
		window.onload=function(){
			var canvas =document.getElementById("test");
			if(canvas.getContext){
				var ctx = canvas.getContext("2d");
			}
			// 在鼠标按下时触发事件
			canvas.onmousedown=function(ev){
				// 获取事件对象
				ev = ev || window.event;
				if(canvas.setCapture){
					// 设置鼠标捕获,确保鼠标事件在元素上持续触发
					canvas.setCapture();
				}
				ctx.beginPath();
				// ev.clientX表示鼠标相对于文档可视区域的水平坐标,canvas.offsetLeft表示画布相对于文档可视区域的水平偏移量
				ctx.moveTo(ev.clientX -canvas.offsetLeft,ev.clientY -canvas.offsetTop);
				document.onmousemove=function(ev){
					// 在整个文档区域内移动鼠标时触发事件
					// 保存画布当前的状态
					ctx.save();
					ctx.strokeStyle="black";
					ev = ev || event;
					// 创建从当前点到指定点的线段,即绘制直线
					ctx.lineTo(ev.clientX -canvas.offsetLeft,ev.clientY -canvas.offsetTop);
					// 绘制已定义的路径
					ctx.stroke();
					ctx.restore();
				}
				// 释放鼠标捕获
				document.onmouseup=function(){
					document.onmousemove=document.onmouseup=null;
					if(document.releaseCapture){
						document.releaseCapture();
					}
				}
				return false;
			}
		}
	</script>
</html>

📚绘制曲线

  • 角度与弧度的js表达式:radians=(Math.PI/180)*degrees

🐇canvas绘制圆形

  • arc(x, y, radius, startAngle, endAngle, anticlockwise)

    • 画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。ture:逆时针,false:顺时针
    • x,y为绘制圆弧所在圆上的圆心坐标,radius为半径
    • startAngle以及endAngle参数用弧度定义了开始以及结束的弧度。这些都是以x轴为基准参数anticlockwise 为一个布尔值。为true时,是逆时针方向,否则顺时针方向。
    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    	var ctx = canvas.getContext("2d");
    	ctx.beginPath();
    	ctx.moveTo(100,100);
    	ctx.arc(100,100,50,0,90*Math.PI/180,true);
    	ctx.closePath();
    	ctx.stroke();
    }

🐇arcTo

  • arcTo(x1, y1, x2, y2, radius),根据给定的控制点和半径画一段圆弧。

  • 相当于指定了起点和终点的切线(就是下边示例中,画出的直线就是在画弧线时的切线)

    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    	var ctx = canvas.getContext("2d");
    	ctx.strokeStyle="purple";
    	ctx.beginPath();
    	ctx.moveTo(50,50);
    	ctx.lineTo(300,0);
    	ctx.lineTo(200,200);
    	ctx.stroke();
    	
    	ctx.beginPath();
    	ctx.moveTo(50,50)
    	ctx.arcTo(300,0,200,200,50);
    	ctx.stroke();
    }

🐇二次贝塞尔

  • quadraticCurveTo(cp1x, cp1y, x, y)

    • 绘制二次贝塞尔曲线,cp1x,cp1y为一个控制点,x,y为结束点。
    • 起始点为moveto时指定的点。
    js 复制代码
    var ctx = canvas.getContext("2d");
    ctx.strokeStyle="purple";
    ctx.beginPath();
    ctx.moveTo(50,50);
    ctx.lineTo(300,0);
    ctx.lineTo(200,200);
    ctx.stroke();
    
    ctx.beginPath();
    ctx.moveTo(50,50)
    ctx.quadraticCurveTo(300,0,200,200);
    ctx.stroke();

🐇三次贝塞尔

  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

    • 绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。
    • 起始点为moveto时指定的点。
    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    	var ctx = canvas.getContext("2d");
    	ctx.beginPath();
    	ctx.moveTo(50,50);
    	ctx.lineTo(300,0);
    	ctx.lineTo(0,300);
    	ctx.lineTo(300,300);
    	ctx.stroke();
    	
    	ctx.strokeStyle="purple";
    	ctx.beginPath();
    	ctx.moveTo(50,50)
    	ctx.bezierCurveTo(300,0,0,300,300,300);
    	ctx.stroke();
    }

📚canvas中的变换

🐇translate(x, y)

  • 我们先介绍 translate 方法,它用来移动 canvas的原点到一个不同的位置。

  • translate 方法接受两个参数。x 是左右偏移量,y 是上下偏移量,在 canvas中translate是累加的。

    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    	var ctx = canvas.getContext("2d");
    	ctx.translate(100,100)
    	ctx.beginPath();
    	ctx.fillStyle="skyblue";
    	ctx.fillRect(0,0,100,100);
    }

🐇rotate(angle)

  • 这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。

  • 旋转的中心点始终是 canvas 的原点 ,如果要改变它,我们需要用到 translate 方法,在canvas中rotate是累加 的。

    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    	var ctx = canvas.getContext("2d");
    	ctx.translate(70,70)
    	ctx.rotate(22.1*Math.PI/180)
    	ctx.rotate(22.9*Math.PI/180)
    	ctx.beginPath();
    	ctx.fillStyle="skyblue";
    	ctx.fillRect(50,50,100,100);
    }

🐇scale(x, y)

  • scale 方法接受两个参数。x,y 分别是横轴和纵轴的缩放因子,它们都必须是正值。

  • 值比 1.0 小表示缩小,比 1.0 大则表示放大,值为 1.0 时什么效果都没有。

  • 缩放一般我们用它来增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大。

  • 在canvas中scale是累积的。

    js 复制代码
    var ctx = canvas.getContext("2d");
    ctx.fillStyle="skyblue";
    ctx.beginPath();
    ctx.fillRect(50,50,100,100);
    ctx.beginPath();
    ctx.fillStyle="green";
    ctx.scale(0.5,0.5)
    ctx.fillRect(50,50,100,100);
    ctx.beginPath();
    ctx.fillStyle="yellow";
    // 因为累加,相当于两倍
    ctx.scale(4,4)
    ctx.fillRect(50,50,100,100);

🔥放缩变换实例

html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			*{
				margin: 0;
				padding: 0;
			}
			html,body{
				height: 100%;
				background-color: #dbdbff;
				overflow: hidden;
			}
			#test{
				background: rgba(143, 143, 253, 0.785);
				position: absolute;
				left: 0;
				top: 0;
				right: 0;
				bottom: 0;
				margin: auto;
			}
		</style>
	</head>
	<body>
		<canvas id="test" width="300" height="300">
			<span>您的浏览器不支持画布元素 请您换成可爱的谷歌</span>
		</canvas>
	</body>
	<script type="text/javascript">
		window.onload=function(){
			var angle =0;//角度
			var scale = 0;//比例
			var angleScale = 0;//状态
			var canvas = document.querySelector("#test");
			if(canvas.getContext){
				var ctx = canvas.getContext("2d");
				ctx.fillStyle="rgba(10, 10, 79, 0.785)";
				ctx.save();
				// 将坐标原点平移到150,150
				ctx.translate(150,150);
				
				ctx.beginPath();
				ctx.fillRect(-50,-50,100,100);
				ctx.restore();
				
				// 设定定时器函数,每隔10毫秒执行一次
				setInterval(function(){
					// 每次执行时,将旋转角度标记加1
					angle++;
					ctx.clearRect(0,0,canvas.width,canvas.height);
					ctx.save();
					ctx.translate(150,150);
					// 旋转
					ctx.rotate(angle*Math.PI/180);
					
					// 0~100之间缩放
					if(scale==100){
						angleScale=-1;
					}else if(scale==0){
						angleScale=1;
					}
					scale+=angleScale;
					// 缩放
					ctx.scale(scale/50,scale/50);
					ctx.beginPath();
					ctx.fillRect(-50,-50,100,100);
					ctx.restore();
				},10)
			}
		}
	</script>
</html>

🔥画一个电子钟表

html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			*{
				margin: 0;
				padding: 0;
			}
			html,body{
				height: 100%;
				overflow: hidden;
				background: rgb(197, 197, 255);
			}
			#clock{
				background: rgb(167, 208, 255);
				position: absolute;
				left: 50%;
				top: 50%;
				transform: translate3d(-50%,-50%,0);
			}
		</style>
	</head>
	<body>
		<canvas id="clock" width="400" height="400"></canvas>
	</body>
	<script type="text/javascript">
		window.onload=function(){
	var clock = document.querySelector("#clock"); 
	if(clock.getContext){ 
		var ctx = clock.getContext("2d"); 
		// 定时器函数,每隔1秒执行一次
		setInterval(function(){ 
			// 清除整个画布的内容
			ctx.clearRect(0,0,clock.width,clock.height); 
			// 调用move函数
			move(); 
		},1000);
	
		move(); 
		// move函数,用于绘制时钟
		function move(){ 
			ctx.save(); 
			ctx.lineWidth = 8; 
		  	ctx.strokeStyle = "black"; 
			// 设置线条末端的形状为圆形
		  	ctx.lineCap = "round"; 
            // 将坐标原点平移到(200, 200)的位置
			ctx.translate(200,200); 
			// 对画布进行旋转操作,旋转的角度为-90°,即将角度转换为弧度单位,并逆时针旋转
			ctx.rotate(-90*Math.PI/180); 
			
			ctx.beginPath(); 
            
			// 外层空心圆盘
			ctx.save(); 
			ctx.strokeStyle="#325FA2"; 
			ctx.lineWidth = 14;
			
			ctx.beginPath();
			// 绘制一个从0°到360°的弧线,即画一个圆
			ctx.arc(0,0,140,0,360*Math.PI/180); 
			ctx.stroke(); // 绘制已定义的路径
			ctx.restore(); // 恢复之前保存的画布状态
			
			// 时针刻度
			ctx.save(); 
			// 循环绘制12个时针刻度
			for(var i=0;i<12;i++){ 
				// 逆时针旋转30°,即将角度转换为弧度单位
				ctx.rotate(30*Math.PI/180); 
				ctx.beginPath(); 
				ctx.moveTo(100,0) 
				ctx.lineTo(120,0); 
				ctx.stroke(); 
			}
			ctx.restore(); 
			
			// 分针刻度
			ctx.save(); 
			ctx.lineWidth=4; 
			// 循环绘制60个分针刻度
			for(var i=0;i<60;i++){ 
				// 逆时针旋转6°,即将角度转换为弧度单位
				ctx.rotate(6*Math.PI/180); 
				// 如果不是整点刻度,则绘制
				if((i+1)%5!=0){ 
					ctx.beginPath();
					ctx.moveTo(117,0) 
					ctx.lineTo(120,0); 
					ctx.stroke(); 
				}
			}
			ctx.restore(); 
			
			// 时针、分针、秒针、表座
			// 创建一个Date对象,获取当前时间
			var date = new Date(); 
			// 获取当前的秒数
			var s = date.getSeconds(); 
			// 获取当前的分钟数,并加上秒数的部分
			var m = date.getMinutes()+s/60; 
			// 获取当前的小时数,并加上分钟数的部分
			var h = date.getHours()+m/60; 
			// 如果小时数大于12,则减去12
			h = h>12?h-12:h; 
			
			// 时针
			ctx.save() 
			ctx.lineWidth=14; 
			// 逆时针旋转小时数乘以30°,即将角度转换为弧度单位
			ctx.rotate(h*30*Math.PI/180) 
			ctx.beginPath() 
			ctx.moveTo(-20,0); 
			ctx.lineTo(80,0);
			ctx.stroke(); 
			ctx.restore() 
			
			// 分针
			ctx.save() 
			ctx.lineWidth=10; 
			// 逆时针旋转分钟数乘以6°,即将角度转换为弧度单位
			ctx.rotate(m*6*Math.PI/180) 
			ctx.beginPath() 
			ctx.moveTo(-28,0); 
			ctx.lineTo(112,0); 
			ctx.stroke(); 
			ctx.restore() 
			
			// 秒针
			ctx.save() 
			ctx.lineWidth=6; 
			ctx.strokeStyle="#D40000"; 
			ctx.fillStyle="#D40000"; 
			// 逆时针旋转秒数乘以6°,即将角度转换为弧度单位
			ctx.rotate(s*6*Math.PI/180) 
			ctx.beginPath(); 
			ctx.moveTo(-30,0); 
			ctx.lineTo(83,0); 
			ctx.stroke(); 
			
			// 表座
			ctx.beginPath(); 
			ctx.arc(0,0,10,0,360*Math.PI/180); 
			ctx.fill(); 
			
			// 秒针头
			ctx.beginPath(); 
			ctx.arc(96,0,10,0,360*Math.PI/180);
			ctx.stroke(); 
			ctx.restore() 
			ctx.restore(); 
		}
	}
}
	</script>
</html>

📚图片

🐇插入图片(需要image对象)

  • canvas操作图片时,必须要等图片加载完才能操作

  • drawImage(image, x, y, width, height):其中 image 是 image 或者 canvas 对象,x 和 y 是其在目标 canvas 里的起始坐标。这个方法多了2个参数:width 和 height,这两个参数用来控制 当像canvas画入时应该缩放的大小

    js 复制代码
    var ctx = canvas.getContext("2d");
    var img = new Image();
    img.src="tg.png";
    img.onload=function(){
    	draw();
    }
    function draw(){
    	ctx.drawImage(img,0,0,img.width,img.height)
    }

🐇设置背景(需要image对象)

  • createPattern(image, repetition)

  • image:图像源

  • epetition:"repeat" 、"repeat-x" 、"repeat-y" 、"no-repeat"

  • 一般情况下,我们都会将createPattern返回的对象作为fillstyle的值

    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    		var ctx = canvas.getContext("2d");
    		var img = new Image();
    		img.src="tg.png";
    		img.onload=function(){
    			draw();
    		}
    		function draw(){
    			var pattern = ctx.createPattern(img,"no-repeat")
    			ctx.fillStyle=pattern;
    			ctx.fillRect(0,0,300,300);
    		}	
    	}

🐇渐变

  • 线性渐变:createLinearGradient(x1, y1, x2, y2),表示渐变的起点 (x1,y1) 与终点 (x2,y2)

  • gradient.addColorStop(position, color)

    • gradient :createLinearGradient的返回值
    • addColorStop 方法接受 2 个参数,position 参数必须是一个 0.0 与 1.0 之间的数值,表示渐变中颜色所在的相对位置。例如,0.5 表示颜色会出现在正中间。
    • color 参数必须是一个有效的 CSS 颜色值(如 #FFF, rgba(0,0,0,1),等等)。
    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    	var ctx = canvas.getContext("2d");
    	var gradient =  ctx.createLinearGradient(0, 0, 200, 200);
    	gradient.addColorStop(0,"red");
    	gradient.addColorStop(0.5,"yellow");
    	gradient.addColorStop(0.7,"pink");
    	gradient.addColorStop(1,"green");
    	ctx.fillStyle=gradient;
    	ctx.fillRect(0,0,300,300);
    }

  • 径向渐变:createRadialGradient(x1, y1, r1, x2, y2, r2)

    • 前三个参数则定义另一个以(x1,y1) 为原点,半径为 r1 的圆
    • 后三个参数则定义另一个以 (x2,y2) 为原点,半径为 r2 的圆
    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    	var ctx = canvas.getContext("2d");
    	var gradient = ctx.createRadialGradient(150, 150, 50, 150, 150, 100)
    	gradient.addColorStop(0,"red");
    	gradient.addColorStop(0.5,"yellow");
    	gradient.addColorStop(0.7,"pink");
    	gradient.addColorStop(1,"green");
    	ctx.fillStyle=gradient;
    	ctx.fillRect(0,0,300,300);	
    }

🔥图片变动画

html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			*{
				margin: 0;
				padding: 0;
			}
			html,body{
				height: 100%;
				overflow: hidden;
			}
			#test{
				background: white;
				position: absolute;
				left: 0;
				top: 0;
				right: 0;
				bottom: 0;
				margin: auto;
			}
		</style>
	</head>
	<body>
		<canvas id="test" width="300" height="300"></canvas>
	</body>
	<script type="text/javascript">
		window.onload=function(){
			var canvas = document.querySelector("#test");
			canvas.width = document.documentElement.clientWidth;
			canvas.height = document.documentElement.clientHeight;
			if(canvas.getContext){
				var ctx = canvas.getContext("2d");
				// 用于标记当前显示的图片序号
				var flag = 0;
				// 用于控制图片在x轴的偏移量
				var value=0;
				setInterval(function(){
					ctx.clearRect(0,0,canvas.width,canvas.height)
					value+=10;
					flag++;
					if(flag==9){
						// 当图片序号达到9时,重新设置为1
						flag=1;
					}
					var img = new Image();
					img.src="img/q_r"+(flag)+".jpg";
					img.onload=function(){
						draw(this);
					}	
				},100)
				function draw(img){
					ctx.drawImage(img,value,0)
				}
			}
		}	
	</script>
</html>

📚文本

🐇绘制文本

  • canvas 提供了两种方法来渲染文本:

    • fillText(text, x, y):在指定的(x,y)位置填充指定的文本
    • strokeText(text, x, y):在指定的(x,y)位置绘制文本边框
    js 复制代码
    var canvas = document.querySelector("#test");
    if(canvas.getContext){
    	var ctx = canvas.getContext("2d");
    	ctx.fillStyle="skyblue"
    	ctx.font="40px sans-serif"
    	ctx.fillText("lalayouyi",70,150);
    	ctx.strokeText("lalayouyi",70,150);
    }

🐇文本样式

  • font = value

    • 当前我们用来绘制文本的样式,这个字符串使用和 CSS font 属性相同的语法。
    • 默认的字体是 10px sans-serif。
    • font属性在指定时,必须要有。大小和字体 缺一不可
  • textAlign = value:文本对齐选项。可选的值包括: left, right center.

  • textBaseline = value:描述绘制文本时,当前文本基线的属性。

  • measureText() 方法返回一个 TextMetrics 对象,包含关于文本尺寸的信息(例如文本的宽度)

  • canvas中文本水平垂直居中

    js 复制代码
    var oC =document.getElementById('c1');
    var oGC = oC.getContext('2d');
    oGC.font = '60px impact';
    oGC.textAlign = 'left';
    oGC.textBaseline = 'middle'; 
    var w = oGC.measureText('lalayouyi').width;
    oGC.fillText('lalayouyi',(oC.width - w)/2 , (oC.height - 60)/2);

🐇阴影(文本阴影&盒模型阴影)

  • shadowOffsetX = float:shadowOffsetX 和 shadowOffsetY 用来设定阴影在 X 和 Y 轴的延伸距离,它们默认都为 0。

  • shadowOffsetY = float:shadowOffsetX 和 shadowOffsetY 用来设定阴影在 X 和 Y 轴的延伸距离,它们默认都为 0。

  • shadowBlur = float:shadowBlur 用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为 0。

  • shadowColor = color(必需项):shadowColor 是标准的 CSS 颜色值,用于设定阴影颜色效果,默认是全透明的黑色。

    js 复制代码
    var oC =document.getElementById('c1');
    var oGC = oC.getContext('2d');
    oGC.font = '60px impact';
    oGC.textAlign = 'left';
    oGC.textBaseline = 'middle'; 
    var w = oGC.measureText('lalayouyi').width;
    //文本阴影&盒阴影
    oGC.shadowOffsetX = 20;
    oGC.shadowOffsetY = 20;
    oGC.shadowBlur = 30;
    oGC.shadowColor = "yellow";
    oGC.fillText('lalayouyi',(oC.width - w)/2 , (oC.height - 60)/2);

📚像素操作

到目前为止,我们尚未深入了解Canvas画布真实像素的原理,事实上,你可以直接通过ImageData对象操纵像素数据,直接读取或将数据数组写入该对象中。

🐇得到像素数据

  • getImageData():获得一个包含画布场景像素数据的ImageData对像,它代表了画布区域的对象数据
  • ctx.getImageData(sx, sy, sw, sh)
    • sx:将要被提取的图像数据矩形区域的左上角 x 坐标。
    • sy:将要被提取的图像数据矩形区域的左上角 y 坐标。
    • sw:将要被提取的图像数据矩形区域的宽度。
    • sh:将要被提取的图像数据矩形区域的高度。
  • ImageData对象中存储着canvas对象真实的像素数据,它包含以下几个只读属性:
    • width:图片宽度,单位是像素
    • height:图片高度,单位是像素
    • data:Uint8ClampedArray类型的一维数组

  • putImageData()方法去对场景进行像素数据的写入。

  • putImageData(myImageData, dx, dy):dx和dy参数表示你希望在场景内左上角绘制的像素数据所得到的设备坐标。

  • ctx.createImageData(width, height):创建一个ImageData对象,默认创建出来的是透明的。

    js 复制代码
    var ctx = canvas.getContext("2d");
    // 设置填充样式为"rgba(255, 192, 203,1)"
    ctx.fillStyle="rgba(255, 192, 203,1)";
    ctx.fillRect(0,0,100,100);
    // 获取矩形区域(0, 0, 100, 100)内的图像数据
    var imageData = ctx.getImageData(0,0,100,100);
    // var imageData = ctx.createImageData(100,100);
    // 透明度即为数组 ​imageData.data​中下标为4i+3的值(其中i表示像素点的索引)。
    // 将每个像素点的透明度设置为100,即不完全透明状态。
    for(var i=0;i<imageData.data.length;i++){
    	imageData.data[4*i+3]=100;
    }
    // 重新绘制
    ctx.putImageData(imageData,0,0)

🔥马赛克实例

  • 流程

    • 创建一个Image对象,并将其src属性设置为图片路径。
    • 在图片加载完成后,设置canvas的宽度为图片宽度的两倍,高度为图片高度。调用draw函数绘制原始图片。
    • 在draw函数中
      • 首先使用ctx.drawImage方法绘制原始图片。
      • 使用ctx.getImageData方法获取原始图片的图像数据,并保存在oldImgdata变量中。
      • 使用ctx.createImageData方法创建新的图像数据,并保存在newImgdata变量中。
      • 定义size变量为5,表示每个小方块的大小为5像素。
      • 使用嵌套的循环遍历每个小方块内的像素点,随机获取小方块内某个像素点的颜色信息,并将该颜色信息应用于整个小方块内的像素。
      • 清除画布内容。
      • 使用ctx.putImageData方法将修改后的新图像数据绘制到画布上。
  • 通过获取和设置图像数据的像素颜色信息,将原始图片划分为小块,并在每个小块中随机选择一个像素点的颜色信息,并应用于整个小块内的所有像素点,从而实现像素化的效果。

    html 复制代码
    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="UTF-8">
    		<title></title>
    		<style type="text/css">
    			*{
    				margin: 0;
    				padding: 0;
    			}
    			html,body{
    				height: 100%;
    				overflow: hidden;
    			}
    			#msk{
    				position: absolute;
    				left: 50%;
    				top: 50%;
    				transform: translate3d(-50%,-50%,0);
    			}
    		</style>
    	</head>
    	<body>
    		<canvas id="msk" ></canvas>
    	</body>
    	<script type="text/javascript">
    		var oc = document.querySelector("#msk");
    		if(oc.getContext){
    			var ctx = oc.getContext("2d");
    			var img = new Image();
    			img.src="2.png";
    			img.onload=function(){
    				oc.width=img.width*2;
    				oc.height=img.height;
    				draw();
    			}
    			
    			function draw(){
    				ctx.drawImage(img,0,0);
    				var oldImgdata = ctx.getImageData(0,0,img.width,img.height);
    				var newImgdata = ctx.createImageData(img.width,img.height);
    				// 定义每个小方块的大小为5像素
    				var size = 5;
    				for(var i=0;i<oldImgdata.width/size;i++){
    					for(var j=0;j<oldImgdata.height/size;j++){
    						// 获取当前小方块内某个像素的颜色信息
    						var color = getPxInfo(oldImgdata,i*size+Math.floor(Math.random()*size),j*size+Math.floor(Math.random()*size));
    						for(var a=0;a<size;a++){
    							for(var b=0;b<size;b++){
    								// 设置新图像数据中的像素颜色信息
    								setPxInfo(newImgdata,i*size+a,j*size+b,color)
    							}
    						}
    					}
    				}
    				ctx.clearRect(0,0,oc.width,oc.height);
    				ctx.putImageData(newImgdata,0,0);
    			}
    
    			// 获取指定位置像素的颜色信息
    			function getPxInfo(imgdata,x,y){
    				var color = [];
    				var data = imgdata.data;
    				var w = imgdata.width;
    				var h = imgdata.height;
    				// 获取像素点的红色值
    				color[0]=data[(y*w+x)*4];
    				// 获取像素点的绿色值
    				color[1]=data[(y*w+x)*4+1];
    				// 获取像素点的蓝色值
    				color[2]=data[(y*w+x)*4+2];
    				// 获取像素点的透明度值
    				color[3]=data[(y*w+x)*4+3];
    				// 返回颜色信息数组
    				return color;
    			}
    			
    			// 设置指定位置像素的颜色信息
    			function setPxInfo(imgdata,x,y,color){
    				var data = imgdata.data;
    				var w = imgdata.width;
    				var h = imgdata.height;
    				data[(y*w+x)*4]=color[0];
    				data[(y*w+x)*4+1]=color[1];
    				data[(y*w+x)*4+2]=color[2];
    				data[(y*w+x)*4+3]=color[3];
    			}
    		}
    	</script>
    </html>

📚合成

🐇全局透明度的设置

globalAlpha = value。这个属性影响到 canvas 里所有图形的透明度,有效的值范围是 0.0 (完全透明)到 1.0(完全不透明),默认是 1.0。

js 复制代码
var ctx = canvas.getContext("2d");
ctx.fillStyle="red";
ctx.globalAlpha=.4;
ctx.fillRect(0,0,100,100);
ctx.fillRect(100,100,100,100);

🐇合成

globalCompositeOperation属性用于设置绘制图形时的组合操作方式。组合操作可以理解为将已有的图形和新绘制的图形进行合并或者覆盖的方式。该属性的值可以是以下几种:

  • "source-over"(默认值):新绘制的图形覆盖在已有图形之上。这是最常用的组合操作方式。

  • "source-in":新绘制的图形只显示于已有图形的重叠部分,其他部分不可见。

  • "source-out":新绘制的图形只显示于已有图形之外的部分,与已有图形重叠的部分不可见。

  • "source-atop":新绘制的图形显示于已有图形上方的重叠部分,与已有图形下方的部分不可见。

  • "destination-over":已有的图形覆盖在新绘制的图形之上。

  • "destination-in":已有的图形只显示于新绘制图形的重叠部分,其他部分不可见。

  • "destination-out":已有的图形只显示于新绘制图形之外的部分,与新绘制图形重叠的部分不可见。

  • "destination-atop":已有的图形显示于新绘制图形上方的重叠部分,与新绘制图形下方的部分不可见。

  • "lighter":将已有的图形和新绘制的图形颜色混合,得到更亮的颜色。

  • "copy":新绘制的图形完全替换已有的图形。

  • "xor":新绘制的图形与已有的图形进行异或运算,产生一种类似于橡皮擦的效果。

js 复制代码
var ctx = canvas.getContext("2d");
ctx.fillStyle="pink";
ctx.fillRect(50,50,100,100);
// 控制新绘制的图形如何与已有的图形进行组合。
// 源图形仅会在与目标图形重叠的区域内显示,并且会覆盖目标图形,而非重叠区域将保持目标图形原有的样式。
ctx.globalCompositeOperation="destination-atop";
ctx.fillStyle="green";
ctx.fillRect(100,100,100,100);

🔥刮刮卡实例

html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"></meta>
		<title></title>
		<style type="text/css">
			*{
				margin: 0;
				padding: 0;
			}
			html,body{
				height: 100%;
				overflow: hidden;
			}
			#wrap,ul,ul>li{
				height: 100%;
			}
			ul>li{
				background: url(img/b.png);
				background-size:100% 100% ;
			}
			canvas{
				position: absolute;
				left: 0;
				top: 0;
				transition:1s;
			}
		</style>
	</head>
	<body>
		<div id="wrap">
			 <!-- 画布 -->
			<canvas></canvas>
			<ul>
				<li></li>
			</ul>
		</div>
	</body>
	
	<script type="text/javascript">
		window.onload = function() {
    		var canvas = document.querySelector("canvas"); 
			canvas.width = document.documentElement.clientWidth;
			canvas.height = document.documentElement.clientHeight;
			if (canvas.getContext) {
				var ctx = canvas.getContext("2d"); 
				var img = new Image();
				img.src = "img/a.png"; 
				img.onload = function() { 
					draw(); 
				}

				//绘制画布
				function draw() {
					// 记录透明像素点的数量
					var flag = 0; 
					// 将图片绘制到画布上
					ctx.drawImage(img, 0, 0, canvas.width, canvas.height); 
					// 判断是否正在绘制路径(使得刮刮乐更平滑)
					var isDrawing = false; 
					var lastX = 0; // 上一个绘制点的水平坐标
					var lastY = 0; // 上一个绘制点的垂直坐标
					canvas.addEventListener("mousedown", startDrawing); 
					canvas.addEventListener("mousemove", drawPath); 
					canvas.addEventListener("mouseup", stopDrawing); 

					/**
					 * 开始绘制路径
					 * @param {MouseEvent} ev 鼠标按下事件
					 */
					function startDrawing(ev) {
						isDrawing = true; 
						// 获取当前鼠标位置的水平坐标
						var x = ev.clientX - canvas.offsetLeft; 
						// 获取当前鼠标位置的垂直坐标
						var y = ev.clientY - canvas.offsetTop; 
						// 记录起始坐标
						[lastX, lastY] = [x, y]; 
					}

					/**
					 * 绘制路径
					 * @param {MouseEvent} ev 鼠标移动事件
					 */
					function drawPath(ev) {
						if (!isDrawing) return; 
						var x = ev.clientX - canvas.offsetLeft; 
						var y = ev.clientY - canvas.offsetTop; 
						// 设置绘制模式为"目标图像透明部分显示出来"
						ctx.globalCompositeOperation = "destination-out";
						ctx.lineWidth = 40; 
						ctx.lineCap = "round"; 
						ctx.lineJoin = "round"; 
						ctx.save(); 
						ctx.beginPath(); 
						ctx.moveTo(lastX, lastY); 
						ctx.lineTo(x, y); 
						ctx.stroke(); 
						ctx.restore(); 
						[lastX, lastY] = [x, y]; 
					}

					/**
					 * 停止绘制路径
					 */
					function stopDrawing() {
						isDrawing = false; 
						var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
						// 计算像素总数
						var allPx = imgData.width * imgData.height; 
						for (var i = 0; i < allPx; i++) {
							// 判断像素是否透明
							if (imgData.data[4 * i + 3] === 0) { 
								flag++; 
							}
						}
						// 如果透明像素点数量超过一半,则将画布透明度设置为0(就认为已经刮得差不多
						if (flag >= allPx / 2) { 
							canvas.style.opacity = 0;
						}
					}
					// 监听CSS过渡效果结束事件
					canvas.addEventListener("transitionend", function() { 
						this.remove(); // 移除画布元素
					})
				}
			}
		}
	</script>
</html>
相关推荐
Rverdoser1 小时前
unocss 一直热更新打印[vite] hot updated: /__uno.css
前端·css
Bang邦1 小时前
使用nvm管理Node.js多版本
前端·node.js·node多版本管理
podoor1 小时前
wordpress不同网站 调用同一数据表
前端·wordpress
LJ小番茄1 小时前
Vue 常见的几种通信方式(总结)
前端·javascript·vue.js·html
黑狼传说2 小时前
前端项目优化:极致最优 vs 相对最优 —— 深入探索与实践
前端·性能优化
장숙혜2 小时前
前端-CDN的理解及CDN一些使用平台
前端
FakeOccupational3 小时前
nodejs 007:错误npm error Error: EPERM: operation not permitted, symlink
前端·npm·node.js
奶糖 肥晨3 小时前
react是什么?
前端·react.js·前端框架
亦舒.3 小时前
JSDelivr & NPM CDN 国内加速节点
前端·npm·node.js
代码搬运媛3 小时前
code eintegrity npm err sha512
前端·npm·node.js