3.4 使用quadraticCurveTo绘制二次贝赛尔曲线
Canvas提供了一系列的方法来绘制曲线,比如quadraticCurveTo(通过起始两个点以及一个控制点来绘制,前两个参数为控制点横纵坐标,后两个参数为终点横纵坐标,使用的是数学上的二次贝赛尔方程)。下面我们来看一下常见的一些使用。
固定控制点
如下程序,我们实现了一个固定起始点,使用鼠标位置做为控制点,来绘制二次贝赛尔曲线的应用:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Canvas</title>
</head>
<body>
<canvas id="canvas" width="400" height="400">当前浏览器不支持canvas</canvas>
<script type="text/javascript" src="utils.js"></script>
<script type="text/javascript" src="arrow.js"></script>
<script type="text/javascript">
window.onload=function(){
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var mouse=utils.captureMousePosition(canvas);
//固定两个端点
var x0=100,y0=200,x2=300,y2=200;
canvas.addEventListener('mousemove',function(){
//context.clearRect(0,0,canvas.width,canvas.height);
//指定控制点为鼠标位置
var x1=mouse.x;
var y1=mouse.y;
//绘制曲线
context.beginPath();
context.moveTo(x0,y0);
context.quadraticCurveTo(x1,y1,x2,y2);
context.stroke();
},false);
};
</script>
</body>
</html>
如下,即为其效果图:
曲线过定点
之前我们绘制的曲线是控制点确定的曲线,如果我们已知曲线上的一个点(非起始点),则需要通过贝赛尔方程的一些特性来处理。(n次贝赛尔曲线有n+1个控制点,并且贝赛尔曲线位于这些控制点组成的多边形内部。)
已知两个端点(x0,y0)与(x2,y2)及曲线上的一个点(x1,y1).则可以得到其控制点的位置为
x = x1 * 2 - (x0 + x2) / 2;
y = y1 * 2 - (y0 + y2) / 2;
(参见如下文章:
http://learn.gxtc.edu.cn/NCourse/jxcamcad/cadcam/Mains/main4-2.htm
http://www.leeyou.net/UTutorial/show.php?cid=153
http://hi.baidu.com/ggggwhw/blog/item/ffd23d35f313543c5ab5f5ed.html)。
所以,我们只需要修正上述固定控制点的代码,通过曲线上的点来求新的固定点的坐标即可:
var x1=mouse.x*2-(x0+x2)/2;
var y1=mouse.y*2-(y0+y2)/2;
多点控制的平滑曲线
下面来看一个复杂一点的问题,如果我们随机有N个点,如何来使用这N个点,利用二次贝赛尔曲线来绘制一条平滑的曲线?最简单的想法就是每三个点一组来绘制一个二次贝赛尔曲线,不难得到如下代码:
window.onload=function(){
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var points=[];
var pointNumber=9;
//随机生成点
for(var i=0;i<pointNumber;i++){
points.push({
x:Math.random()* canvas.width,
y:Math.random()* canvas.height
});
}
//每三点来绘制一条曲线
context.beginPath();
context.moveTo(points[0].x,points[0].y);
for(var i=1;i< pointNumber; i+=2){
context.quadraticCurveTo(points[i].x,points[i].y,points[i+1].x,points[i+1].y);
}
context.stroke();
//绘制所有给定的点
for(var i=0;i<pointNumber;i++){
context.moveTo(points[i].x,points[i].y);
context.beginPath();
context.arc(points[i].x,points[i].y,2,0,Math.PI*2,true);
context.closePath();
context.fill();
}
};
查看结果,会发现并不是想要的效果,除了共用起始点外,所有的二次曲线都是独立分隔的,并不是一条平滑的曲线,如下图:
这是因为使用二次贝赛尔方程来绘制一个多点控制的平滑曲线,不可以每三个已知点为一组依次来绘制曲线,这样绘制出来的每一个二次曲线并不在一个相对坐标系统内。可以从第二个点开始,使用每两个点的中点来做端点,其它点均做为控制点来绘制曲线,如下图:(空心点为已知的给定点,实心点为给定点的中点)
按上面的说明,我们来更新代码:
window.onload=function(){
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var points=[];
var pointNumber=9;
var ctrlPoint={x:0,y:0};
for(var i=0;i<pointNumber;i++){
points.push({
x:Math.random()* canvas.width,
y:Math.random()* canvas.height
});
}
context.beginPath();
context.moveTo(points[0].x,points[0].y);
//从第二个点开始,使用之后两点之间的“中点”来做为端点
for(var i=1;i< pointNumber-2; i++){
ctrlPoint.x=(points[i].x+points[i+1].x)/2;
ctrlPoint.y=(points[i].y+points[i+1].y)/2;
context.quadraticCurveTo(points[i].x,points[i].y,ctrlPoint.x,ctrlPoint.y);
}
//最后两个点需要特殊处理,无法使用此两点间的“中点”
context.quadraticCurveTo(points[i].x,points[i].y,points[i+1].x,points[i+1].y);
context.stroke();
for(i=0;i<pointNumber;i++){
context.moveTo(points[i].x,points[i].y);
context.beginPath();
context.arc(points[i].x,points[i].y,5,0,Math.PI*2,true);
context.closePath();
context.fill();
}
};
其效果如下图:
封闭曲线的实现
可以看出,上述代码对于第一个点,最后一个点相关的“中点”都没有使用到。如果我们想利用到这两个点相关的“中点”来构成一个封闭的平滑曲线。那把所有的点均做为控制点来处理,使用它们的“中点”来做为每一个二次贝赛尔曲线的端点,如下代码所示:
window.onload=function(){
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var points=[];
var pointNumber=9;
var ctrlPoint={x:0,y:0};
var ctrlPoint1={x:0,y:0};
for(var i=0;i<pointNumber;i++){
points.push({
x:Math.random()* canvas.width,
y:Math.random()* canvas.height
});
}
//构造控制点,使用给定点的“中点”来做为曲线的端点
ctrlPoint1.x=(points[0].x+points[pointNumber-1].x)/2;
ctrlPoint1.y=(points[0].y+points[pointNumber-1].y)/2;
context.beginPath();
context.moveTo(ctrlPoint1.x,ctrlPoint1.y);
for(var i=0;i< pointNumber-1; i++){
ctrlPoint.x=(points[i].x+points[i+1].x)/2;
ctrlPoint.y=(points[i].y+points[i+1].y)/2;
context.quadraticCurveTo(points[i].x,points[i].y,ctrlPoint.x,ctrlPoint.y);
}
//封闭曲线
context.quadraticCurveTo(points[i].x,points[i].y,ctrlPoint1.x,ctrlPoint1.y);
context.stroke();
for(i=0;i<pointNumber;i++){
context.moveTo(points[i].x,points[i].y);
context.beginPath();
context.arc(points[i].x,points[i].y,2,0,Math.PI*2,true);
context.closePath();
context.fill();
}
}
其效果图如下:
注意上面的代码中的i,我们在第一个循环外也使用了i,这是因为JavaScript的变量作用域不是块级的,而是以函数来区分的,如:
console.log(j); //undefined
for(var j=0;j<10;j++){
console.log(j); //1~9
}
console.log(j); //10
- 大小: 26.3 KB
- 大小: 20.7 KB
- 大小: 21.6 KB
- 大小: 28.8 KB
- 大小: 23.8 KB
分享到:
相关推荐
canvas拼图游戏,基于html + canvas画布实现
H5 canvas 实现小游戏 H5 canvas 实现小游戏 H5 canvas 实现小游戏 H5 canvas 实现小游戏
HTML5 Canvas 赛车游戏动画DEMO演示 键盘控制车的方向
canvas端午节小游戏
利用canvas画布,进行撞击,消除操作的小游戏
canvas拼图小游戏
html5 开发技术 游戏.很好玩的html5+canvas技术实现的拼图游戏
试验性质的一个微信小程序,用canvas做的一个类似flappy-bird的小游戏。 包含一些基本的功能:躲避障碍物、计分、排行榜等等。后端的工程也一并上传了,在java目录中,很简单的一个SpringMVC工程。 游戏原型见这里。...
通过一个简单的例子,讲解如何在HTML5 canvas中开发游戏,主要是定时器的用法值得大家借鉴
canvas端午节吃粽子小游戏,兼容移动端
canvas 飞机大战小游戏
HTML5 Canvas赛车游戏动画,赛车游戏动画演示。
canvas 实现2d 简单小游戏,点击出现蓝色攻击方块,攻击黑色方块得分,触碰红色方块游戏结束,黑色方块想灭完,游戏等级提升
HTML 5 CANVAS游戏开发实战HTML 5 CANVAS游戏开发实战HTML 5 CANVAS游戏开发实战
canvas-弹珠游戏
HTML 5 CANVAS游戏开发实战
canvas打砖块小游戏
1.open-type=“share”,onShareAppMessage实现分享微信功能。2.canvas绘制分享海报。
HTML5+CANVAS游戏开发实战,很值得学习,就算不是做游戏,但是对js的进阶也很有好处。
canvas版的2048小游戏,canvas版的2048小游戏,canvas版的2048小游戏,canvas版的2048小游戏,canvas版的2048小游戏