Three中的動畫實現-[three.js]


Table Of Content
動畫原理
js中動畫實現原理setInterval
js中動畫實現新方法requestAnimationFrame
一個示例

動畫原理

動畫的本質實際上就是快速地不斷變化的圖片,每張圖片對比前后兩張圖片有細微的變化。整個連續的過程達到一定速度在我們人眼看來就是動畫。人眼一般能區分的幀率是24幀。過低給人的感覺就會很不流暢。
和我們小時候看的動畫書原理一樣

![](https://img2018.cnblogs.com/blog/1735896/202001/1735896-20200101155537787-253121630.gif)

### js中動畫實現原理 在js中,要實現動畫,我們首先需要做的第一件事情,就是找到一個能夠以特定時間間隔的方法重復渲染畫面,在Three.js中就是重繪場景Scene。在HMTL5之前,JavaScript中的做法是使用JS API提供的定時器,`setInterval(function,interval)`方法,通過該方法,我們可以指定一個定時器,讓他間隔的調用定時器函數,例如每隔100毫秒調用一次,在定時器的回調函數中,定義場景渲染。 但是通過`setInterval`方法,有一些很致命的問題,那就是該定時器函數並不會考慮瀏覽器發生的動作,例如,打開一個新的頁面,該方法在后台還是會不斷的執行。此外,`setInterval`函數並不是和重繪屏幕同步進行,這將會導致很高的CPU占用,性能會很差。
### js中動畫實現新方法 為了解決上述問題,現代瀏覽器提供了一個解決方案,那就是`requestAnimationFrame`方法。 通過該方法,你可以通過調用瀏覽器定義的方法,指定一個定時器。你的任何重繪都將在該提供的方法中進行。瀏覽器將會確保流暢高效地繪制場景,該方法的使用也非常簡單,你只需要創建一個處理渲染的函數。如下示例: ```javascript function renderScene(){ requestAnimationFrame(renderScene); renderer.render(scene,camera); } ``` 在該函數中,我們不斷的調用本身,不斷的重繪場景,實現動畫效果。
### 一個示例: 以下是一個動畫示例:
![](https://img2018.cnblogs.com/blog/1735896/202001/1735896-20200101223727644-1132905605.gif)

模型部分代碼如下

function draw(scene,clock) {
var textureLoader  = new THREE.TextureLoader();
//太陽
var sun = new THREE.SphereGeometry(50,50,50);
var sunMaterial = new THREE.MeshPhongMaterial({
    map:textureLoader.load('./assets/2k_sun.jpg'),
    color:0xffaaaa,
    // wireframe:true
});
var sunModel = new THREE.Mesh(sun,sunMaterial);

sunModel.position.set(0,60,0)
sunModel.castShadow  = true;
//這個函數 每幀都會在渲染之前執行一次  所有的網格模型 都有這個方法
sunModel.onBeforeRender = function (renderer, scene, camera) {
    var elapsed =  clock.getElapsedTime();
    this.rotation.y += -Math.PI/120

}

scene.add(sunModel)

//地球
var sphereGeometry = new THREE.SphereGeometry(20,40,50);
var sphereMaterial = new THREE.MeshPhongMaterial({
    map:textureLoader.load('./assets/earth_atmos_2048.jpg'),
    specular:0x333333,
});
var sphere = new THREE.Mesh(sphereGeometry,sphereMaterial);
// sphere.position.y = 60
sphere.castShadow  = true;

//這個函數 每幀都會在渲染之前執行一次  所有的網格模型 都有這個方法
sphere.onBeforeRender = function (renderer, scene, camera) {
    var elapsed =  clock.getElapsedTime();
    this.position.set(Math.sin(elapsed)*200,60,Math.cos(elapsed)*200);
    this.rotation.y += -Math.PI/120

}

scene.add(sphere)

//月球
var moonGeometry = new THREE.SphereGeometry(5,40,50);
var moonMaterial = new THREE.MeshPhongMaterial({
    map:textureLoader.load('./assets/moon_1024.jpg'),
    specular:0x333333,

});
var moon = new THREE.Mesh(moonGeometry,moonMaterial);
// sphere.position.y = 60
moon.castShadow  = true;

//這個函數 每幀都會在渲染之前執行一次  所有的網格模型 都有這個方法
moon.onBeforeRender = function (renderer, scene, camera) {
    var elapsed =  clock.getElapsedTime();
    this.position.set((Math.sin(elapsed*5)*50+Math.sin(elapsed)*200),60,(Math.cos(elapsed)*200+Math.cos(elapsed*5)*50))
}

scene.add(moon)

}

控制更新以及初始化如下

function init() {
    var stats = initStats();
    var renderer = initRenderer();
    var camera = initCamera();
    var scene = new THREE.Scene();
    var clock = new THREE.Clock();
    // var elapsed =  Math.acos(clock.getDelta())

    initDefaultLighting(scene);
    initModel()
    initControls();
    render();
    draw(scene,clock);

    function initModel() {
        //輔助工具
        var helper = new THREE.AxesHelper(900);
        scene.add(helper);
        // var map = new THREE.TextureLoader().load("./assets/jay.jag");
        //外部盒子
        // var material = new THREE.MeshLambertMaterial({
        //     // map: map
        //     color: 0xffffff,
        // });
        // material.transparent = true;
        // material.opacity = 0.4;

        //--------------------------------地板--------------------------
        var planeGeometry = new THREE.PlaneGeometry(1000, 1000, 50, 50);
        var planeMaterial = new THREE.MeshBasicMaterial({
            color: 0xff0000,
            wireframe: true
        });
        planeMaterial.transparent = true;
        planeMaterial.opacity = 0.2;

        plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 0;
        plane.position.y = -6;
        plane.position.z = 0;


        //告訴底部平面需要接收陰影
        plane.receiveShadow = true;

        scene.add(plane);
        // scene.add(PlaneSegs);
        //--------------------------------地板end-----------------------
    }

    //初始化控制器
    var obtControls; //定義控制器變量
    function initControls() {
        //定義控制器核心           
        obtControls = new THREE.OrbitControls(camera, renderer.domElement);

        // 如果使用animate方法時,將此函數刪除
        // controls.addEventListener('change', render);
        //以下都是為了滿足各種需求的各種控制器配置參數
        obtControls.enableDampling = true; //使動畫循環使用時阻尼或自轉 意思是否有慣性
        obtControls.enableZoom = true; //是否允許縮放
        obtControls.enablePan = true; //是否開啟鼠標右鍵拖拽
        obtControls.autoRotate = false; //是否允許自動旋轉
        obtControls.dampingFactor = 0.25; //動態阻尼系數:就是鼠標拖拽旋轉靈敏度
        obtControls.minDistance = 0; //設置相機距離原點的最近距離;
        obtControls.maxDistance = 1000; //設置相機距離原點的最遠距離;

    }
    //控制更新
    function render() {
        stats.update();
        // fpControls.update(clock.getDelta());
        obtControls.update(clock.getDelta());
        requestAnimationFrame(render);
        renderer.render(scene, camera)
    }

}

該demo的完整代碼在這里:Link


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM