canvas初学之——绘制一片星空

                                       利用canvas画一片星空

 

效果图如下:

   

观察这篇星空:
    多一半的位置被随机分布的星星所覆盖,右上角有一轮月牙,背景色为深蓝色到黑色的渐变色,下方是一片波浪形绿地,绿地颜色也为渐变色。


1.首先,如何绘制渐变色?
   已知可以绘制的渐变色有两种情况,径向渐变(Radial Gradient)和线性渐变(Linear Gradient)。

                                                              径向渐变与线性渐变效果如图:

第一张图为线性渐变,第二张图为径向渐变

·线性渐变Linear Gradient
            eg:var grd = cxt.createLinearGradient(xstart,ystart,xend,yend);
                //构成渐变线,得到渐变线的方向和尺度
               grd.addColorStop(stop,color);
                stop:介于 0.0 与 1.0 之间的值,表示渐变中开始与结束之间的位置
                color 在结束位置显示的 CSS 颜色值     

扫描二维码关注公众号,回复: 3492280 查看本文章

    上图中线性渐变代码如下:


			var linearGrad = context1.createLinearGradient(0,0,800,800);
			linearGrad.addColorStop(0.0, '#fff');
			linearGrad.addColorStop(0.3, 'yellow');
			linearGrad.addColorStop(0.6, '#e89abe');
			linearGrad.addColorStop(1.0, 'blue');
			context1.fillStyle = linearGrad;
			context1.fillRect(0,0,800,800);

在0.0-1.0种用了4种颜色,这4种颜色 呈线性渐变


 ·径向渐变Radial Gradient
            呈放射状渐变,定义在两个同心圆,而非两点  
            eg:var grd = cxt.createRadialGradient(x0,y0,r0,x1,y1,r1);
               grd.addColorStop(stop,color);

上图中径向渐变代码如下:

var radialGrad = context2.createRadialGradient(400,400,0,400,400,500);
			radialGrad.addColorStop(0.0, '#fff');
			radialGrad.addColorStop(0.3, 'yellow');
			radialGrad.addColorStop(0.6, '#e89abe');
			radialGrad.addColorStop(1.0, 'blue');
			context2.fillStyle = radialGrad;
			context2.fillRect(0,0,800,800);

在0.0-1.0种用了4种颜色,这4种颜色 呈径向渐变

而在星空例子中径向渐变效果更好;
  径向渐变呈放射状渐变,是定义在两个同心圆之间的
  通过createRadialGradient(x0,y0,r0,x1,y1,r1)函数,其中,x0,y0,r0表示小圆
  原点坐标以及半径,,x1,y1,r1表示大圆原点坐标及半径;
  再利用addColorStop(stop,color)函数,在0.0处定义深蓝色,在1.0初定义黑色
  这样就可以做出放射状的渐变色了;

2.如何绘制漫天繁星?

首先看单独的一颗五角星如何绘制:

                                                     (图片转自慕课网liuyubobobo讲师的课程Canvas绘图详解)

  根据观察,可以得到,五角星的绘制其实是基于两个同心圆的基础上的,根据这两个同心圆,可以算出每个角的坐标

代码如下:

		function starPath(cxt){//绘制一个标准的五角星
			cxt.beginPath();
			for (var i = 0; i < 5; i++) {
				cxt.lineTo(Math.cos( (18+i*72)/180*Math.PI),
					-Math.sin((18+i*72)/180*Math.PI));

				cxt.lineTo(Math.cos( (54+i*72)/180*Math.PI)*0.5,
					-Math.sin((54+i*72)/180*Math.PI)*0.5);

			}
			cxt.closePath();
		}

这是以大圆半径为1 来绘制的一个标准的五角星;(我们令小圆半径为大圆半径的0.5倍)

其中一个注意点:在Math中的三角函数是以弧度值来计算的,因此在使用sin与cos时,需要将角度转化为弧度。

这样用for循环遍历5次,即得到了一个标准的五角星;

我们将这个过程封装成一个函数starPath(cxt)以便复用,其中参数cxt是绘图上下文环境

然而夜空中不可能只有一颗星星,而且天上的星星的排列应该是随机的,因此,我们需要采用Math中的random随机函数来随机星星的坐标与大小;

for (var i = 0; i < 200; i++) {
				var r = Math.random()*5+5;
				var x = Math.random()*canvas.width;
				var y = Math.random()*canvas.height*0.65;
				var a = Math.random()*360;
				drawStar(cxt,r,x,y,a);
			}

我们随机了星星的半径,坐标以及旋转角度,遍历200次,使天空中出现200颗星星

而其中绘制星星,我们也将其封装为一个函数drawStar(cxt,r,x,y,a),其中,r表示大圆半径,(x,y)为星星偏移量,a为星星的旋转角度

	function drawStar(cxt,R,x,y,rot){//rot表示旋转角度

			cxt.save();

			cxt.translate(x,y);
			cxt.rotate(rot/180*Math.PI);
			cxt.scale(R,R);

			starPath(cxt)


			cxt.fillStyle = '#fb3';
		


			cxt.fill();

			cxt.restore();

		}

这样,我们就绘制好了一片星空了 

3.接下来,是绘制一轮月牙 

                                                  (图片转自慕课网liuyubobobo讲师的课程Canvas绘图详解) 

可以看出,这个月牙的外圆是一个1/2圆弧,易画出,可用arc()函数,然而内圆比较复杂,需要通过计算,再使用arcTo()函数画出。

实际实现中,为了使代码复用方便,我们绘制的是一个以(0,0)点为圆心,1为半径的一个月牙

function pathMoon(cxt, d){
			cxt.beginPath();
			cxt.arc(0, 0, 1, 0.5*Math.PI, 1.5*Math.PI, true);
			cxt.moveTo(0, -1);
			cxt.arcTo(d, 0, 0, 1, dis(0, -1, d, 0)/d);
			cxt.closePath();
		}

其中,参数d表示控制点,即图中C点的横坐标

在实现这个函数时,我们还封装了一个函数dis(x1,y1,x2,y2),用于求取直角三角形的斜边:

function dis(x1, y1, x2, y2){//直角三角形求取斜边
			return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));

这样,我们就画好了一个标准的月亮,而为了使这个月亮的大小位置颜色等属性满足我们的星空,还需要 进行完善,我们封装了一个函数fillMoon(cxt, d, x, y, R, rot, /*optional*/fillColor),其中,d表示控制点,(x,y)表示偏移量,R表示对月亮的缩放倍数,rot表示旋转角度,还有一个可选参数fillColor,表示月亮的颜色,如果没有给定,则用默认颜色#fb5

function fillMoon(cxt, d, x, y, R, rot, /*optional*/fillColor){
			cxt.save();
			cxt.translate(x,y);
			cxt.rotate(rot*Math.PI/180);
			cxt.scale(R,R);
			pathMoon(cxt, d):
			cxt.fillStyle = fillColor || '#fb5';
			cxt.fill();
			cxt.restore();
		}

最后调用fillMoon函数,这样,我们的月亮也画好啦

4.如何绘制波浪形的绿地

我们已知,使用arcTo函数是没办法绘制波浪线的,贝塞尔二次曲线也无法绘制波浪线,因为这两个都只有一个控制点,因此这里我们只能用贝塞尔三次曲线来绘制波浪线。

贝塞尔三次曲线
        context.moveTo(x0, y0); 起始点
        context.bezierCurveTo(x1, y1, 控制点,
                                             x2, y2, 控制点,
                                             x3, y3); 结束点

与贝塞尔二次曲线不同,贝塞尔三次曲线有6个参数,分为两个控制点和一个结束点,因此,要绘制这片绿地,需要使用贝塞尔三次曲线。同时,这片绿地的背景色我们采用线性渐变色来处理,代码如下:

	function drawLand(cxt){
			cxt.save();
			cxt.beginPath();
			cxt.moveTo(0, 600);
			cxt.bezierCurveTo(540, 400, 600, 800, 1200, 600);
			cxt.lineTo(1200,800);
			cxt.lineTo(0,800);
			cxt.closePath();

			var landStyle = cxt.createLinearGradient(0,800,0,0);
			landStyle.addColorStop (0.0, '#030');
			landStyle.addColorStop (1.0, '#580');
			cxt.fillStyle = landStyle;
			cxt.fill();

			cxt.restore();
		}

所有代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>星空练习</title>
</head>
<body>
	<canvas id="canvas" style="display:block;border: 1px solid #aaa;margin: 50px auto">该浏览器不支持canvas,请更换浏览器后再试</canvas>
	<script type="text/javascript">

		window.onload = function(){
			var canvas = document.getElementById('canvas');
			canvas.width = 1200;
			canvas.height = 800;
			var cxt = canvas.getContext('2d');

			//线性渐变背景
			// var skyStyle = cxt.createLinearGradient(0,0,0,canvas.height);
			// skyStyle.addColorStop(0.0, 'black');
			// skyStyle.addColorStop(1.0, '#035');

			// cxt.fillStyle = skyStyle;
			// cxt.fillRect(0,0,canvas.width,canvas.height);
			
			//径向渐变背景
			var skyStyle = cxt.createRadialGradient(canvas.width/2,canvas.height,0,canvas.width/2,canvas.height,canvas.height);
			skyStyle.addColorStop(0.0, '#035');
			skyStyle.addColorStop(1.0, 'black');

			cxt.fillStyle = skyStyle;
			cxt.fillRect(0,0,canvas.width,canvas.height);





			for (var i = 0; i < 200; i++) {
				var r = Math.random()*5+5;//大圆半径为0-20
				var x = Math.random()*canvas.width;
				var y = Math.random()*canvas.height*0.65;
				var a = Math.random()*360;
				drawStar(cxt,r,x,y,a);
			}

			fillMoon(cxt, 2, 900, 200, 100, 30);
            drawLand(cxt);
						
		}

	function drawLand(cxt){
			cxt.save();
			cxt.beginPath();
			cxt.moveTo(0, 600);
			cxt.bezierCurveTo(540, 400, 600, 800, 1200, 600);
			cxt.lineTo(1200,800);
			cxt.lineTo(0,800);
			cxt.closePath();

			var landStyle = cxt.createLinearGradient(0,800,0,0);
			landStyle.addColorStop (0.0, '#030');
			landStyle.addColorStop (1.0, '#580');
			cxt.fillStyle = landStyle;
			cxt.fill();

			cxt.restore();
		}


		function drawStar(cxt,R,x,y,rot){//rot表示旋转角度

			cxt.save();

			cxt.translate(x,y);
			cxt.rotate(rot/180*Math.PI);
			cxt.scale(R,R);

			starPath(cxt)


			cxt.fillStyle = '#fb3';
			// cxt.strokeStyle = 'fd5';
			// cxt.lineWidth = 3;
			// cxt.lineJoin = 'round';


			cxt.fill();
			//cxt.stroke();

			cxt.restore();

		}

		function starPath(cxt){//绘制一个标准的五角星
			cxt.beginPath();
			for (var i = 0; i < 5; i++) {
				cxt.lineTo(Math.cos( (18+i*72)/180*Math.PI),
					-Math.sin((18+i*72)/180*Math.PI));

				cxt.lineTo(Math.cos( (54+i*72)/180*Math.PI)*0.5,
					-Math.sin((54+i*72)/180*Math.PI)*0.5);

			}
			cxt.closePath();
		}


		function fillMoon(cxt, d, x, y, R, rot, /*optional*/fillColor){
			cxt.save();
			cxt.translate(x,y);
			cxt.rotate(rot*Math.PI/180);
			cxt.scale(R,R);
			pathMoon(cxt, d);
			cxt.fillStyle = fillColor || '#fb5';
			cxt.fill();
			cxt.restore();
		}

		function pathMoon(cxt, d){
			cxt.beginPath();
			cxt.arc(0, 0, 1, 0.5*Math.PI, 1.5*Math.PI, true);
			cxt.moveTo(0, -1);
			cxt.arcTo(d, 0, 0, 1, dis(0, -1, d, 0)/d);
            // cxt.quadraticCurveTo(1.2,0,0,1);利用贝塞尔二次曲线绘制
			cxt.closePath();
		}


		function dis(x1, y1, x2, y2){//直角三角形求取斜边
			return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
		}
	</script>
</body>
</html>

代码中还有线性渐变的背景色被注释掉了,感兴趣的小伙伴可以尝试一下线性背景色哦 

补充:

对于月亮的绘制,其实不一定要用arcTo绘制,也可以尝试使用贝塞尔二次曲线来绘制

// cxt.quadraticCurveTo(1.2,0,0,1);利用贝塞尔二次曲线绘制,可以用这个代替arcTo曲线

猜你喜欢

转载自blog.csdn.net/Yvonne_Lu7/article/details/81196565
今日推荐