文章开始以前,应该了解几个重要的公式。回忆一下咱们逝去的高中。html
重要概念:前端
sinθ、cosθ的值得大小区间是[-1,1];git
弧度跟角度的转换公式是:弧度 = π/180 * 角度。github
公式:dom
一、在空间直角坐标系中,以坐标原点为球心,半径为R的球面的参数方程为:函数
x = r * sinθ * cosΦ; y = r * sinθ * sinΦ; z = r * cosθ;
说明:θ为点跟圆心的连线与z轴的夹角,Φ是点跟圆心连线在xy平面投影线与x轴的夹角(能够根据须要创建不一样的坐标系,或者取不一样的夹角,坐标的表达方式能够有不少,但原理是相似的)。this
资料:http://baike.baidu.com/item/%E7%90%83%E9%9D%A2?fr=aladdincode
二、旋转公式:htm
x1 = cosθ * x - sinθ * y; y1 = cosθ * y + sinθ * x;
说明:(x,y)是开始的坐标,θ是旋转的角度,(x1,y1)是结束的坐标。球绕某一条轴的旋转能够抽象成圆绕圆心旋转,根据旋转前的坐标和角度能够求出旋转后的坐标。blog
资料:http://www.cnblogs.com/ywxgod/archive/2010/08/06/1793609.html
了解了几个公式以后,3D标签云的旋转其实就很简单了。原理就是把标签当成一个点,经过设置不一样的θ,Φ把它们平均分布在球面的各个坐标点上。 旋转x轴或者y轴达到球体旋转的目的。z轴是一条虚拟出来的轴,它与咱们的屏幕垂直。咱们不能真的实现一个立体的球体出来,可是咱们能够经过"近大远小"达到视觉的欺骗,呈现一种立体的感受。
原理差很少说完了,接下来开始具体的代码实现过程。
设置坐标是最重要,也是相对难的一步,由于咱们要达到平均分布,避免分布太过集中或者重叠。由于半径是固定的,因此咱们从角度出发,调整角度达到平均分布的目的。接下来咱们引入一位大神的式子,我也不知道出处是那里,可是确实很好用,式子以下:
θ = arccos(((2 * i) - 1) / len - 1); Φ = θ * sqrt(len * π);
第一个式子arccos
中的((2 * i) - 1) / len - 1
实际上是一个[-1,1]区间上关于0对称分布的等差数列,经过反余弦转换成弧度值,的确是一个很高明的式子,学渣的我确实想不出来。第二个式子,是关于θ的等差(变量只有θ),不过参数sqrt(len * π)
的取值就不是很懂了,还望知情的大神告知。
具体的代码以下:
分配坐标:
var init = function() { const tagEle = cloud.querySelectorAll('.tag'), tagLen = tagEle.length; for(let i = 0; i < tagLen; i++) { // 设置随机坐标,平均分布 let a = Math.acos((2 * (i + 1) - 1) / tagLen - 1), // θ = arccos(((2*(i+1))-1)/len - 1) b = a * Math.sqrt(tagLen * Math.PI), // Φ = θ*sqrt(all * π) x = R * Math.sin(a) * Math.cos(b), // x轴坐标: x=r*sinθ*cosΦ y = R * Math.sin(a) * Math.sin(b), // y轴坐标: x=r*sinθ*cosΦ z = R * Math.cos(a), // z轴坐标: z=r*cosθ t = new tag(tagEle[i] , x , y , z); tagEle[i].style.color = '#' + Math.floor(Math.random() * 0xffffff).toString(16); // 设置随机颜色 tags.push(t); t.move(); // 初始化位置 } animate(); // 旋转 };
设置坐标及参数:
let scale = _focalLength / (_focalLength - this.z), alpha = (this.z + R) / (2 * R), ele = this.ele; ele.style.fontSize = 14 * scale + "px"; ele.style.opacity = alpha + 0.5; ele.style.zIndex = parseInt(scale * 100); // 原点是 (cloud.offsetWidth/2, cloud.offsetHeight/2) ele.style.left = this.x + cloud.offsetWidth / 2 - ele.offsetWidth/2 + "px"; ele.style.top = this.y + cloud.offsetHeight / 2 - ele.offsetHeight/2 + "px";
scale、alpha 都是取关于z坐标的递增函数,因此能够根据须要调整函数达到更好的显示效果。
开始的时候咱们简单介绍了圆的旋转,球的旋转其实相似圆。例如绕z轴旋转,其实改变的是x,y坐标的值,z坐标的值并无变化。理解了这一层,咱们能够得出绕x轴旋转的和y轴旋转的函数,分别为:
/* 绕x轴旋转 y = ycosθ - zsinθ; z = ysinθ + zcosθ; */ function rotateX() { let cos = Math.cos(angleX), sin = Math.sin(angleX); tags.forEach(function(tag) { let y = tag.y * cos - tag.z * sin, z = tag.z * cos + tag.y * sin; tag.y = y; tag.z = z; }) }; /* 绕y轴旋转 x = xcosθ - zsinθ; z = xsinθ + zcosθ; */ function rotateY() { let cos = Math.cos(angleY), sin = Math.sin(angleY); tags.forEach(function(tag) { let x = tag.x * cos - tag.z * sin, z = tag.z * cos + tag.x * sin; tag.x = x; tag.z = z; }) };
因而咱们就能经过控制angleX、angleY的大小来达到旋转的目的了,值越大,单位时间旋转的角度越大,也就是旋转的速度越快。固然,旋转360度跟没旋转的效果是同样的,因此咱们应该合理的设置单位时间和每一次旋转的角度值,让咱们的眼睛知道它是个球,它在转!!!
这是我在百度前端学院的一个课程练习的代码,代码所有放在Github上,但愿对您有一点帮助。同时,Github求关注!!!
预览效果: http://alvin-liu.github.io/FrontCode/src/tagcloud/
代码地址: https://github.com/Alvin-Liu/FrontCode/blob/gh-pages/src/tagcloud/js/tagcloud.js
以上内容来自一个前端低手的我的总结与整理,不足之处,还请指正。
题外:经过此次的练习才知道,学好数学是多么重要啊!!!
革命还没有成功,同志还需很努力!!!
参考文章: