作为IT小生制造那支能够在头顶上旋转的球有一定难度,但可以让其在电脑中为人们表演。
这篇博客描述的是如何实现一个3D球的效果,主要是通过HTML5里面的canvas来实现。整片文章属于原创若有相同纯属有缘,在做这个效果之前简略的学习了一下《3D数学基础》收获颇丰,因为大学里面学的数学基本上都还给了老师。闲话少说,先看实现的效果如图一:
图一
要想使得整个球动起来,需要先激活。然后再改变他的旋转方向或者改变这个小球的半径。看启动后的效果,如图二:
图二
这里为什么要设定一个2秒冻结,因为如果立刻冻结的话整个求的样式就无法及时改变所以就采取了这样一个迂回之策。详见代码如下:
- <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。
由代码注释较详细,具体就不再赘述。哪位大牛有好的算法实现希望能够多多交流,共同学习,共同进步,欢迎拍砖。
源文:http://blog.csdn.net/qingfeilee/article/details/7437972,使用代码请保留作者署名,谢谢!