基础

  看了岑安大大的教程学习了3d基础,之前写了篇总结,觉得写的太散废话太多,重写一篇。

  本文需要实现的效果如下:3d球

  岑安的两篇教程写的很棒,但我感觉改变下顺序或许会更好理解。

  我们把画布(此文所讲所见所得均基于canvas)的中心当做是一个空间三维系的中心,画布的x和y轴正方向分别当做三维系的x和y轴的正方向,把垂直画布向内当做z轴正方向,那么三维系大致如下图:

  我们假设空间中有个点在围绕y轴转动,那么转动的轨迹就是个圆;如果画面感强的画,可以想象出就像地球上的某个点由于地球的自转做的向心运动。那么问题来了,如果三维系里的某个点围绕y轴转动确定的角度后,能计算出转动后的三维坐标吗?

  答案是肯定的,空间上的某个点(已知三维坐标)围绕x轴y轴或者z轴转动一定角度后,都能计算出相应的三维坐标。

  过程貌似是个线性代数里的矩阵变换,数学渣渣跪...

  直接把它写在矢量类里了:

// 矢量旋转
Vector3.prototype.rotateX = function(angleX) {
  var cosx = Math.cos(angleX);
  var sinx = Math.sin(angleX);
  var y1 = this.y * cosx - this.z * sinx;
  var z1 = this.y * sinx + this.z * cosx;
  this.y = y1;
  this.z = z1;
};

Vector3.prototype.rotateY = function(angleY) {
  var cosy = Math.cos(angleY);
  var siny = Math.sin(angleY);
  var x1 = this.z * siny + this.x * cosy;
  var z1 = this.z * cosy - this.x * siny;
  this.x = x1;
  this.z = z1;
};

  更近一步,空间上的某个点围绕任意轴转动一定角度,都能计算出转动后的三维坐标。有兴趣的可以参考【自己给自己题目做】:如何在Canvas上实现魔方效果

  那么就好办了,我们在三维空间初始化一些点(当做球心),随意赋给他们xyz坐标值,随意设定旋转角度,那么旋转后的xyz值也能很轻松地得到了!当然有了这些还只是成功了一半,我们的canvas只支持2d,如果支持3d问题就解决了,我们还得把三维降到二维上。我们知道,如果一个球在三维系上围绕y轴转动,球的大小是不会变化的,但是如果体现在二维上,z值越大时,球体在视觉上的体现就是越小(跟z值有关);在三维上,y值是不会变化的,但是在二维视觉上的体现不应该是这样,y值是应该变化的(跟z值有关)。另一方面,球体的任意角度视图都是一个圆形。

  于是我们需要“把z方向扁平化”,即把z轴数值的大小体现在x和y上:

// focalLength 表示当前焦距,一般可设为一个常量
var focalLength = 250;

// 把z方向扁平化
var scale = focalLength / (focalLength + this.z);
this.x2 = this.garden.vpx + this.x * scale;
this.y2 = this.garden.vpy + this.y * scale;
this.radius = this.ballR * scale;

  z方向扁平化使得z方向的值体现在x、y和r的大小以及球的透明度等变量上,一方面由于球体的旋转我们需要时刻调整球体在三维系上的坐标,另一方面在draw的时候需要计算二维上的坐标。

  所以简单的来说:

    1. 初始化球心在三维系上的坐标以及球的颜色半径等属性(三维系原点的设定)
    2. 将三维降到二维,绘制
    3. 根据旋转角度重新计算三维坐标
    4. 将三维降到二维,绘制
    5. 反复重复

  这里还有个注意点就是每帧重绘时,根据小球在三维系的z值排个序,因为后面的会被前面的挡住,所以绘制应该有个顺序。

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title> rotate 3d</title>
<script>
window.onload = function() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var garden = new Garden(canvas);

// 设置原点(一般为画布中心)
garden.setBasePoint(500, 250);

// 构造三维系上的点
for(var i = 0; i < 10; i++)
garden.createBall();

// 设置监听
garden.setListener();

// 渲染
setInterval(function() {garden.render();}, 1000 / 60);
};

function Garden(canvas) {
this.canvas = canvas;
this.ctx = this.canvas.getContext('2d');

// 三维系在二维上的原点
this.vpx = undefined;
this.vpy = undefined;
this.balls = [];
this.angleY = 0;
this.angleX = 0;
}

Garden.prototype = {
setBasePoint: function(x, y) {
this.vpx = x;
this.vpy = y;
},

createBall: function(x, y, z, ballR) {
this.balls.push(new Ball(this, x, y, z, ballR));
},

render: function() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.balls.sort(function (a, b) {return b.z - a.z });

for(var i = 0; i < this.balls.length; i++) {
this.balls[i].rotateY();
this.balls[i].rotateX();
this.balls[i].draw(i);
}
},

setListener: function() {
var that = this;
document.addEventListener('mousemove', function(event){
var x = event.clientX - that.vpx;
var y = event.clientY - that.vpy;
that.angleY = -x * 0.0001;
that.angleX = y * 0.0001;
});
}
};

function Ball(garden, x, y, z, ballR) {
this.garden = garden;

// 三维上坐标
this.x = x === undefined? Math.random() * 200 - 100: x;
this.y = y === undefined? Math.random() * 200 - 100: y;
this.z = z === undefined? Math.random() * 200 - 100: z;

this.r = Math.floor(Math.random() * 255);
this.g = Math.floor(Math.random() * 255);
this.b = Math.floor(Math.random() * 255);

// 三维上半径
this.ballR = ballR === undefined? 10 + Math.random() * 10: ballR;

// 二维上半径
this.radius = undefined;

// 二维上坐标
this.x2 = undefined;
this.y2 = undefined;
}

Ball.prototype = {
// 绕y轴变化,得出新的x,z坐标
rotateY: function() {
var cosy = Math.cos(this.garden.angleY);
var siny = Math.sin(this.garden.angleY);
var x1 = this.z * siny + this.x * cosy;
var z1 = this.z * cosy - this.x * siny;
this.x = x1;
this.z = z1;
},

// 绕x轴变化,得出新的y,z坐标
rotateX: function() {
var cosx = Math.cos(this.garden.angleX);
var sinx = Math.sin(this.garden.angleX);
var y1 = this.y * cosx - this.z * sinx;
var z1 = this.y * sinx + this.z * cosx;
this.y = y1;
this.z = z1;
},

draw: function(a) {
// focalLength 表示当前焦距,一般可设为一个常量
var focalLength = 300;

// 把z方向扁平化
var scale = focalLength / (focalLength + this.z);
this.x2 = this.garden.vpx + this.x * scale;
this.y2 = this.garden.vpy + this.y * scale;
this.radius = this.ballR * scale;

this.garden.ctx.beginPath();
this.garden.ctx.fillStyle = 'rgba('+this.r+','+this.g+','+this.b+','+ Math.min(1, scale)+')';
this.garden.ctx.arc(this.x2, this.y2, this.radius, 0, Math.PI * 2 , true);
this.garden.ctx.fill();
}
}
</script>
</head>
<body bgcolor='#000'>
<canvas id='canvas' width=1000 height=600 style='background-color:rgb(0,0,0)'>
This browser does not support html5.
</canvas>
</body>
</html>

 

3d标签云

  有了以上的基础,可以动手做个3d标签云。理论上3d标签云中的标签应该是个a标签,是可以点击跳转的,这里为了方便,不添加点击跳转功能了(直接在canvas上)。

  思路应该很简单,在一个球面上初始化一些点,把球换成文字就可以了。

  怎样构造球体获得坐标?充分认识到了数学的必要性...

  不懂数学,试了下觉得角度的取值有两种方法:

  • (0 <= θ <= PI && 0 <= Φ <= 2 * PI)
  • (0 <= θ <= 2 * PI && 0 <= Φ <= PI)

  可以验证x*x+y*y+z*z确实等于r*r。

  有了公式,我们可以枚举角度获得坐标。

  如果有n个点,我需要平均分配在球面上,怎么做?我们引入第二个公式:

  初始化坐标:

var all = 100;  // 100个
for(var i = 1; i <= all; i++) {
  var a1 = Math.acos(1 - (2 * i) / all);
  var a2 = a1 * Math.sqrt(all * Math.PI);
  var x = 150 * Math.sin(a1) * Math.cos(a2);  // r = 150
  var y = 150 * Math.sin(a1) * Math.sin(a2);
  var z = 150 * Math.cos(a1);
  garden.createBall(x, y, z);
}
 
//
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title> rotate 3d</title>
<script>
window.onload = function() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var garden = new Garden(canvas);

// x=r*sinθ*cosΦ y=r*sinθ*sinΦ z=r*cosθ;
// θ = arccos((2*num-1)/all - 1);
// Φ = θ*sqrt(all * π);

// for(var i = 0; i <= 90; i += 5)
// for(var j = 90; j <= 90; j += 5) {
// var a1 = Math.PI / 180 * i;
// var a2 = Math.PI / 180 * j;
// var x = 150 * Math.sin(a1) * Math.cos(a2);
// var y = 150 * Math.sin(a1) * Math.sin(a2);
// var z = 150 * Math.cos(a1);
// garden.createBall(x, y, z);
// }

var all = 30;
for(var i = 1; i <= all; i++) {
var a1 = Math.acos(1- (2 * i) / all);
var a2 = a1 * Math.sqrt(all * Math.PI);
var x = 150 * Math.sin(a1) * Math.cos(a2);
var y = 150 * Math.sin(a1) * Math.sin(a2);
var z = 150 * Math.cos(a1);
garden.createBall(x, y, z);

}

document.addEventListener("mousemove" , function(event){
var x = event.clientX - garden.vpx;
var y = event.clientY - garden.vpy;

garden.angleY = -x * 0.0001;
garden.angleX = y * 0.0001;
});

setInterval(function() {garden.render();}, 1000/60);
};

function Garden(canvas, vpx, vpy) {
this.canvas = canvas;
this.ctx = this.canvas.getContext('2d');

// 三维系在二维上的原点
this.vpx = vpx === undefined? 500: vpx;
this.vpy = vpy === undefined? 250: vpy;
this.balls = [];
this.angleY = 0;
this.angleX = 0;
}

Garden.prototype = {
createBall: function(x, y, z) {
this.balls.push(new Ball(this, x, y, z));
},

render: function() {
this.ctx.clearRect(0,0,1000,500)
this.balls.sort(function (a, b) {return b.z-a.z })
for(var i = 0; i < this.balls.length; i++) {
this.balls[i].rotateY();
this.balls[i].rotateX();
this.balls[i].draw();
}
}
};

function Ball(garden, x, y, z, angleX, angleY, ballR) {
this.garden = garden;

// 三维下坐标
this.x = x === undefined? Math.random() * 200 - 100: x;
this.y = y === undefined? Math.random() * 200 - 100: y;
this.z = z === undefined? Math.random() * 200 - 100: z;

this.r = Math.floor(Math.random() * 255);
this.g = Math.floor(Math.random() * 255);
this.b = Math.floor(Math.random() * 255);

this.fontSize = (10 + 10 * Math.random());

this.angleX = 0;
// this.angleX = angleX || Math.PI / 200;

this.angleY = angleY === undefined? Math.PI / 100: angleY;

// 三维上半径
this.ballR = 1;

// 二维上半径
this.radius = undefined;

// 二维上坐标
this.x2 = undefined;
this.y2 = undefined;
}

Ball.prototype = {
// 绕y轴变化,得出新的x,z坐标
rotateY: function() {
var cosy = Math.cos(this.garden.angleY);
var siny = Math.sin(this.garden.angleY);
var x1 = this.z * siny + this.x * cosy;
var z1 = this.z * cosy - this.x * siny;

this.x = x1;
this.z = z1;

},

// 绕x轴变化,得出新的y,z坐标
rotateX: function() {
var cosx = Math.cos(this.garden.angleX);
var sinx = Math.sin(this.garden.angleX);
var y1 = this.y * cosx - this.z * sinx;
var z1 = this.y * sinx + this.z * cosx;

this.y = y1;
this.z = z1;
},

draw: function() {
// focalLength 表示当前焦距,一般可设为一个常量
var focalLength = 300;

// 把z方向扁平化
var scale = focalLength / (focalLength + this.z);
this.x2 = this.garden.vpx + this.x * scale;
this.y2 = this.garden.vpy + this.y * scale;
this.radius = this.ballR * scale;

this.garden.ctx.beginPath();
this.garden.ctx.fillStyle = 'rgba('+this.r+','+this.g+','+this.b+','+ Math.min(1, scale)+')';
// this.garden.ctx.arc(this.x2, this.y2, this.radius, 0, Math.PI * 2 , true);
this.garden.ctx.font = 'bold ' + this.fontSize * scale+ 'px serif';
this.garden.ctx.textAlign = "left";
this.garden.ctx.textBaseline = "top";
this.garden.ctx.fillText('博客园', this.x2, this.y2);
this.garden.ctx.fill();
}
}
</script>
</head>
<body bgcolor='#000'>
<canvas id='canvas' width=1000 height=500 style='background-color:rgb(0,0,0)'>
This browser does not support html5.
</canvas>
</body>
</html>

  其实利用这个球面公式还能做很多好看的效果,例如:3D旋转球(W·Axes)
 

点-线-面

  有了点,根据点-线-面的原理,线和面也呼之欲出了。

  稍微动点脑筋搞点创意,就能搞点看起来炫酷(实际上没花头)的小东东。

  比如下面这样:

// _ function runCode(id) {
obj = document.getElementById(id);
var TestWin=open(''); //打开一个窗口并赋给变量TestWin。
TestWin.opener = null;
TestWin.document.write(obj.value); //向这个打开的窗口中写入代码code,这样就实现了运行代码功能。
TestWin.document.close();
}
// ]]>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title> rotate 3d</title>
<script>
window.onload = function() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var garden = new Garden(canvas);

// x=r*sinθ*cosΦ y=r*sinθ*sinΦ z=r*cosθ;
// θ = arccos((2*num-1)/all - 1);
// Φ = θ*sqrt(all * π);

// for(var i = 0; i <= 180; i += 10)
// for(var j = 0; j <= 360; j += 10) {
// var a1 = Math.PI / 180 * i;
// var a2 = Math.PI / 180 * j;
// var x = 150 * Math.sin(a1) * Math.cos(a2);
// var y = 150 * Math.sin(a1) * Math.sin(a2);
// var z = 150 * Math.cos(a1);
// garden.createBall(x, y, z);
// }
for(var i = 10; i <= 30; i += 0.2) {
var ans = getHeartPoint(i);
garden.createBall(ans[0], ans[1], -50);
garden.createBall(ans[0], ans[1], 0);

// ctx.beginPath();
// ctx.arc(ans[0], ans[1], 1, 0, Math.PI * 2, true);
// ctx.fill();
}

function getHeartPoint(angle) {
var t = angle / Math.PI;
var sx = 10;
var sy = -10;
var x = sx * (16 * Math.pow(Math.sin(t), 3));
var y = sy * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
return new Array(x, y);
}

// var all = 50;
// for(var i = 1; i <= all; i++) {
// garden.createBall();
// }

document.addEventListener("mousemove" , function(event){
var x = event.clientX - garden.vpx;
var y = event.clientY - garden.vpy;

garden.angleY = -x * 0.0001;
garden.angleX = y * 0.0001;
});

setInterval(function() {garden.render();}, 1000/60);
// for(var i = 0; i < garden.balls.length; i++)
// console.log("%d %d %d", garden.balls[i].x, garden.balls[i].y, garden.balls[i].z)
};

function Garden(canvas, vpx, vpy) {
this.canvas = canvas;
this.ctx = this.canvas.getContext('2d');

// 三维系在二维上的原点
this.vpx = vpx === undefined? 500: vpx;
this.vpy = vpy === undefined? 250: vpy;
this.balls = [];
this.angleY = Math.PI / 180;
this.angleX = 0;
}

Garden.prototype = {
createBall: function(x, y, z) {
this.balls.push(new Ball(this, x, y, z));
},

render: function() {
this.ctx.clearRect(0,0,1600,700)
// this.balls.sort(function (a, b) {return b.z-a.z })
for(var i = 0; i < this.balls.length; i++) {
this.balls[i].rotateY();
this.balls[i].rotateX();
this.balls[i].draw(i);
}
}
};

function Ball(garden, x, y, z, angleX, angleY, ballR) {
this.garden = garden;

// 三维下坐标
this.x = x === undefined? Math.random() * 800 : x;
this.y = y === undefined? Math.random() * 200 : y;
this.z = z === undefined? Math.random() * 400 - 200: z;

this.r = Math.floor(Math.random() * 255);
this.g = Math.floor(Math.random() * 255);
this.b = Math.floor(Math.random() * 255);

this.fontSize = (10 + 10 * Math.random());

// this.angleX = 0;
// this.angleX = angleX || Math.PI / 200;

// this.angleY = angleY === undefined? Math.PI / 100: angleY;

// 三维上半径
this.ballR = 1;

// 二维上半径
this.radius = undefined;

// 二维上坐标
this.x2 = undefined;
this.y2 = undefined;
}

Ball.prototype = {
// 绕y轴变化,得出新的x,z坐标
rotateY: function() {
var cosy = Math.cos(this.garden.angleY);
var siny = Math.sin(this.garden.angleY);
var x1 = this.z * siny + this.x * cosy;
var z1 = this.z * cosy - this.x * siny;

this.x = x1;
this.z = z1;

},

// 绕x轴变化,得出新的y,z坐标
rotateX: function() {
var cosx = Math.cos(this.garden.angleX);
var sinx = Math.sin(this.garden.angleX);
var y1 = this.y * cosx - this.z * sinx;
var z1 = this.y * sinx + this.z * cosx;

this.y = y1;
this.z = z1;
},

draw: function(a) {
// focalLength 表示当前焦距,一般可设为一个常量
var focalLength = 300;

// 把z方向扁平化
var scale = focalLength / (focalLength + this.z);
this.x2 = this.garden.vpx + this.x * scale;
this.y2 = this.garden.vpy + this.y * scale;
this.radius = this.ballR * scale;

this.garden.ctx.beginPath();
this.garden.ctx.fillStyle = 'rgba('+this.r+','+this.g+','+this.b+','+ Math.min(1, scale)+')';
this.garden.ctx.arc(this.x2, this.y2, this.radius, 0, Math.PI * 2 , true);
// this.garden.ctx.font = 'bold ' + this.fontSize * scale+ 'px serif';
// this.garden.ctx.textAlign = "left";
// this.garden.ctx.textBaseline = "top";
// this.garden.ctx.fillText('博客园', this.x2, this.y2);
this.garden.ctx.fill();

if(a % 4 === 3) {
var x1 = this.garden.balls[a - 3].x2;
var y1 = this.garden.balls[a - 3].y2;
var x2 = this.garden.balls[a - 2].x2;
var y2 = this.garden.balls[a - 2].y2;
var x3 = this.garden.balls[a - 1].x2;
var y3 = this.garden.balls[a - 1].y2;
var x4 = this.x2;
var y4 = this.y2;
this.garden.ctx.beginPath();
// this.garden.ctx.strokeStyle = 'rgba('+this.r+','+this.g+','+this.b+','+ Math.min(1, scale)+')';
this.garden.ctx.moveTo(x1, y1);
this.garden.ctx.lineTo(x2, y2);
this.garden.ctx.lineTo(x4, y4);
this.garden.ctx.lineTo(x3, y3);
this.garden.ctx.closePath();

this.garden.ctx.fill();
// debugger;
}
}
}
</script>
</head>
<body bgcolor='#000'>
<canvas id='canvas' width=1200 height=500 style='background-color:rgb(0,0,0)'>
This browser does not support html5.
</canvas>
</body>
</html>

  或者这样: 
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title> rotate 3d</title>
<script>
window.onload = function() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var garden = new Garden(canvas);

for(var i = 10; i <= 30; i += 0.2) {
var ans = getHeartPoint(i);
garden.createBall(ans[0], ans[1], -50);
garden.createBall(ans[0], ans[1], 0);
}

function getHeartPoint(angle) {
var t = angle / Math.PI;
var sx = 10;
var sy = -10;
var x = sx * (16 * Math.pow(Math.sin(t), 3));
var y = sy * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
return new Array(x, y);
}

document.addEventListener("mousemove" , function(event){
var x = event.clientX - garden.vpx;
var y = event.clientY - garden.vpy;

garden.angleY = -x * 0.0001;
garden.angleX = y * 0.0001;
});

setInterval(function() {garden.render();}, 1000/60);
};

function Garden(canvas, vpx, vpy) {
this.canvas = canvas;
this.ctx = this.canvas.getContext('2d');

// 三维系在二维上的原点
this.vpx = vpx === undefined? 500: vpx;
this.vpy = vpy === undefined? 250: vpy;
this.balls = [];
this.angleY = Math.PI / 180;
this.angleX = 0;
}

Garden.prototype = {
createBall: function(x, y, z) {
this.balls.push(new Ball(this, x, y, z));
},

render: function() {
this.ctx.clearRect(0,0,1600,700)
// this.balls.sort(function (a, b) {return b.z-a.z })
// alert(this.balls.length)
for(var i = 0; i < this.balls.length; i++) {
this.balls[i].rotateY();
this.balls[i].rotateX();
this.balls[i].draw(i);
}
}
};

function Ball(garden, x, y, z, angleX, angleY, ballR) {
this.garden = garden;

// 三维下坐标
this.x = x === undefined? Math.random() * 800 : x;
this.y = y === undefined? Math.random() * 200 : y;
this.z = z === undefined? Math.random() * 400 - 200: z;

this.r = Math.floor(Math.random() * 255);
this.g = Math.floor(Math.random() * 255);
this.b = Math.floor(Math.random() * 255);

this.fontSize = (10 + 10 * Math.random());

// 三维上半径
this.ballR = 1;

// 二维上半径
this.radius = undefined;

// 二维上坐标
this.x2 = undefined;
this.y2 = undefined;
}

Ball.prototype = {
// 绕y轴变化,得出新的x,z坐标
rotateY: function() {
var cosy = Math.cos(this.garden.angleY);
var siny = Math.sin(this.garden.angleY);
var x1 = this.z * siny + this.x * cosy;
var z1 = this.z * cosy - this.x * siny;

this.x = x1;
this.z = z1;
},

// 绕x轴变化,得出新的y,z坐标
rotateX: function() {
var cosx = Math.cos(this.garden.angleX);
var sinx = Math.sin(this.garden.angleX);
var y1 = this.y * cosx - this.z * sinx;
var z1 = this.y * sinx + this.z * cosx;

this.y = y1;
this.z = z1;
},

draw: function(a) {
// focalLength 表示当前焦距,一般可设为一个常量
var focalLength = 300;

// 把z方向扁平化
var scale = focalLength / (focalLength + this.z);
this.x2 = this.garden.vpx + this.x * scale;
this.y2 = this.garden.vpy + this.y * scale;
this.radius = this.ballR * scale;

this.garden.ctx.beginPath();
this.garden.ctx.fillStyle = 'rgba('+this.r+','+this.g+','+this.b+','+ Math.min(1, scale)+')';
// this.garden.ctx.arc(this.x2, this.y2, this.radius, 0, Math.PI * 2 , true);
// this.garden.ctx.font = 'bold ' + this.fontSize * scale+ 'px serif';
// this.garden.ctx.textAlign = "left";
// this.garden.ctx.textBaseline = "top";
// this.garden.ctx.fillText('博客园', this.x2, this.y2);
// this.garden.ctx.fill();

if(a % 4 === 3 || (a % 4 === 1 && a != 1)) {
var x1 = this.garden.balls[a - 3].x2;
var y1 = this.garden.balls[a - 3].y2;
var x2 = this.garden.balls[a - 2].x2;
var y2 = this.garden.balls[a - 2].y2;
var x3 = this.garden.balls[a - 1].x2;
var y3 = this.garden.balls[a - 1].y2;
var x4 = this.x2;
var y4 = this.y2;
this.garden.ctx.beginPath();
this.garden.ctx.fillStyle = 'rgba('+'255'+','+'0'+','+'0'+','+ Math.min(1, scale)+')';
this.garden.ctx.moveTo(x1, y1);
this.garden.ctx.lineTo(x2, y2);
this.garden.ctx.lineTo(x4, y4);
this.garden.ctx.lineTo(x3, y3);
this.garden.ctx.closePath();
console.log('%d %d %d %d %d %d %d %d', x1, y1, x2, y2, x4, y4, x3, y3)
this.garden.ctx.fill();

this.garden.ctx.beginPath();
this.garden.ctx.strokeStyle = 'rgba('+'255'+','+'0'+','+'0'+','+ Math.min(1, scale)+')';
this.garden.ctx.moveTo(x4, y4);
this.garden.ctx.lineTo(x3, y3);
this.garden.ctx.lineWidth = 1;

// this.garden.ctx.stroke();
debugger;
}

if(a !== 201) return;
this.garden.ctx.fillStyle = 'red'
this.garden.ctx.beginPath();
var x = this.garden.balls[0].x2;
var y = this.garden.balls[0].y2;
this.garden.ctx.moveTo(x, y);
for(var i = 2; i <= 200; i+=2) {
var x = this.garden.balls[i].x2;
var y = this.garden.balls[i].y2;
this.garden.ctx.lineTo(x, y);
}
this.garden.ctx.closePath();
this.garden.ctx.fill();

this.garden.ctx.beginPath();
var x = this.garden.balls[1].x2;
var y = this.garden.balls[1].y2;
this.garden.ctx.moveTo(x, y);
for(var i = 3; i <= 201; i+=2) {
var x = this.garden.balls[i].x2;
var y = this.garden.balls[i].y2;
this.garden.ctx.lineTo(x, y);
}
this.garden.ctx.closePath();
this.garden.ctx.fill();
}
}
</script>
</head>
<body bgcolor='#000'>
<canvas id='canvas' width=1000 height=500 style='background-color:rgb(0,0,0)'>
This browser does not support html5.
</canvas>
</body>
</html>

//

  再添点东西加点文字音效,表个白什么的还是不错的。
  更多demo:
  不得不感叹数学的奇妙,有时改变一个数值就能出现完全不同的效果。
  更多数学公式请参考:Famous Curves Index
 

总结

  学习3d主要是为了做粒子特效。

  这只是很基础的入门,我也只会这么点,有更进一步兴趣的可以参考岑安当耐特W·Axes或者miloyip的博客。

  岑安的JCanvas也可以看看,添加了很多事件代理。

//

rotate 3d基础的更多相关文章

  1. 从零3D基础入门XNA 4.0(2)——模型和BasicEffect

    [题外话] 上一篇文章介绍了3D开发基础与XNA开发程序的整体结构,以及使用Model类的Draw方法将模型绘制到屏幕上.本文接着上一篇文章继续,介绍XNA中模型的结构.BasicEffect的使用以 ...

  2. 从零3D基础入门XNA 4.0(1)——3D开发基础

    [题外话] 最近要做一个3D动画演示的程序,由于比较熟悉C#语言,再加上XNA对模型的支持比较好,故选择了XNA平台.不过从网上找到很多XNA的入门文章,发现大都需要一些3D基础,而我之前并没有接触过 ...

  3. wpf之3d基础

    原文:wpf之3d基础 关于3D方面的东西,本人只是浅尝辄止,从未曾在项目中使用过,相信有不少人也是基于一份兴趣去学习.这里将展示几个基本的WPF 3D编程,希望对初学者有一定帮助. 为帮助理解,这里 ...

  4. css 3d 基础知识

    css3d 总结 3d transform (3D变形)(rotate skew scale translate) 基础知识 perspective (视距,景深) perspective-origi ...

  5. 简单实用的Boom 3D基础入门教程分享

    Boom 3D可以很大限度的弥补声音设备或是环境的不足,满足您更加高级的声学体验.Boom 3D用简单明了的方式帮助您设计声音,即使您不是专业的声音编辑,也可以达到专业相似的效果. 打开Boom 3D ...

  6. 自己做得一个用于直观观察css3 transform属性中的rotate 3D效果

    这段代码还是非常简单,是看了一个老师的视频受的启发,也是调用了很多个函数,所以比较杂乱吧! <!DOCTYPE html><html lang="en">& ...

  7. 游戏开发3D基础知识

    概念学习: 向量 向量简介 我们将所有彼此平行的向量进行平移,使其起点与坐标原点重合,当某一向量的起始端与坐标原点重合,我们成该向量处于标准位置.这样,我们就可用向量的终点坐标来描述一个处于标准位置的 ...

  8. Unity 3D 基础知识方法

    A. 组件中默认的方法有如下:            Awake,Start,Update,OnGUI,OnDisable,OnEnable,OnDestory,LateUpdate,FixedUpd ...

  9. css3实践之摩天轮式图片轮播+3D正方体+3D标签云(perspective、transform-style、perspective-origin)

    本文主要通过摩天轮式图片轮播的例子来讲解与css3 3D有关的一些属性. demo预览: 摩天轮式图片轮播(貌似没兼容360 最好用chrome) 3D正方体(chrome only) 3D标签云(c ...

随机推荐

  1. [整理]PHP/HTML混写的四种方式

    PHP作为一款后端语言,为了输出给浏览器让浏览器呈现出来,无可避免的要输出HTML代码,下文介绍下我用过的三种PHP/HTML混编方法 1.单/双引号包围法 这是最初级的方法了,用法就像下面这样 &l ...

  2. 抛弃NVelocity,来玩玩Razor

    对于内容型,不易变动的东西我们都希望给它来个静态化,还有种情况就是比如新浪云不支持.net,为了能跑起我们的网站, 只能放些静态页面上面,外加jsonp来实现交互,我们知道.net中有很多模板引擎,但 ...

  3. 【hadoop】——window下elicpse连接hadoop集群基础超详细版

    1.Hadoop开发环境简介 1.1 Hadoop集群简介 Java版本:jdk-6u31-linux-i586.bin Linux系统:CentOS6.0 Hadoop版本:hadoop-1.0.0 ...

  4. EF Power Tools的Reverse Engineer Code First逆向生成Model时处理计算字段

    VS2013上使用EF Power Tools的Reverse Engineer Code First逆向生成Model时,没有处理计算字段.在保存实体时会出现错误. 可以通过修改Mapping.tt ...

  5. shell执行mysql命令

    难点主要在参数的传递方式吧,不过查资料后发现很简单. 1.使用-e参数传递命令,适用于简单语句      mysql -uuser -ppasswd -e "create database ...

  6. Android UI编程(1)——九宫格(GridView)

    (转自:http://blog.csdn.net/Thanksgining/article/details/42968847) 参考博客:http://blog.csdn.net/xyz_lmn/ar ...

  7. 盘点国内程序员不常用的热门iOS第三方库:看完,还敢自称”精通iOS开发”吗?【转载】

    综合github上各个项目的关注度与具体使用情况,涵盖功能,UI,数据库,自动化测试,编程工具等类型,看完,还敢自称”精通iOS开发”吗? https://github.com/syedhali/EZ ...

  8. 大话设计模式C++版——装饰模式

    女人常说男人喜新厌旧,只见新人笑,那闻旧人哭,但装饰模式(Decorator)却是一种结交新朋友不忘老朋友的设计模式,非常适合去古代当老公(现代是不行的,因为只能娶一个老婆了).装饰模式的本质是每一个 ...

  9. [转]How do I use variables in Oracle SQL Developer?

    本文转自:http://stackoverflow.com/questions/5653423/how-do-i-use-variables-in-oracle-sql-developer Below ...

  10. 扩展欧几里德算法 cogs.tk 2057. [ZLXOI2015]殉国

    2057. [ZLXOI2015]殉国 ★☆   输入文件:BlackHawk.in   输出文件:BlackHawk.out   评测插件时间限制:0.05 s   内存限制:256 MB [题目描 ...