在浏览器上画图(canvas的基本用法)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Tank_in_the_street/article/details/78161103

        在HTML5里,我们可以通过canvas标签来在浏览器里进行画图,但是这个标签并不是能画图的,画图还是要通过JavaScript,这个标签只是一个载体。在canvas这个标签里,默认的width是300,、height是150,想要设置canvas的长宽的话需要在canvas的width属性和height属性那里设置,这个width和height不属于style里的,搞清楚这一点很重要,所以当你在css里设置width和height的时候,你会发现你不仅把canvas变长或者变宽了,并且连里面的内容也随着放大或缩小。如果你在canvas标签里面设置了width和height的话,仅仅只是改变长宽,并不影响里面图像的大小。详情可见我写的另一篇文章:点击打开链接

        canvas标签在IE浏览器里只有IE9之前的不支持,那么对于这些不支持的浏览器,我们可以在canvas标签里写入一些内容,当浏览器不支持的时候会显示这些内容,如果浏览器支持的话,则会正常渲染canvas而忽略掉这些内容:

<canvas>
    hahaha
</canvas>
<canvas>
    <img src='a.png' />
</canvas>

        在这里,如果你的浏览器支持canvas的话,那么canvas内的内容则不会显示,会正常的渲染canvas,如果不支持的话,则会显示上面的文字或者图片。

        下面我来讲一下canvas比较常用的几个方法。第一个是getContext()方法。这个方法是用来渲染上下文和它的绘画功能,在这个方法里只有一个参数,这个参数值目前来说只有2d,即:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
        在这里,getContext('2d')通过调用CanvasRenderingContext2D接口,来实现下面的一系列的操作。getContext()这个方法在实现canvas画图的时候是必不可少的。关于这个参数值2d,它的意思是绘画2d图像,在未来版本里,可能会多加一个3d的参数值,到时候再详讲3d。

        fillStyle()方法也是用的比较多的,它用于设置或返回用于填充绘画的颜色、渐变或模式,有三种不同的属性值,它的定义和用法如何下图所示:


        最常用的就是给一个矩形填充颜色,如:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
cxt.fillStyle = '#FF0000'
cxt.fillRect(20, 20, 150, 100)

        或者是线性渐变,如:
var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
var grd = cxt.createLinearGradient(0, 0, 175, 50)
grd.addColorStop(0, 'red')
grd.addColorStop(1, 'blue')
cxt.fillStyle = grd
cxt.fillRect(0, 0, 175, 50)

        还有就是填充,如:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
var img = new Image()
img.src = 'file:///d:/桌面/aa.png'
var pat = cxt.createPattern(img, 'repeat')
cxt.rect(0, 0, 400, 400)
cxt.fillStyle = pat
cxt.fill()

        在上面的代码中,我们又看见了一些新的方法,如createLinearGradient(x0,y0,x1,y1)方法,这个方法定义的是一个线性渐变,这个方法是作为fillStyle或者strokeStyle的值的。而addColorStop(stop,color)方法则是与createLinearGradient()相对应的方法,它表示的是gradient对象中的颜色和位置。它有两个参数stop参数介于0.0到1.0之间,表示渐变中开始与结束之间的位置,而color表示结束位置的颜色值。

        createPattern(image,“repeat|repeat-x|repeat-y|no-repeat”)表示的是指定方向上重复的图像,参数值repeat表示在水平和垂直方向上重复图像,如此类推。

        在继续讲下去之前,先提一下带有fill和stroke前缀的方法,带有fill前缀的如fillStyle、fillRect等方法,fill前缀表示的是canvas这个画笔画出来的图像进行填充,而stroke表示的是canvas这个画笔画出来的图像的边框进行着色,在看canvas的方法的时候你会发现很多带有fill和stroke前缀的方法,理解好这个就不怕记不住了。

        在我们的canvas元素里,是以像素为单位的,以左上角为起点。在canvas里,仅支持原生的图形绘制:矩形。其他的图形绘制都至少生成一条路径。在绘制矩形中,有四种方法:

        1.rect(x,y,width,height)表示的是创建一个矩形,由于没有填充和对边框进行着色,这个矩形对人眼来说是不可见的,想让它可见可以在后面添加stroke()或者fill()方法

        2.fillRect(x,y,width,height)表示的是绘制并填充一个矩形

        3.strokeRect(x,y,width,height)表示的是绘制并给矩形边框着色

        4.clearRect(x,y,width,height)表示的是清除指定矩形区域,让清除部分完全透明。

        下面是上面四个方法的综合例子:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
cxt.rect(100,100,600,600)
cxt.stroke()
cxt.fillRect(150,150,500,500)
cxt.clearRect(200,200,400,400)
cxt.strokeRect(250,250,300,300)
cxt.rect(300,300,200,200)
cxt.stroke()
        除了用原生来画矩形外,我们可以用路径来画矩形,设置可以画多边形。在这里,介绍要用到的六种方法:

        1.beginPath()指的是新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。

        2.closePath()指的是闭合路径之后图形绘制命令又重新指向到上下文中。

        3.stroke()指的是通过线条来绘制图形轮廓。

        4.fill()指的是通过填充路径的内容区域生成实心的图形。

        5.moveTo(x,y)指的是定义开始画图时点位置

        6.lineTo(x,y)指的是画一个点

        在通常情况下,绘制路径时的第一个点是需要moveTo()方法来声明的,而后的各条线的路径通过lineTo()这个方法来绘制其他线,通过closePath()方法来将最后一个点和第一个点来闭合。通过beginPath()来新建或处置一条路径。在fill()方法中,不管你有没有用closePath()方法将点线闭合,它都会自动的闭合然后填充,但是strike()方法则不会自动的将点线闭合。下面是运用上面例子来画了两个图形,一个三角形,一个五边形:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
cxt.beginPath()
cxt.moveTo(10,150)
cxt.lineTo(60,150)
cxt.lineTo(60, 280)
//cxt.closePath()
cxt.fill()

cxt.beginPath()
cxt.moveTo(10, 10)
cxt.lineTo(110, 10)
cxt.lineTo(110, 30)
cxt.lineTo(120, 40)
cxt.lineTo(110, 50)
cxt.lineTo(110, 110)
cxt.lineTo(10, 110)
cxt.closePath()
cxt.stroke()

        直来直往的图像比较简单,下面来讲一下弯的图形如何来弄。曲线有四种方法可以弄,分别是是arc(x,y,radius,startAngle,endAngle,anticlockwise)、arcTo(x1,y1,x2,y2,radius)、quadraticCurveTo(cp1x,cp1y,x,y)和bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)前两个方法都是圆弧的画法,红面两个是贝赛尔曲线一个是二次一个是三次。圆弧的第一个方法里的x和y表示的是圆心的坐标,startAngle和endAngle表示的开始的角和结束的角,这里的角度是以弧度为单位,radius表示的是半径而anticlokwise表示的顺时针还是逆时针,true表示逆时针,false表示顺时针,false为默认值。另一个圆弧的方法里x1,y1,x2,y2表示的是起始点的坐标和结束点的坐标。在二次贝赛尔曲线中,cp1x和cp1y表示的是控制点的坐标,x和y表示的是结束点的坐标。而在三次贝赛尔曲线里,cp1x,cp1y,cp2x,cp2y表示的是两个控制点的坐标。

        arc()方法对比arcTo()方法来讲,arc方法比较简单,arcTo()方法的用法比较复杂,并且arcTo()方法只能画圆弧而不能画圆,因为arcTo方法利用了前端点、前端点1(x1,y1)、前端点2(x2,y2)这三个点来确定一个夹角,然后通过这个夹角和半径来绘制一个与夹角相切的圆弧,所以arcTo()方法比较复杂。具体两个用法如下:

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(100,20);
ctx.arcTo(150,20,150,70,50);
// ctx.arc(100,70,50,3*Math.PI/2,2*Math.PI)
ctx.lineTo(150,120);
ctx.stroke();
        二次贝赛尔曲线用法如下:
<body>
		<canvas id='a' width='300' height='300'></canvas>
		<button onclick='cha(0)' id='aa'>-x</button>
		<button onclick='cha(1)' id='ab'>+x</button>
		<button onclick='cha(2)' id='ac'>-y</button>
		<button onclick='cha(3)' id='ad'>+y</button>
	</body>
	<script type="text/javascript">
		var a = document.getElementById('a')
		var cxt = a.getContext('2d')
		var x = 20
		var y = 20
		createQC()
		function createQC(){
			cxt.beginPath()
			cxt.moveTo(20, 20)
			cxt.quadraticCurveTo(x,y,200,20)
			cxt.stroke()
			console.log(x+" "+y)
		}
		function clearQC(){
			cxt.clearRect(0,0,300,300)
		}
		function del(eve) {
			if (eve == 2) {
				if (y > 0) {
					y = y - 1
					clearQC()
					createQC()
				}
			}
			else if (eve == 0) {
				if (x > 0) {
					x = x - 1
					clearQC()
					createQC()
				}
			}
			else if (eve ==3) {
				if (y < 300) {
					y = y + 1
					clearQC()
					createQC()
				}
			}
			else if (eve == 1) {
				if (x < 300) {
					x = x + 1
					clearQC()
					createQC()
				}
			}
		}
	</script>

        三次贝塞尔曲线绘制心形:

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    //三次贝塞尔曲线
    ctx.beginPath();
    ctx.moveTo(75,40);
    ctx.bezierCurveTo(75,37,70,25,50,25);
    ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
    ctx.bezierCurveTo(20,80,40,102,75,120);
    ctx.bezierCurveTo(110,102,130,80,130,62.5);
    ctx.bezierCurveTo(130,62.5,130,25,100,25);
    ctx.bezierCurveTo(85,25,75,37,75,40);
    ctx.fill();
  }
}

        在HTML5里,有一个Path2D对象是专门用来存储我们弄好的图形,具体用法如下;

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    var rectangle = new Path2D();
    rectangle.rect(10, 10, 50, 50);

    var circle = new Path2D();
    circle.moveTo(125, 35);
    circle.arc(100, 35, 25, 0, 2 * Math.PI);

    ctx.stroke(rectangle);
    ctx.fill(circle);
  }
}

        上面讲了这些内容都只是绘制图形而已,到涉及到给图形着色的时候,需要用到fillStyle和strokeStyle这两个方法,fillStyle方法是用于给图形填充颜色,而strokeStyle方法是用于给图形的轮廓着色。一旦给strokeStyle或者fillStyle赋值了,那么后面新绘制的图像的颜色就是strokeStyle或者fillStyle设置的值,如果想要给每个图形赋予不同的颜色,则需要重新设置fillSty或者strokeStyle了。

        透明的话可以运用globalAlpha方法或者利用css3的rgba属性。globalAlpha的取值范围是0.0到1.0。运用这两个和上面的fillStyle方法代码如下:

function draw(){
			var ctx = document.getElementById('myCanvas').getContext('2d')
			ctx.fillStyle = 'red'
			ctx.fillRect(40,40,200,200)
			ctx.fillStyle = 'white'
			
			// ctx.globalAlpha = 0.3
			// ctx.beginPath()
			// ctx.arc(140, 140, 100, 0, Math.PI * 2, true)
			// ctx.fill()

			ctx.beginPath()
			ctx.fillStyle = 'rgba(255,255,255,0.3)'
			ctx.arc(140, 140, 90, 0, Math.PI * 2, true)
			ctx.fill()
		}
draw()

        在画布里,可以通过fillText(text,x,y[,maxWidth])和strokeText(text,x,y[,maxWidth])方法来绘制文本。在这些方法里,text表示的是文本,x和y指定开始的位置,maxWidth指的是绘制的最大宽度。详细代码如下所示:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
cxt.font = '30px serif'

cxt.fillStyle = 'green'
cxt.fillText('Hello world', 10, 30)

cxt.strokeStyle = 'red'
cxt.strokeText('Hello world', 10, 60) 

        我们除了可以绘制点线面和填充颜色和添加文字外,也可以添加图片。在canvas里,添加图片有三种不同的方法:第一种就是最普通的方法drawImage(image,x,y),这里的x和y表示目标的起始坐标,image表示添加哪副图像。第二种方法是drawImage(image,x,y,width,height),这种用于对图片的缩放,width和height指的是图片的大小。第三种方法是drawImage(image,sx,sy,sWidth,sHeight,dx,dy,dWidth,dHeight),这种方法用于切片,该方法前五个参数和第二种方法的参数一样,后面四个参数表示的是定义切片的目标明显位置和大小。下面的例子就是第二种和第三种的用法:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
		
var img1 = new Image()
// img1.src = 'file:///d:/桌面/pig.jpg'
img1.src = 'http://pic.wenwo.com/fimg/513558924_550.jpg'

var img2 = new Image()
// img2.src = 'file:///d:/桌面/timg.jpg'
img2.src = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1507269085638&di=f71daf5be98181991d2f056c103e91df&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fimage%2Fc0%253Dshijue1%252C0%252C0%252C294%252C40%2Fsign%3D587a7e3ede0735fa85fd46faf63865c6%2Fe7cd7b899e510fb3fc46abb2d333c895d1430cbc.jpg'

cxt.drawImage(img2,0,0,300,270)
cxt.drawImage(img1,200,30,200,180,50,40,200,180)

        在canvas里,canvas的状态就是当前画面应用的所有样式和变形的一个快照,这个状态存储在栈中。在canvas里,有save()和restore()方法,save()就是把当前的状态压到栈中保存,而restore()就是把上一次状态从栈中弹出,所有设定都恢复。下面这个例子就是对save()和restore()的综合应用:

var c = document.getElementById('myCanvas')
var ctx = c.getContext('2d')
		
ctx.fillRect(0,0,150,150);   // 使用默认设置绘制一个矩形
ctx.save();                  // 保存默认状态

ctx.fillStyle = '#09F'       // 在原有配置基础上对颜色做改变
ctx.fillRect(15,15,120,120); // 使用新的设置绘制一个矩形
ctx.save();                  // 保存当前状态
  		
ctx.fillStyle = '#FFF'       // 再次改变颜色配置
ctx.globalAlpha = 0.5;    
ctx.fillRect(30,30,90,90);   // 使用新的配置绘制一个矩形

ctx.restore();               // 重新加载之前的颜色状态
ctx.fillRect(45,45,60,60);   // 使用上一次的配置绘制一个矩形

ctx.restore();               // 加载默认颜色配置
ctx.fillRect(60,60,30,30);   // 使用加载的配置绘制一个矩形

        在canvas里,我们可以运用translate(x,y)来对图形进行位移 ,这个方法和我们刚刚上面所讲的drawImage(image,x,y)有什么区别呢?其实你一看到drawImage()这个方法的名字,你就知道这个方法是用来绘制图片的,虽然也能进行位移,但是我们的translate()方法是不能绘制图片的,translate()方法的意思是重新映射画布上的(0,0)位置。所以,你用这个方法的时候,他的x和y的偏移量每次都是以原点为起始点进行位移的,如:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d') 

cxt.fillRect(10,10,50,50)
cxt.translate(10,70)
cxt.fillRect(10,10,50,50)
        有平移肯定少不了旋转和缩放的啦。旋转的方法是rotate(angle),这个angle指的是弧度,这个方法是以canvas的原点为圆心顺时针旋转的,如果要改变圆心则通过translate()方法来进行改变。下面的例子注释的是用translate()方法,没用注释的没用了translate()方法。

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
		
cxt.fillRect(100,100,100,100)
cxt.fillStyle = 'red'
cxt.rotate(Math.PI/4)
cxt.fillRect(100,100,100,100)

// cxt.fillRect(100,100,100,100)
// cxt.fillStyle = 'red'
// cxt.translate(150,80)  //这个原点很难找,知道公式的小伙伴可以把它贴到评论那里,楼主感谢了
// cxt.rotate(Math.PI/4)
// cxt.fillRect(0,0,100,100)
        缩放的方法是scale(x,y),这两个参数表示横轴和纵轴的缩放因子,这两个参数都必须为正值,大于1.0表示放大,小于1.0表示缩小,等于1.0则无变化。如果对绘图进行缩放的话,那么所有之后的绘图也会被缩放,定位也会被缩放,所以要注意。例子:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.strokeRect(5,5,25,15);
ctx.scale(2,2);
ctx.strokeRect(5,5,25,15);
ctx.scale(2,2);
ctx.strokeRect(5,5,25,15);
ctx.scale(2,2);
ctx.strokeRect(5,5,25,15);
        最后,可以通过transform(m11,m12,m21,m22,dx,dy)这个方法来把上面的平移、旋转和缩放给整合起来用,transform()方法的原理是涉及到计算机图形学的二维图形的几何变换矩阵,深入理解的话涉及到数学的问题,感兴趣的话可以查阅这个文章: 二维图形的几何变换,这里不做深究,参数的详细用法如下所示:

        transform()方法和setTransform()方法的主要区别是每次用transform()方法都是用上一次的图形来进行的操作的,而每次运用setTransform()方法的话他每次都会用最初的图形来进行操作的,并且不管执行多少次setTransform()方法,最后面都只会出现一个图形,如:

var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");

ctx.fillStyle="yellow";
ctx.fillRect(0,0,250,100)

ctx.transform(1,0.5,-0.5,1,30,10);
ctx.fillStyle="red";
ctx.fillRect(0,0,250,100);

ctx.transform(1,0.5,-0.5,1,30,10);
ctx.fillStyle="blue";
ctx.fillRect(0,0,250,100);
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");

ctx.fillStyle="yellow";
ctx.fillRect(0,0,250,100)

ctx.setTransform(1,0.5,-0.5,1,30,10);
ctx.fillStyle="red";
ctx.fillRect(0,0,250,100);

ctx.setTransform(1,0.5,-0.5,1,30,10);
ctx.fillStyle="blue";
ctx.fillRect(0,0,250,100);


        最后介绍一个方法clip(),这个方法表示的是裁剪,是从原始画布中剪切任意形状和尺寸,一旦裁剪了某个区域,则所有之后的绘图都会被限制在被裁剪的区域内。如:

var c = document.getElementById('myCanvas')
var cxt = c.getContext('2d')
cxt.rect(50,20,200,120)
cxt.stroke()
cxt.clip()
cxt.fillStyle = 'green'
cxt.fillRect(0,0,150,100)

        当然,上面的只是canvas的基础入门,canvas可以说是入门容易精通难,常见用的比较多的有购物网站那里把鼠标放到商品图片的时候会放大、一些动画效果比如时钟,还可以用到游戏上。详细的canvas教程推荐去MDN查看,因为是Mozilla大公司写的,比较权威。


参考文章:MDN canvas教程  更多canvas的方法  Canvas实例

猜你喜欢

转载自blog.csdn.net/Tank_in_the_street/article/details/78161103