标签:
第二篇来学习如何用canvas打造3d标签云。
demo -> 3d标签云
参考资料:
前面我们已经构造了一个三维空间旋转的模板(其实Ball类初始化时有bug...),如何构造一个标签云?思考ing...
其实就是构造一个球体,标签放在球体上,然后每个标签旋转即可。也就是说,我们可以把每个粒子当做是一个标签。
怎样构造球体?前面一篇中我用确定z参数,然后枚举x获取y的方法获得球面坐标,其实有更好的方法:

不懂数学,试了下觉得角度的取值有两种方法:
可以验证x*x+y*y+z*z确实等于R*R。
有了公式,我们可以枚举角度获得坐标。
如果有n个点,我需要平均分配在球面上,怎么做?我们引入第二个公式:

var all = 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);
var y = 150 * Math.sin(a1) * Math.sin(a2);
var z = 150 * Math.cos(a1);
garden.createBall(x, y, z);
}
然后就差不多了,以前的demo我都是代码自己控制旋转角度,加个事件的监听:
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;
});
不考虑兼容...仅在chrome下测试。然后每帧绘制的时候,前面的demo是绘制小球,现在就是fillText,根据scale改变text的大小、透明度等。其实应该是个“标签”,应该有点击跳转的功能,无奈我的css能力为0...
完整代码:
<!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);
// }
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标签云插件的实现也大同小异,无非是用div代替canvas,然后设置原点为div的中心,原html里写好链接,然后监听,实现a标签的位置变化...不会css是硬伤...
标签:
原文地址:http://www.cnblogs.com/zichi/p/4245494.html