3D標籤雲

開始以前

文章開始以前,應該瞭解幾個重要的公式。回憶一下咱們逝去的高中。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

結束

以上內容來自一個前端低手的我的總結與整理,不足之處,還請指正。

題外:經過此次的練習才知道,學好數學是多麼重要啊!!!

革命還沒有成功,同志還需很努力!!!

參考文章:

解析3D標籤雲,其實很簡單

相關文章
相關標籤/搜索