HT for Web (Hightopo) 使用心得(5)- 動畫的實現
其實,在 HT for Web 中,有多種手段可以用來實現動畫。我們這裡仍然用直升機為例,只是更換了場景。增加了巡遊過程。
使用 HT 開發的一個簡單網頁直升機巡邏動畫(Hightopo 使用心得(5))
這裡主要用到的動畫實現方式有三種:
- setInterval
- ht.Default.startAnim()
- DataModel.addScheduleTask(task)
場景搭建
具體3D場景的相關概念請參考《Hightopo 使用心得(4)- 3D 場景 Graph3dView 與 Obj 模型》。
這裡的主要工作分為:3D 場景配置以及模型載入。其中 3D 場景部分的設定程式碼如下:
this.g3d = new ht.graph3d.Graph3dView();
this.g3d.setGridVisible(true);
this.g3d.setGridSize(5000);
this.g3d.setGridGap(2000);
this.g3d.setNear(10)
this.g3d.setFar(10000000)
this.g3d.addToDOM();
this.dataModel = this.dm = this.g3d.dm();
為了給直升機搭建一個逼真的環境。這裡我們增加了一個山體模型。另外,由於直升機機體與螺旋槳模型是分開的,因此需要分別載入並調整其位置讓二者合併成一個模型。
// 載入山體模型
this.mountains = await this.createObj(MODELS.MOUNTAINS.name, MODELS.MOUNTAINS.obj, MODELS.MOUNTAINS.mtl);
this.mountains.s('3d.selectable',false);
this.mountains.s('shape3d.scaleable',true);
this.mountains.setScale3d([0.01, 0.1, 0.01]);
this.mountains.setElevation(1800); // 讓山體在地面以上
// 分別載入直升機及螺旋槳模型
this.helicopterNode = await this.createObj(MODELS.HELICOPTER.name, MODELS.HELICOPTER.obj, MODELS.HELICOPTER.mtl);
this.propellerNode = await this.createObj(MODELS.PROPELLER.name, MODELS.PROPELLER.obj, MODELS.PROPELLER.mtl);
// 由於預設建立 Node 的時候,其錨點是在 [0.5, 0.5, 0.5],位置是在 [0, 0, 0]。導致模型並不在水平面以上。
let size3d = this.helicopterNode.getSize3d(); // 獲取直升機模型的 [長,寬,高]
let height = size3d[1]; // 獲取模型高度
this.helicopterNode.setPosition3d([0, height/2, 0]); // 將直升機放到地面上
this.propellerNode.setRotation3d([0.10506443461595279, 4.550746858974086, -0.007825951889059535]); // 讓螺旋槳水平
this.propellerNode.setPosition3d([0, 215, -99.00152946490829]); // 將螺旋槳放到直升機上
this.propellerNode.setHost(this.helicopterNode); // 螺旋槳吸附到直升機上
this.helicopterNode.p3(0,2000,0); // 直升機
螺旋槳動畫 - setInterval
螺旋槳動畫比較簡單,其本質是透過不斷地修改螺旋槳節點在豎直方向(Y 軸)的角度。
/**
* 螺旋槳旋轉動畫
*
*/
startPropellerAnim(node) {
setInterval(() => {
const r3 = node.getRotation3d();
node.setRotation3d([r3[0], r3[1] + 0.4, r3[2]]); // 繞 Y 軸旋轉
}, 20);
}
建立直升機巡遊路徑
有了直升機及環境,我們需要讓直升機動起來。例如在這裡,我們計劃讓直升機圍繞山體巡邏。這裡該如何實現呢?
在 HT for Web 官方手冊中,其提供了一種實現方式,我們這裡稍微加以改造便可讓直升機圍繞山體巡邏。
在程式碼層面,我們建立了一條三維線段(Polyline)。該線段實現的是一個圓環,懸浮在山體上面。有了這條路徑,直升機便可沿著該路徑前進實現巡遊動畫。
polyline的形狀主要由points和segments這兩個屬性描述。二者都是陣列。其中 points 可以理解成組成 polyline 所要用到的點集合,而 segments 陣列主要用來定義如何使用前面的點來組成 polyline。
points 中的每一項為 {x,y,e} 格式,需要注意的是,這裡代表高度的是 e(elevation),而不是 y。
segments 陣列裡面有5種值。分別為:
- 1: moveTo,佔用1個點資訊,代表一個新路徑的起點
- 2: lineTo,佔用1個點資訊,代表從上次最後點連線到該點
- 3: quadraticCurveTo,佔用2個點資訊,第一個點作為曲線控制點,第二個點作為曲線結束點
- 4: bezierCurveTo,佔用3個點資訊,第一和第二個點作為曲線控制點,第三個點作為曲線結束點
- 5: closePath,不佔用點資訊,代表本次路徑繪製結束,並閉合到路徑的起始點
/**
* 建立直升機巡遊路徑
*
* @memberof Index3d
*/
createPath() {
this.g3d.setDashDisabled(false); // 顯示虛線
let height = 2000; // 線段離地高度
let dataModel = this.dataModel;
let polyline = this.polyline = new ht.Polyline();
polyline.setThickness(5); // 線段粗細
polyline.s({
'shape3d.image': 'assets/flow.png', // 貼圖
"shape3d": "cylinder", // polyline型別,這裡是圓柱。也可以是
'repeat.uv.length': 400, // 貼圖寬度
'shape3d.resolution': 1600, // 管線解析度,解析度越高越平滑
});
dataModel.add(polyline);
// 起始點
const points = [{
x: -15000,
y: 0,
e: height,
}];
const segments = [1];
// 二次曲線,佔用兩個點。生成一條弧線。下同。
points.push({
x: -15000,
y: -15000,
e: height
});
points.push({
x: 0,
y: -15000,
e: height
});
segments.push(3);
points.push({
x: 15000,
y: -15000,
e: height
});
points.push({
x: 15000,
y: 0,
e: height
});
segments.push(3);
points.push({
x: 15000,
y: 15000,
e: height
});
points.push({
x: 0,
y: 15000,
e: height
});
segments.push(3);
points.push({
x: -15000,
y: 15000,
e: height
});
points.push({
x: -15000,
y: 0,
e: height,
});
segments.push(3);
polyline.setPoints(points);
polyline.setSegments(segments);
polyline.setAnchorElevation(0)
}
直升機巡遊動畫 - ht.Default.startAnim
接下來,我們需要讓直升機沿著巡遊路徑前進。在實現的時候,我們使用了 ht.Default.startAnim() 方法。該方法我們在前幾篇文章中都用過,這裡就不再詳細介紹。
ht.Default.startAnim() 會執行 duration 毫秒,在執行過程中,其會自動計算所需要的幀數並在每一幀都呼叫一次action 方法。也就是說,如果我們想讓直升機 40 秒圍繞路徑飛行一圈,我們只需要將 duration 設定成40*1000 毫秒,並且在每一幀拿到當前時刻 polyline 上的點的座標及方向。同時,使用該座標與方向設定直升機位置及朝向就可以實現巡遊動畫。
這裡面比較關鍵的一個方法是 g3d.getLineOffset(polyline, length * v) 。該方法會返回一個物件:{point: p.M…h.Vector3, tangent: p.M…h.Vector3}。其分別代表當前時刻 polyline 上的點的座標及放向。根據這兩個值,我們可以進一步配置直升機的位置和朝向。
/**
* 直升機沿著巡遊路徑飛行
*
* @param {number} [duration=40 * 1000]
* @memberof Index3d
*/
startFly(duration = 40 * 1000) {
const {
g3d,
polyline
} = this;
/** 獲取巡遊路徑總長度 */
let length = g3d.getLineLength(polyline);
const params = {
delay: 0,
duration,
easing: (t) => {
return t;
},
action: (v, t) => {
let offset = g3d.getLineOffset(polyline, length * v),
point = offset.point,
px = point.x,
py = point.y + 200, // 讓直升機高於polyline
pz = point.z,
tangent = offset.tangent,
tx = tangent.x,
ty = tangent.y,
tz = tangent.z;
this.helicopterNode.p3(px, py, pz);
this.helicopterNode.lookAt([px + tx, py + ty, pz + tz], 'back'); // 一個模型有6個面,這裡需要確定機頭處於哪個面
// 視角盯住直升機
if (this._cameraType == 1) {
g3d.setCenter(px, py, pz);
} else if (this._cameraType == 2) { // Camera跟隨直升機運動
g3d.setEye(px - tx * 1800 + 1000, py - ty * 1800 + 1000, pz - tz * 1800); // 讓鏡頭高於直升機並在尾部進行觀察
g3d.setCenter(px, py, pz);
}
this.helicopterNode.a('angle', v * Math.PI * 120);
},
finishFunc: () => {
ht.Default.startAnim(params);
}
};
ht.Default.startAnim(params);
}
管道流動動畫 - DataModel.addScheduleTask()
實現管道流動的動畫有多種方式,其本質是定期改變管道的貼圖偏移。
這裡我們採用DataModel#addScheduleTask(task)實現流動動畫。DataModel#addScheduleTask(task)實際上是新增了一個排程任務。由於該方法是在 DataModel 上執行,因此在每次執行的時候,DataModel 裡面的每個 Data 都會被呼叫。我們可以在 action 引數裡面對 Data 進行過濾。DataModel#addScheduleTask(task)方法的引數task為json物件,可指定如下屬性:
- interval:間隔毫秒數,預設值為10
- enabled:是否啟用開關,預設為true
- beforeAction:排程開始之前的動作函式
- action:間隔動作函式,對DataModel上的每個data節點都會執行一次action操作
- afterAction:排程結束之後的排程函式
另外,可以用DataModel#removeScheduleTask(task)刪除排程任務,其中task為以前新增過的排程任務物件。
/**
* 透過DataModel的addScheduleTask實現流動效果
*
* @memberof Index3d
*/
addScheduleTasks() {
const task = {
interval: 50, // 間隔毫秒數,預設值為10
enabled: true, // 是否啟用開關,預設為true
beforeAction: () => {}, // 排程開始之前的動作函式
afterAction: () => {}, // 排程結束之後的排程函式
action: (data) => { // 間隔動作函式,對DataModel上的每個data節點都會執行一次action操作
if (data.getClassName() == 'ht.Polyline') {
const offset = (data.s('shape3d.uv.offset') || [0,0]);
data.s('shape3d.uv.offset', [offset[0] + 0.1, offset[1]]);
}
}
};
this.dataModel.addScheduleTask(task);
// this.dataModel.removeScheduleTask(task); // 刪除排程任務
}
這裡我們只是舉例介紹一下DataModel#addScheduleTask(task)的用法。對於一個 DataModel 中大部分 Data 都需要動畫的時候,可以考慮使用該方法。
在程式碼執行的時候,我們可以選擇把巡遊路徑隱藏。這樣看起來直升機就是沿著一個圓形持續巡遊。
hidePath() {
this.polyline.s('3d.visible', false);
}
總結
本文介紹瞭如何透過程式碼實現一個直升機繞山巡遊的動畫,包括建立路徑和實現直升機的飛行動畫。另外,還介紹瞭如何透過DataModel#addScheduleTask(task)實現流動效果的動畫。讀完本文,你將瞭解到如何使用 HT for Web 實現各種動畫效果。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69997639/viewspace-2997906/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- HT for Web (Hightopo) 使用心得(4)- 3D 場景 Graph3dView 與 Obj 模型Web3DViewOBJ模型
- 學習 HT for Web 中的互動事件Web事件
- Web 頁面如何實現動畫效果Web動畫
- canvas動畫心得Canvas動畫
- 使用CADisplayLink實現UILabel動畫特效UI動畫特效
- [譯] 使用 Swift 實現原型動畫Swift原型動畫
- 使用canvas實現簡單動畫Canvas動畫
- H5移動端彈幕動畫實現H5動畫
- 由實現web動畫到產生轉行的念頭Web動畫
- js利用H5的requestAnimationFrame()API實現動畫效果JSH5requestAnimationFrameAPI動畫
- 基於 HTML5 Canvas 實現的文字動畫特效HTMLCanvas動畫特效
- 5分鐘用動效工廠實現粒子動畫動畫
- Web前端培訓教程:CSS3動畫怎麼實現的Web前端CSSS3動畫
- Rust使用Sauron實現Web介面互動RustWeb
- Canvas 實現畫中畫動畫效果–網易娛樂年度盤點H5動畫解密Canvas動畫H5解密
- Canvas 實現畫中畫動畫效果--網易娛樂年度盤點H5動畫解密Canvas動畫H5解密
- 如何在 pyqt 中使用動畫實現平滑滾動的 QScrollAreaQT動畫
- jQuery中動畫的實現jQuery動畫
- 基於 HTML5 Canvas 實現文字動畫特效HTMLCanvas動畫特效
- Android 動畫實現Android動畫
- Flutter實現動畫Flutter動畫
- H5移動端獲獎無縫滾動動畫實現H5動畫
- WPF使用Shape實現複雜線條動畫動畫
- css3實現顫動的動畫CSSS3動畫
- Android 動畫框架實現Android動畫框架
- iOS實現字串動畫iOS字串動畫
- 使用css3實現小車行駛的動畫效果CSSS3動畫
- 基於 HTML5 實現的簡單雲動畫和景物描述HTML動畫
- iOS動畫系列之七:實現類似Twitter的啟動動畫iOS動畫
- 基於HTML5+Webkit實現樹葉飄落動畫HTMLWebKit動畫
- 萬彩動畫大師教程 | 如何實現物件的閃動的動畫效果動畫物件
- canvas之實現控制動畫Canvas動畫
- Flutter 實現背景 Parallax 動畫Flutter動畫
- web動態驗證碼的實現Web
- SAP UI5 Web Component的圖示實現UIWeb
- Flutter動畫:用Flutter來實現一個拍手動畫Flutter動畫
- 短視訊系統,Android 使用MotionLayout實現動畫效果Android動畫
- Flutter實戰之動畫實現篇Flutter動畫