transformjs 玩轉星球

如你所見。這篇就是要講下使用transformjs製做星球的過程。你也能夠無視文章,直接去看源碼和在線演示:html

源碼 | 在線演示git

代碼100行多一點,直接看也沒有什麼壓力。下面分幾步講解下。github

生成球上點座標

設球心爲 (a,b,c),半徑爲r,
則球的標準方程爲 (x-a)²+(y-b)²+(z-c)²=r²瀏覽器

這裏假設球心的(0,0,0),則:
標準方程爲 x²+y²+z²=r²app

由於能夠渲染的時候再把球的本地座標轉爲世界座標進行位移,因此球心(0,0,0)即可以。dom

function randomPoints() {
    var x, y, z, j = -1, i = 0;
    for (; i < size; i++) {
        x = getRandomNumber(-250, 250);
        y = getRandomNumber(-250, 250);
        j *= -1;
        if (x * x + y * y <= r * r) {
            z = j * Math.sqrt(Math.abs(r * r - x * x - y * y));
            positions.push({x: x, y: y, z: z});
            rd_positions.push({x: x, y: y, z: z});
        }
    }
}複製代碼

上面的生成過程很取巧:ui

  • 1.隨機生成2D內的圓內的座標x和y。(x x + y y <= r * r就是表示圓內)
  • 2.根據2D維度的座標推算其屬於球面上的z

其中positions用來存放全部點的local座標,rd_positions用來之後存放投影后的座標。spa

座標轉Dom

function createImgs() {
    var i = 0,
        len = positions.length;
    for (; i < len; i++) {
        var img = document.createElement("img");
        img.style.position = "absolute";
        img.style.left = "0px";
        img.style.top = "0px";
        img.src = "../asset/star.png";
        document.body.appendChild(img);
        Transform(img,true);
        transformImg(img,i);
        img_list.push(img);
    }
}複製代碼

全部的點都對應建立一個絕對定位的圖片,而且經過Transform(img,true)給img注入transformation能力。注意第二個參數true表明關閉透視投影,由於投影下面會本身去實現。code

投影變換

function positionsProjection() {
    var index = 0,
        len=positions.length;
    for (; index < len; index++) {
        var p = positions[index];
        var rp = rd_positions[index];
        //perspective projection
        //rp.x = p.x * distance / Math.abs(camera_position.z - p.z);
        //rp.y = p.y * distance / Math.abs(camera_position.z - p.z);
        //orthogonal projection
        rp.x = p.x ;
        rp.y =  p.y ;
    }
}複製代碼

爲了簡單起見,把球心和攝像機(也能夠叫眼睛、亦或是視點)的座標分別設置爲:orm

center = {x: 300, y: 300, z: 0},
camera_position = {x: 300, y: 300, z: 500},
distance = 600,複製代碼

distance表明攝像機到投影平面的距離,攝像機就固定在球心的正前方不動,這樣進行透視投影或正交投影計算起來無比方便,免去用齊次座標、4*4矩陣的過程。以下簡單推導即可:

這裏須要注意的是,上面是透視投影的圖解,會產生近大遠小的感受。透視投影是視錐體(上圖沒有把視錐體畫出來),正交投影是立方體。
正交投影以下圖解,x和y座標投影后不變就能夠了:

能夠這麼理解:

  • 透視投影從一個點看無數個點
  • 正交投影從無數個點看無數個點

旋轉

function rotate() {
    var cx,
        z,
        i = 0,
        len=positions.length;
    for (; i < len; i++) {
        cx = positions[i].x;
        z = positions[i].z;
        positions[i].x = positions[i].x * Math.cos(step_angle) - positions[i].z * Math.sin(step_angle);
        positions[i].z = positions[i].z * Math.cos(step_angle) + cx * Math.sin(step_angle);
    }
}複製代碼

能夠看到,上面是繞y軸旋轉,因此y的座標不變,x和z須要通過下面的matrix變換:

Transformation

function transformImg(img, i) {
    var z = positions[i].z;
    img.translateX = center.x + rd_positions[i].x;
    img.translateY = center.x + rd_positions[i].y;
    //projection
    img.scaleX = img.scaleY = 0.5 * distance / Math.abs(camera_position.z - z);
    img.style.opacity =0.1+ 1 - (r - z) / (2 * r);
}

function render(){
    var i = 0,
        len=positions.length;
    for (; i < len; i++) {
        transformImg(img_list[i],i);
    }
}複製代碼

初始化和循環

function tick() {
    rotate();
    positionsProjection();
    render();
    requestAnimationFrame(tick);
}

(function () {
    randomPoints();
    createImgs();
    positionsProjection();
    tick();
})();複製代碼

經過經過上面幾行代碼串整個流程。經過requestAnimationFrame循環執行tick。

最後

爲了加深理解,你能夠把源碼 clone下來,而後改代碼實現:

  • 試試繞着z軸旋轉
  • 試試繞着x軸旋轉
  • 試試切換下透視投影和正交投影
  • 透視投影的時候試着修改攝像機的z座標
  • 正交投影的時候試着修改攝像機的z座標
  • 透視投影的時候試着修改到投影面的距離
  • 正交投影的時候試着修改到投影面的距離
  • 不使用星星素材換過其餘素材會達到意想不到的酷炫效果

第二種實現方式:試試Transform(img,false)

由於Transform第二個參數不傳,或者設置爲false的時候是打開透視投影的。
因此能夠設置img.translateZ來使用瀏覽器自身的透視投影,省去positionsProjection過程。

建立圖片的時候,使用下面的方式注入Transformation能力,

Transform(img, false);複製代碼
  • 即打開透視投影,
  • 即近大遠小,
  • 即不用本身去positionsProjection
  • 即不用本身去設置圖片的scaleX和scaleY

渲染的時候直接使用原始座標即可:

function transformImg(img, i) {
    var p = positions[i];
    img.translateX =  p.x;
    img.translateY =  p.y;
    img.translateZ = p.z;
    img.style.opacity =0.1+ 1 - (r - p.z) / (2 * r);
}

function render(){
    var i = 0,
        len=positions.length;
    for (; i < len; i++) {
        transformImg(img_list[i],i);
    }
}複製代碼

循環和初始化,再也不須要投影過程:

function tick() {
    rotate();
    render();
    requestAnimationFrame(tick);
}

(function () {
    randomPoints();
    createImgs();
    tick();
})();複製代碼

transformjs

transformjs提供了基礎的transformation能力,不與任什麼時候間和運動庫綁定。雖然官網demo簡單,可是稍微費點腦細胞就能夠作出很酷炫的效果。因此酷炫靠你們,用transformjs就對了。
傳送門:transformjs 主頁 | transformjs Github

全部例子能夠在上面找到。

相關文章
相關標籤/搜索