首页 > 代码库 > rotate 3d II
rotate 3d II
第二篇来学习如何用canvas打造3d标签云。
demo -> 3d标签云
参考资料:
- 解析3D标签云,其实很简单
- rotate 3d I
前面我们已经构造了一个三维空间旋转的模板(其实Ball类初始化时有bug...),如何构造一个标签云?思考ing...
其实就是构造一个球体,标签放在球体上,然后每个标签旋转即可。也就是说,我们可以把每个粒子当做是一个标签。
怎样构造球体?前面一篇中我用确定z参数,然后枚举x获取y的方法获得球面坐标,其实有更好的方法:
不懂数学,试了下觉得角度的取值有两种方法:
- (0 <= θ <= PI && 0 <= Φ <= 2 * PI)
- (0 <= θ <= 2 * PI && 0 <= Φ <= PI)
可以验证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是硬伤...
rotate 3d II
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。