您的位置:首页 > Web前端 > HTML5

昂首那瞬间,球已停止转动——HTML5实现3D球效果 .

2012-04-12 14:46 423 查看
曲终人即散,每当看到这个词汇总是略感的伤感。现在的我们周围的生活节奏是如此之快,尤其是生活在北京这个经济高速发展的地方。有时曲可能尚未终,可是人已经散!当我们下班后漫步在繁华都市的夜幕之下,总是略感孤独和伤感。或许是由于工作压力,也或许是因为亲人爱人不在身边,更或许是自己的心一次次的被冲击。京城周末的夜晚丝毫没有因为是夜晚而变得静谧,而我总是急匆匆的上了回家的夜班车。或许我应该停下脚步去聆听、去感受、去享受,但是我却找不到一个这样的地方,唯独小区里的那张长长的木椅。喧嚣的夜幕总是让我想起电视上看见的迪厅里悬挂的那个水晶球,如此的耀眼,如此的抢眼。人们在球的衬托下排遣着自己的疲惫,宣泄着自己的寂寞,放纵着自己的形态……
作为IT小生制造那支能够在头顶上旋转的球有一定难度,但可以让其在电脑中为人们表演。
这篇博客描述的是如何实现一个3D球的效果,主要是通过HTML5里面的canvas来实现。整片文章属于原创若有相同纯属有缘,在做这个效果之前简略的学习了一下《3D数学基础》收获颇丰,因为大学里面学的数学基本上都还给了老师。闲话少说,先看实现的效果如图一:



图一
要想使得整个球动起来,需要先激活。然后再改变他的旋转方向或者改变这个小球的半径。看启动后的效果,如图二:





图二

这里为什么要设定一个2秒冻结,因为如果立刻冻结的话整个求的样式就无法及时改变所以就采取了这样一个迂回之策。详见代码如下:

[html]
view plaincopyprint?

<html>
<head>
<title>
再回首,那只小球已停止转动——HTML5实现3D球效果
</title>
<meta charset="utf-8">
<style type="text/css">
#box{
border:2px solid #f60; margin:0 auto;
}
</style>
<script>
var spaceX = 30; //X方向的密度
var spaceY = 30; //Y方向的密度
var PI = Math.PI; //数学角度π
var radius = 200; //球的半径
var radian = PI / 180; //弧度
var speedX = 0; //X方向的速度
var speedY = 0; //Y方向的速度
var offsetX = 300; //X方向的偏移量相当于将球的中心X坐标移之到画布中央
var offsetY = 300; //Y方向的偏移量相当于将球的中心Y坐标移之到画布中央
var spheres = new Array(); //存储像素点
var canvas; //画布
var context; //上下文
var focalLength = 300; //控制球距离屏幕的距离
var start = true; //是否启动
var sx = 0; //sinx
var cx = 0; //cosx
var sy = 0; //siny
var cy = 0; //cosy
var sz = 0; //sinz
var cz = 0; //cosz
var innerStaColor = "GREEN"; //表示内部颜色
var outerStaColor = "RED"; //外部颜色
var objectRadius = 10; //绘制原点半径
var scaleRatio = 0;

var cameraView = {
x: 0,
y: 0,
z: 0,
rotX: 0,
rotY: 0,
rotZ: 0
}; //视角角度
/**
author:qingfeilee
date:2012-03-28
description:初始化系统画布信息
**/
function initCanvas() {
try{
canvas = document.getElementById("sphere");
context = canvas.getContext("2d");
}catch(e){
document.getElementById("tip_info").innerHTML = "您的浏览器不支持!";
}
}
/**
author:qingfeilee
date:2012-03-28
description:初始化小球实体
**/
function initSphere() {
for (var i = spaceY; i < 180; i += spaceY) {
for (var angle = 0; angle < 360; angle += spaceX) {
var object = {};
var x = Math.sin(radian * i) * radius;

object.x = Math.cos(angle * radian) * x;
object.y = Math.cos(radian * i) * radius;
object.z = Math.sin(angle * radian) * x;
object.glow = .5; //亮度的范围
spheres.push(object);
}
}
}
/**
author:qingfeilee
date:2012-03-28
description:初始化系统函数
**/
function init() {
initCanvas();
initSphere();
setInterval(this.update, 1000 / 60, this);
setTimeout(function() {
start = false;
},
1000);
}
/**
author:qingfeilee
date:2012-03-28
description:设置整个大球的运转速度
**/
function setSpeed(speedX, speedY) {
this.speedX = speedX;
this.speedY = speedY;
}
/**
author:qingfeilee
date:2012-03-28
description:更新整个球的状态以实现动态效果
**/
function update() {
if (start) {
setParam();
}
}
/**
author:qingfeilee
date:2012-03-28
description:设置各个小球的属性
**/
function setParam() {
//根据速度大小计算出一次旋转的角度大小
var rotYstep = speedX / 10000;
var rotXstep = speedY / 10000;
cameraView.rotY = rotYstep;
cameraView.rotX = -rotXstep;
//计算出对应的cos和sin
sx = Math.sin(cameraView.rotX);
cx = Math.cos(cameraView.rotX);
sy = Math.sin(cameraView.rotY);
cy = Math.cos(cameraView.rotY);
sz = Math.sin(cameraView.rotZ);
cz = Math.cos(cameraView.rotZ);

// 设置画布的效果
context.fillStyle = 'rgba(0,0,0,0.1)';
context.fillRect(0, 0, canvas.width, canvas.height);

var l = spheres.length - 1;

for (var i = l,
obj; obj = spheres[i]; i--) {
render(obj);
}
}
/**
author:qingfeilee
date:2012-03-28
description:渲染整个画布
**/
function render(object) {
var xy, xz, yx, yz, zx, zy;

// 计算出物体的相对于照相机的位置
var x = object.x - cameraView.x;
var y = object.y - cameraView.y;
var z = object.z - cameraView.z;

// 绕X轴旋转
xy = cx * y - sx * z;
xz = sx * y + cx * z;
// 绕Y轴旋转
yz = cy * xz - sy * x;
yx = sy * xz + cy * x;
// 绕Z轴旋转
zx = cz * yx - sz * xy;
zy = sz * yx + cz * xy;
//给各个球重新定位
object.x = zx;
object.y = zy;
object.z = yz;

//根据z轴数据来缩放球
scaleRatio = focalLength / (focalLength + yz);
scale = scaleRatio;

if (object.glow > .5) {
object.glow -= .02;
}

var sphereStyle = context.createRadialGradient(offsetX + object.x * scaleRatio, offsetY + object.y * scaleRatio,
scaleRatio * .5, offsetX + object.x * scaleRatio, offsetY + object.y * scaleRatio, scaleRatio * objectRadius * .5);
sphereStyle.addColorStop(0, innerStaColor);
sphereStyle.addColorStop(object.glow, outerStaColor);
sphereStyle.addColorStop(1, 'rgba(0,0,0,0)');

context.fillStyle = sphereStyle;
context.fillRect(offsetX + object.x * scaleRatio - scaleRatio * objectRadius * .5,
offsetY + object.y * scaleRatio - scaleRatio * objectRadius * .5, scaleRatio * objectRadius, scaleRatio * objectRadius);
document.getElementById("tip_info").innerHTML = "当前速度:"+speedX+" "+ speedY+" 小球半径:"+objectRadius;

}
/**
author:qingfeilee
date:2012-03-28
description:冻结/激活真个大球状态
**/
function startOrPause() {
if (start) {
setTimeout(function() {
start = false;
},
2000);
document.getElementById("swi").innerHTML = "激活";
innerStaColor = "GREEN";
outerStaColor = "RED";
} else {
start = true;
document.getElementById("swi").innerHTML = "2秒后冻结";
innerStaColor = "RED";
outerStaColor = "GREEN";
}
}
/**
author:qingfeilee
date:2012-03-28
description:改变球的大小
**/
function changeObjectRadius(val) {
this.objectRadius = val;
}
window.addEventListener("load", init, true);
</script>
</head>
<body>
<div id="box" style="width:600px; height:600px">
<canvas id="sphere" width="600" height="600" style="background:#0066FF">
</canvas>
<div align="center">
<button id="swi" onclick="startOrPause()">
激活
</button>
<button onclick="setSpeed(-150,0)">
向东
</button>
<button onclick="setSpeed(150,0)">
向西
</button>
<button onclick="setSpeed(0,-150)">
向南
</button>
<button onclick="setSpeed(0,150)">
向北
</button>
小球大小:
<input type="range" min="10" max="30" value="10" step="2" onchange="changeObjectRadius(this.value)"/>
</div>
<div align="center">
<a href='http://blog.csdn.net/qingfeilee/'>
阿飞blog
</a>
<a id="tip_info">
</a>
</div>

</div>
</body>
</html>

<html>
<head>
<title>
再回首,那只小球已停止转动——HTML5实现3D球效果
</title>
<meta charset="utf-8">
<style type="text/css">
#box{
border:2px solid #f60; margin:0 auto;
}
</style>
<script>
var spaceX = 30; //X方向的密度
var spaceY = 30; //Y方向的密度
var PI = Math.PI; //数学角度π
var radius = 200; //球的半径
var radian = PI / 180; //弧度
var speedX = 0; //X方向的速度
var speedY = 0; //Y方向的速度
var offsetX = 300; //X方向的偏移量相当于将球的中心X坐标移之到画布中央
var offsetY = 300; //Y方向的偏移量相当于将球的中心Y坐标移之到画布中央
var spheres = new Array(); //存储像素点
var canvas; //画布
var context; //上下文
var focalLength = 300; //控制球距离屏幕的距离
var start = true; //是否启动
var sx = 0; //sinx
var cx = 0; //cosx
var sy = 0; //siny
var cy = 0; //cosy
var sz = 0; //sinz
var cz = 0; //cosz
var innerStaColor = "GREEN"; //表示内部颜色
var outerStaColor = "RED"; //外部颜色
var objectRadius = 10; //绘制原点半径
var scaleRatio = 0;

var cameraView = {
x: 0,
y: 0,
z: 0,
rotX: 0,
rotY: 0,
rotZ: 0
}; //视角角度
/**
author:qingfeilee
date:2012-03-28
description:初始化系统画布信息
**/
function initCanvas() {
try{
canvas = document.getElementById("sphere");
context = canvas.getContext("2d");
}catch(e){
document.getElementById("tip_info").innerHTML = "您的浏览器不支持!";
}
}
/**
author:qingfeilee
date:2012-03-28
description:初始化小球实体
**/
function initSphere() {
for (var i = spaceY; i < 180; i += spaceY) {
for (var angle = 0; angle < 360; angle += spaceX) {
var object = {};
var x = Math.sin(radian * i) * radius;

object.x = Math.cos(angle * radian) * x;
object.y = Math.cos(radian * i) * radius;
object.z = Math.sin(angle * radian) * x;
object.glow = .5; //亮度的范围
spheres.push(object);
}
}
}
/**
author:qingfeilee
date:2012-03-28
description:初始化系统函数
**/
function init() {
initCanvas();
initSphere();
setInterval(this.update, 1000 / 60, this);
setTimeout(function() {
start = false;
},
1000);
}
/**
author:qingfeilee
date:2012-03-28
description:设置整个大球的运转速度
**/
function setSpeed(speedX, speedY) {
this.speedX = speedX;
this.speedY = speedY;
}
/**
author:qingfeilee
date:2012-03-28
description:更新整个球的状态以实现动态效果
**/
function update() {
if (start) {
setParam();
}
}
/**
author:qingfeilee
date:2012-03-28
description:设置各个小球的属性
**/
function setParam() {
//根据速度大小计算出一次旋转的角度大小
var rotYstep = speedX / 10000;
var rotXstep = speedY / 10000;
cameraView.rotY = rotYstep;
cameraView.rotX = -rotXstep;
//计算出对应的cos和sin
sx = Math.sin(cameraView.rotX);
cx = Math.cos(cameraView.rotX);
sy = Math.sin(cameraView.rotY);
cy = Math.cos(cameraView.rotY);
sz = Math.sin(cameraView.rotZ);
cz = Math.cos(cameraView.rotZ);

// 设置画布的效果
context.fillStyle = 'rgba(0,0,0,0.1)';
context.fillRect(0, 0, canvas.width, canvas.height);

var l = spheres.length - 1;

for (var i = l,
obj; obj = spheres[i]; i--) {
render(obj);
}
}
/**
author:qingfeilee
date:2012-03-28
description:渲染整个画布
**/
function render(object) {
var xy, xz, yx, yz, zx, zy;

// 计算出物体的相对于照相机的位置
var x = object.x - cameraView.x;
var y = object.y - cameraView.y;
var z = object.z - cameraView.z;

// 绕X轴旋转
xy = cx * y - sx * z;
xz = sx * y + cx * z;
// 绕Y轴旋转
yz = cy * xz - sy * x;
yx = sy * xz + cy * x;
// 绕Z轴旋转
zx = cz * yx - sz * xy;
zy = sz * yx + cz * xy;
//给各个球重新定位
object.x = zx;
object.y = zy;
object.z = yz;

//根据z轴数据来缩放球
scaleRatio = focalLength / (focalLength + yz);
scale = scaleRatio;

if (object.glow > .5) {
object.glow -= .02;
}

var sphereStyle = context.createRadialGradient(offsetX + object.x * scaleRatio, offsetY + object.y * scaleRatio,
scaleRatio * .5, offsetX + object.x * scaleRatio, offsetY + object.y * scaleRatio, scaleRatio * objectRadius * .5);
sphereStyle.addColorStop(0, innerStaColor);
sphereStyle.addColorStop(object.glow, outerStaColor);
sphereStyle.addColorStop(1, 'rgba(0,0,0,0)');

context.fillStyle = sphereStyle;
context.fillRect(offsetX + object.x * scaleRatio - scaleRatio * objectRadius * .5,
offsetY + object.y * scaleRatio - scaleRatio * objectRadius * .5, scaleRatio * objectRadius, scaleRatio * objectRadius);
document.getElementById("tip_info").innerHTML = "当前速度:"+speedX+" "+ speedY+" 小球半径:"+objectRadius;

}
/**
author:qingfeilee
date:2012-03-28
description:冻结/激活真个大球状态
**/
function startOrPause() {
if (start) {
setTimeout(function() {
start = false;
},
2000);
document.getElementById("swi").innerHTML = "激活";
innerStaColor = "GREEN";
outerStaColor = "RED";
} else {
start = true;
document.getElementById("swi").innerHTML = "2秒后冻结";
innerStaColor = "RED";
outerStaColor = "GREEN";
}
}
/**
author:qingfeilee
date:2012-03-28
description:改变球的大小
**/
function changeObjectRadius(val) {
this.objectRadius = val;
}
window.addEventListener("load", init, true);
</script>
</head>
<body>
<div id="box" style="width:600px; height:600px">
<canvas id="sphere" width="600" height="600" style="background:#0066FF">
</canvas>
<div align="center">
<button id="swi" onclick="startOrPause()">
激活
</button>
<button onclick="setSpeed(-150,0)">
向东
</button>
<button onclick="setSpeed(150,0)">
向西
</button>
<button onclick="setSpeed(0,-150)">
向南
</button>
<button onclick="setSpeed(0,150)">
向北
</button>
小球大小:
<input type="range" min="10" max="30" value="10" step="2" onchange="changeObjectRadius(this.value)"/>
</div>
<div align="center">
<a href='http://blog.csdn.net/qingfeilee/'>
阿飞blog
</a>
<a id="tip_info">
</a>
</div>

</div>
</body>
</html>

仅仅看代码可能一时半会看不出端倪,下面将这块儿的几何图形贴上供参考,由于非常外行所以图形画的比较粗糙,如下图三:



图三

通过上面的图形我们很容易得出initSphere函数里面的一些计算。这样就可以将球的各个点进行了初始化。
然后就是旋转,我们以绕Y轴旋转为例。假设当前时刻某点P(X,Y,Z)绕Y轴旋转α度,到P1(X1,Y1,Z1)这两者有何关系呢?不再赘述直接给出公式:X1 = X*cosα + Z*sinα;Y1 = Y;Z1=-X*sinα+Z*cosα不懂的复习一下高等书写,O(∩_∩)O哈哈~。绕其他轴旋转同理,这样就得到了update方法里面的算法。当然在3D中还存在一个视角角度,就相当于摄像机一样,本例子中默认是是0,0,0。
由代码注释较详细,具体就不再赘述。哪位大牛有好的算法实现希望能够多多交流,共同学习,共同进步,欢迎拍砖。
源文:/article/8774981.html,使用代码请保留作者署名,谢谢!
附件:http://download.csdn.net/detail/qingfeilee/4208565
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: