某天在学校图书馆里溜达的时候看见的这本书,感觉应该有点干货就借回来了.放了几天,今天干什么都不对劲,就把这本书拿出来看了看,发现一些有意思的东西.
第一个是一个递归绘图,之前见过类似的小动画,感觉很神奇,没想到在这本书里的代码就简简单单的一点.
对我来说最没想到的是这个代码就像平时的递归程序一样简单,我总觉得牵涉到图形的时候一切都悔变得复杂,就不愿意动脑了,这样不好.
<!DOCTYPE html>
<html>
<head>
<title>OK</title>
<script type = "text/javascript" src = "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type = "text/javascript">
var maxAngle = Math.PI / 7;//控制子树于亲树的角度偏转最大值
var drawTree = function(ctx, startX, startY, length, angle, depth, branchWidth) {//如同深度优先搜索一般简洁明了的程序
var newLength, endX, endY, newAngle;
if (depth < 0) {
return;
}
//枝杈终点
endX = startX + length * Math.cos(angle);
endY = startY + length * Math.sin(angle);
//绘出当前枝杈
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineCap = "round";
ctx.lineWidth = branchWidth;
ctx.lineTo(endX, endY);
if (depth <= 2) {//根据是否靠近边缘决定颜色
ctx.strokeStyle = 'rgb(0, ' + (((Math.random() * 64) + 128) >> 0) + ', 0)';//strokestyle接受字符串
}
else {
ctx.strokeStyle = 'rgb(' + (((Math.random() * 64) + 64) >> 0) + ', 50, 25)';
}
ctx.stroke();
//下级枝杈
branchWidth *= 0.7;
newAngle = angle - Math.random() * maxAngle;
newLength = length * (0.7 + Math.random() * 0.3);
drawTree(ctx, endX, endY, newLength, newAngle, depth - 1, branchWidth);
newAngle = angle + Math.random() * maxAngle;
newLength = length * (0.7 + Math.random() * 0.3);
drawTree(ctx, endX, endY, newLength, newAngle, depth - 1, branchWidth);
};
</script>
</head>
<body>
<script type = "text/javascript">
$(document).ready(function(){
var cvs = document.getElementById('myCanvas');
var ctx = cvs.getContext("2d");
drawTree(ctx, 400, 600, 60, -Math.PI/2, 14, 12);
});
</script>
<canvas id = "myCanvas" width = "800" height = "600" />
</body>
</html>
上张效果图
第二个是一个大炮模拟程序,里面使用了向量,游戏对象,还有一些我之前没见过的API,对我启发很大.
要记一下的地方:
1 函数继承 形如 : var vector = function(){var that = {}; return that;};
2 对象字面量,以前一直没用过.
3 向量,还有向量的操作.
4 API
原来可以在元素节点上加事件监听,以前一直不知道.
ctx.save() && ctx.restore() 保存和重绘canvas, 在做有背景的动画时用这个绘制背景,不丢失动画元素渐变信息.
ctx.rotate(), 旋转ctx, 可以画倾斜的矩形了
canvas.getBoundingClientRect(), event里的坐标是相对于整个窗口的,我们要的是相对于画布的坐标,用此获得画布的坐标.
5 gameObjects数组,管理游戏元素
<!DOCTYPE html>
<html>
<head>
<title>GO !!!</title>
<script type = "text/javascript">
window.onload = function(){
var gameObjects = [];
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
var vector2d = function(x, y){
var vec = {
vx : x,
vy : y,
//scale() method allows us to scale hte vector, eighter up or down.
scale : function(scale){
vec.vx *= scale;
vec.vy *= scale;
},
//add() method adds a vector.
add : function(vec2){
vec.vx += vec2.vx;
vec.vy += vec2.vy;
},
//sub() method substracts a vector.
sub : function(vec2){
vec.vx -= vec2.vx;
vec.vy -= vec2.vy;
},
//negate() method points the vector in the opposite direction.
negate : function(){
vec.vx = -vec.vx;
vec.vy = -vec.vy;
},
//length() method returns the length of the vector using Pythagoras.
length : function(){
return Math.sqrt(vec.vx * vec.vx + vec.vy * vec.vy);
},
//A faster length calucalation that returns the length squared.
//Useful if all you want to know is that one vector is longer than another.
lengthSquard : function(){
return vec.vx * vec.vx + vec.vy * vec.vy;
},
//normalize() method turns the vector into a unit length vector pointing in the same direction.
normalize : function(){
var len = Math.sqrt(vec.vx * vec.vx + vec.vy * vec.vy);
if(len){
vec.vx /= len;
vec.vy /= len;
}
return len;//Maybe useful.
},
//Rotate the vector by an angle specified in radians.
rotate : function(angle){
var vx = vec.vx;
var vy = vec.vy;
var cosVal = Math.cos(angle);
var sinVal = Math.sin(angle);
vec.vx = vx * cosVal - vy * sinVal;
vec.vy = vx * sinVal + vy * cosVal;
},
//I like it.
toString : function(){
return '(' + vec.vx.toFixed(3) + ',' + vec.vy.toFixed(3) + ')';
}
};
return vec;
};
var cannonBall = function(x, y, vector)
{
var gravity = 0;
var that = {
x : x,
y : y,
removeMe : false,
move : function(){
vector.vy += gravity;
gravity += 0.1;
that.x += vector.vx;
that.y += vector.vy;
if(that.y > canvas.height - 150){
that.removeMe = true;
}
},
draw : function(){
ctx.beginPath();
ctx.arc(that.x, that.y, 5, 0, Math.PI * 2, true);
ctx.fill();
ctx.closePath();
}
};
return that;
};
var cannon = function(x, y){
var mx = 0, my = 0;
var angle = 0;
var that = {
x : x,
y : y,
angle : 0,
removeMe : false,
move : function(){
angle = Math.atan2(my - that.y, mx - that.x);
},
draw : function(){
ctx.save();
ctx.lineWidth = 2;
ctx.translate(that.x, that.y);
ctx.rotate(angle);
ctx.strokeRect(0, -5, 50, 10);
ctx.moveTo(0, 0);
ctx.beginPath();
ctx.arc(0, 0, 15, 0, Math.PI * 2, true);
ctx.fill();
ctx.closePath();
ctx.restore();
}
};
//原来可以直接给heml元素加事件监听
canvas.onmousedown = function(event){
var vec = vector2d(mx - that.x, my - that.y);
vec.normalize();
vec.scale(25);
gameObjects.push(cannonBall(that.x, that.y, vec));
};
canvas.onmousemove = function(event){
var bb = canvas.getBoundingClientRect();
mx = (event.clientX - bb.left);
my = (event.clientY - bb.top);
};
return that;
};
var drawSkyAndGrass = function(){
ctx.save();
ctx.globalAlpha = 0.4;
var linGrad = ctx.createLinearGradient(0, 0, 0, canvas.height);
linGrad.addColorStop(0, "#00BFFF");
linGrad.addColorStop(0.5, 'white');
linGrad.addColorStop(0.5, '#55dd00');
linGrad.addColorStop(1, 'white');
ctx.fillStyle = linGrad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
};
gameObjects.push(cannon(50, canvas.height - 150));
setInterval(function(){
drawSkyAndGrass();
var gameObjectsFresh = [];
for(var i = 0; i < gameObjects.length; i++){
gameObjects[i].move();
gameObjects[i].draw();
if(gameObjects[i].removeMe == false){
gameObjectsFresh.push(gameObjects[i]);
}
}
gameObjects = gameObjectsFresh;
}, 30);
};
</script>
</head>
<body>
<canvas id = "canvas" width = 640 height = 480 />
</body>
</html>