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});
        }
    }
}

上面的生成过程很取巧:spa

  • 1.随机生成2D内的圆内的坐标x和y。(x x + y y <= r * r就是表示圆内)code

  • 2.根据2D维度的坐标推算其属于球面上的zorm

其中positions用来存放全部点的local坐标,rd_positions用来之后存放投影后的坐标。htm

坐标转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表明关闭透视投影,由于投影下面会本身去实现。

投影变换

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 ;
    }
}

为了简单起见,把球心和摄像机(也能够叫眼睛、亦或是视点)的坐标分别设置为:

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

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

相关文章
相关标签/搜索