1. 程式人生 > >Three.js進階篇之2

Three.js進階篇之2

動畫原理

在這裡,我們將動態畫面簡稱為動畫(animation)。正如動畫片的原理一樣,動畫的本質是利用了人眼的視覺暫留特性,快速地變換畫面,從而產生物體在運動的假象。而對於Three.js程式而言,動畫的實現也是通過在每秒中多次重繪畫面實現的。

為了衡量畫面切換速度,引入了每秒幀數FPS(Frames Per Second)的概念,是指每秒畫面重繪的次數。FPS越大,則動畫效果越平滑,當FPS小於20時,一般就能明顯感受到畫面的卡滯現象。

那麼FPS是不是越大越好呢?其實也未必。當FPS足夠大(比如達到60),再增加幀數人眼也不會感受到明顯的變化,反而相應地就要消耗更多資源(比如電影的膠片就需要更長了,或是電腦重新整理畫面需要消耗計算資源等等)。因此,選擇一個適中的FPS即可。

NTSC標準的電視FPS是30,PAL標準的電視FPS是25,電影的FPS標準為24。而對於Three.js動畫而言,一般FPS在30到60之間都是可取的。

setInterval方法

如果要設定特定的FPS(雖然嚴格來說,即使使用這種方法,JavaScript也不能保證幀數精確性),可以使用JavaScript DOM定義的方法:

setInterval(func, msec)

其中,func是每過msec毫秒執行的函式,如果將func定義為重繪畫面的函式,就能實現動畫效果。setInterval函式返回一個id,如果需要停止重繪,需要使用clearInterval方法,並傳入該id,具體的做法為:

首先,在init函式中定義每20毫秒執行draw函式的setInterval,返回值記錄在全域性變數id中:

id = setInterval(draw,20);

draw函式中,我們首先設定在每幀中的變化(畢竟,如果每幀都是相同的,即使重繪再多次,還是不會有動畫的效果),這裡我們讓場景中的長方體繞y軸轉動。然後,執行渲染:

function draw(){
    mesh.rotation.y =(mesh.rotation.y +0.01)%(Math.PI *2);
    renderer.render(scene, camera);}

這樣,每20毫秒就會呼叫一次draw函式,改變長方體的旋轉值,然後進行重繪。最終得到的效果就是FPS為50

的旋轉長方體。

我們在HTML中新增一個按鈕,按下後停止動畫:

<buttonid="stopBtn"onclick="stop()">Stop</button>

對應的stop函式為:

function stop(){if(id !==null){
        clearInterval(id);
        id =null;}}

requestAnimationFrame方法

大多數時候,我們並不在意多久重繪一次,這時候就適合用requestAnimationFrame方法了。它告訴瀏覽器在合適的時候呼叫指定函式,通常可能達到60FPS。

requestAnimationFrame同樣有對應的cancelAnimationFrame取消動畫:

function stop(){if(id !==null){
        cancelAnimationFrame(id);
        id =null;}}

setInterval不同的是,由於requestAnimationFrame只請求一幀畫面,因此,除了在init函式中需要呼叫,在被其呼叫的函式中需要再次呼叫requestAnimationFrame

function draw(){
    mesh.rotation.y =(mesh.rotation.y +0.01)%(Math.PI *2);
    renderer.render(scene, camera);
    id = requestAnimationFrame(draw);}

因為requestAnimationFrame較為“年輕”,因而一些老的瀏覽器使用的是試驗期的名字:mozRequestAnimationFramewebkitRequestAnimationFramemsRequestAnimationFrame,為了支援這些瀏覽器,我們最好在呼叫之前,先判斷是否定義了requestAnimationFrame以及上述函式:

var requestAnimationFrame = window.requestAnimationFrame 
        || window.mozRequestAnimationFrame
        || window.webkitRequestAnimationFrame
        || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;

如何取捨

setInterval方法與requestAnimationFrame方法的區別較為微妙。一方面,最明顯的差別表現在setInterval可以手動設定FPS,而requestAnimationFrame則會自動設定FPS;但另一方面,即使是setInterval也不能保證按照給定的FPS執行,在瀏覽器處理繁忙時,很可能低於設定值。當瀏覽器達不到設定的呼叫週期時,requestAnimationFrame採用跳過某些幀的方式來表現動畫,雖然會有卡滯的效果但是整體速度不會拖慢,而setInterval會因此使整個程式放慢執行,但是每一幀都會繪製出來;

總而言之,requestAnimationFrame適用於對於時間較為敏感的環境(但是動畫邏輯更加複雜),而setInterval則可在保證程式的運算不至於導致延遲的情況下提供更加簡潔的邏輯(無需自行處理時間)。

stat.js是Three.js的作者Mr. Doob的另一個有用的JavaScript庫。很多情況下,我們希望知道實時的FPS資訊,從而更好地監測動畫效果。這時候,stat.js就能提供一個很好的幫助,它佔據螢幕中的一小塊位置(如左上角),效果為:,單擊後顯示每幀渲染時間:

<scripttype="text/javascript"src="stat.js"></script>

在頁面初始化的時候,對其初始化並將其新增至螢幕一角。這裡,我們以右上角為例:

var stat =null;function init(){
    stat =newStats();
    stat.domElement.style.position ='absolute';
    stat.domElement.style.right ='0px';
    stat.domElement.style.top ='0px';
    document.body.appendChild(stat.domElement);// Three.js init ...}

然後,在上一節介紹的動畫重繪函式draw中呼叫stat.begin();stat.end();分別表示一幀的開始與結束:

function draw(){
    stat.begin();

    mesh.rotation.y =(mesh.rotation.y +0.01)%(Math.PI *2);
    renderer.render(scene, camera);

    stat.end();}

最終就能得到FPS效果了。

本節我們將使用一個彈球的例子來完整地學習使用動畫效果。

首先,我們把通用的框架部分寫好,按照4.1節的方法實現動畫重繪函式,並按6.2節的方法加入stat.js庫:

var requestAnimationFrame = window.requestAnimationFrame 
        || window.mozRequestAnimationFrame
        || window.webkitRequestAnimationFrame
        || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;var scene =null;var camera =null;var renderer =null;var id =null;var stat =null;function init(){
    stat =newStats();
    stat.domElement.style.position ='absolute';
    stat.domElement.style.right ='0px';
    stat.domElement.style.top ='0px';
    document.body.appendChild(stat.domElement);

    renderer =new THREE.WebGLRenderer({
        canvas: document.getElementById('mainCanvas')});
    scene =new THREE.Scene();

    id = requestAnimationFrame(draw);}function draw(){
    stat.begin();

    renderer.render(scene, camera);

    id = requestAnimationFrame(draw);

    stat.end();}function stop(){if(id !==null){
        cancelAnimationFrame(id);
        id =null;}}

然後,為了實現彈球彈動的效果,我們建立一個球體作為彈球模型,建立一個平面作為彈球反彈的平面。為了在draw函式中改變彈球的位置,我們可以宣告一個全域性變數ballMesh,以及彈球半徑ballRadius

var ballMesh =null;var ballRadius =0.5;

init函式中新增球體和平面,使彈球位於平面上,平面採用棋盤格影象作材質:

// ball
ballMesh =new THREE.Mesh(new THREE.SphereGeometry(ballRadius,16,8),new THREE.MeshLambertMaterial({
        color:0xffff00}));
ballMesh.position.y = ballRadius;
scene.add(ballMesh);// planevar texture = THREE.ImageUtils.loadTexture('../img/chess.png',{},function(){
    renderer.render(scene, camera);});
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(4,4);var plane =new THREE.Mesh(new THREE.PlaneGeometry(5,5),new THREE.MeshLambertMaterial({map: texture}));
plane.rotation.x =-Math.PI /2;
scene.add(plane);

現在,每幀繪製的都是相同的效果:

為了記錄彈球的狀態,我們至少需要位置、速度、加速度三個向量,為了簡單起見,這裡彈球只做豎直方向上的自由落體運動,因此位置、速度、加速度只要各用一個變量表示。其中,位置就是ballMesh.position.y,不需要額外的變數,因此我們在全域性宣告速度v和加速度a

var v =0;var a =-0.1;

這裡,a = -0.1代表每幀小球向y方向負方向移動0.1個單位。

一開始,彈球從高度為maxHeight處自由下落,掉落到平面上時會反彈,並且速度有損耗。當速度很小的時候,彈球會在平面上作振幅微小的抖動,所以,當速度足夠小時,我們需要讓彈球停止跳動。因此,定義一個全域性變量表示是否在運動,初始值為false

var isMoving =false;

在HTML中定義一個按鈕,點選按鈕時,彈球從最高處下落:

function drop(){
    isMoving =true;
    ballMesh.position.y = maxHeight;
    v =0;}

下面就是最關鍵的函數了,在draw函式中,需要判斷當前的isMoving值,並且更新小球的速度和位置:

            
           

相關推薦

Three.js2

動畫原理 在這裡,我們將動態畫面簡稱為動畫(animation)。正如動畫片的原理一樣,動畫的本質是利用了人眼的視覺暫留特性,快速地變換畫面,從而產生物體在運動的假象。而對於Three.js程式而言,動畫的實現也是通過在每秒中多次重繪畫面實現的。 為了衡量畫面切換速度

Three.js7

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> body {margin: 0;} body, html {overflow: hidden

《手把手教你》系列2-python+ selenium自動化測試 - python基礎掃盲(詳細教程)

1. 簡介   這篇文章主要是分享講解一下,如何封裝自己用到的方法和類。以便方便自己和別人的呼叫,這樣就可以避免重複地再造輪子。   封裝(Encapsulation)是面向物件的三大特徵之一(另外兩個是繼承和多型),它指的是將物件的狀態資訊隱藏在物件內部,不允許外部程式直接訪問物件內部資訊,而是通過該類所提

React學習筆記react2

-s state ops category strong tro 服務 ive 周期 2.組件與服務器通信   組件的生命周期分為三個階段:掛載階段->更新階段->卸載階段,本文主要集中講述掛載和更新階段組件如何和服務器進行通信。 1.組件掛載階段通信  

(十二)vue.js元件——元件通訊(3)

(1)概述 所謂元件間的通訊,實際上就是指在各個元件間,進行引數或者資訊的相互傳遞。比如我們前面學的通過props給子元件傳參,實際上這就是父元件向子元件進行單向的通訊。 (2)元件間通訊的幾種方式 1.父到子的通訊 父到子的通訊使用我們前面使用的props即可

2. web前端開發分享-css,js

一,css進階篇:   等css哪些事兒看了兩三遍之後,需要對看過的知識綜合應用,這時候需要大量的實踐經驗, 簡單的想法:把qq首頁全屏另存為jpg然後通過ps工具切圖結合css轉換成html,有無從下手的地方可以用firebug, chrome除錯工具分析網站結構樣式。如果技術熟練自信可以自己先寫,寫完

JS--JS陣列reduce()方法詳解及高階技巧

去除巢狀的思路: 用遞迴、reduce()、concat()來實現。 遞迴解決多層巢狀,reduce()解決每層陣列的迭代拼接,concat()來拼接陣列即拆除一層巢狀。 let sum = [0, 1, 2, 3].reduce(function(acc, val)

Java十五 ----- JDK1.8的Lambda、Stream和日期的使用詳解(很詳細)

前言 本篇主要講述是Java中JDK1.8的一些新語法特性使用,主要是Lambda、Stream和LocalDate日期的一些使用講解。 Lambda Lambda介紹 Lambda 表示式(lambda expression)是一個匿名函式,Lambda表示式基於數學中的λ演算得名,直接對應於

Android引導頁系列ViewPager實現Animation動畫引導頁

小夥伴們,時隔很久,我又開始來BB了,勿噴,,今天繼續上次所說的引導頁之旅。 其實想實現一個靜態的引導頁還是很容易的,就是一個ViewPager,但是想對每一頁實現動畫效果,比如第一頁有一幾朵雲在飄啊飄!,想實現這種效果對只要瞭解過Animation動畫的人來說也不難實現。

Jenkins外掛開發擴充套件外掛功能

之前寫過一篇文章是關於如何開發jenkins外掛,主要講述了開發jenkins外掛時需要準備的環境,如何新建一個jenkins外掛工程,以及對工程專案目錄結構的解析。本文是jenkins外掛開發的進階篇,主要講述如何擴充套件jenkins外掛的功能。如下圖所示:(1)Job任

如何分析虛擬機器(2): VMProtect 2.13.8

原文網址:https://www.52pojie.cn/forum.php?mod=viewthread&tid=723307&page=1   序言 系列第1篇對一個極弱的虛擬機器 VMProtect 1.81 Demo 版進行

React2) -- Redux

前言 如果還不知道為什麼要使用Redux,說明你暫時還不需要它。 三大原則 單一資料來源 整個應用的 state 被儲存在一棵 object tree 中,並且這個 object tree 只存在於唯一一個 store 中。 State 是隻讀的 唯一改變 state 的方法就是觸發 action

FragmentFragment生命週期和回退棧

前言 上一篇blog(處女男學Android(八)---Fragment初體驗之實現Tab導航)記錄了fragment的基本概念和基本的使用方法,本篇將逐步深入記錄關於fragment的幾個重要知識點,包括:fragment的生命週期、fragm

Android引導頁系列Splash閃屏Log

最近比較閒,所以會陸續出很多基礎型Demo和高階點的Demo,,小夥伴們肯定又要罵了,孫子又來裝B了 在android應用中,其實閃屏是一個很重要的前期 先上效果圖:就是一個頁面,沒有Logo和公司名幾乎就是一張背景,又要捱罵了 很重要?,這不廢話嗎,不重要為什麼每個應用

反射應用自定義反射工具類在springmvc中的應用

本篇使用自定義工具類進行批量處理物件 ---將批量源物件的屬性值注入到實際需要的目標類物件(屬性名相同,型別不同)中 專案使用maven構建war工程:  spring+spring MVC+Mybatis 回顧知識點: 事務:--->為什麼在使用AOP時需要使

【 D3.js 系列 — 6.2 】 餅狀圖的拖拽

本文講解稍微複雜一些的拖拽應用,即拖拽餅圖的各部分。在【入門 - 第 9.1 章】講解了如何製作餅狀圖。餅狀圖的各部分是用具有寬度的弧線來表示的。在與使用者進行互動的時候,如果每一部分都能拖拽,是很有趣的。1.餅狀圖的繪製與【入門 - 第 9.1 章】稍有有些不同,我們對餅狀

AndroidListView、GridView以及ScrollView實現按鈕控制滾動

這次還是一樣,因為專案需要,需要對滾動檢視需要手動控制,也就是點選上下左右按鈕,能讓滾動控制元件觸發對應的操作 在android中,滾動View最基本的有三種,分別是:ListView、GridView和ScrollView 先看效果圖:1、ListView的效果圖,右下角

Data binding 入坑筆記二雙向繫結

上一篇介紹了Data binding的基礎用法,你可能會想這也太基礎了,只支援前置資料的繫結,一旦資料變化了UI都監聽不到。不要著急,這一篇就來講到databinding的雙向繫結用法。 0.舉個例子 我們用一個輸入控制元件EditText和一個

第三部分 -第2章 CC2530 BasicRF(無線點燈)

1 理論分析 1.1 Light_Switch工作流程 無線點燈的工作過程:啟動、發射、接收。【本實驗的重點就在Application層】  啟動 (1)板載外設、射頻IO、系統時鐘、中斷等初始化;確保外圍器件沒有問題;(halBoardInit(

[JS] HTML5 文件操作(file)

版本 無效 tran ssl idt 無法讀取 bin erl this 版權聲明:本文為博主原創文章。未經博主同意不得