// 經過 moveCamera 改變 eye 和 center 來移動場景視角爲大樓的正視面 moveCamera(g3d, [134, 399, 1617], [7, 40, 144], { duration: 2000, easing: t => t * t, finishFunc: () => { // 開啓場景大樓模型的可透明爲 true this.building.eachChild(c => { c.s({ 'shape3d.transparent': true, }); }); // 大樓模型線框的顏色變化 tweenColor(this.building, 'wf.color', 'rgba(72,149,232,1)', 'rgba(56,156,255,0.03)', { duration: 2000, easing: t => t }); // 大樓模型總體染色的顏色變化 tweenColor(this.building, 'shape3d.blend', 'rgba(120,180,255,1)', 'rgba(120,180,255,0)', { duration: 2000, easing: t => t, finishFunc: () => { // 樓層設置爲可見 this.floor.eachChild(c => { setNodeVisible(c, true); }); this.floorLighting = 1; // 顯示大樓建築信息的動畫 this.showBuilding(); } }); } });
tweenColor(node, property, startColor, endColor, animParams) {
animParams = animParams || {}; if (!animParams.frames && !animParams.duration) animParams.duration = 5000; if (!animParams.easing) animParams.easing = t => t; startColor = ht.Default.toColorData(startColor); endColor = ht.Default.toColorData(endColor); const dx = endColor[0] - startColor[0]; const dy = endColor[1] - startColor[1]; const dz = endColor[2] - startColor[2]; const da = endColor[3] - startColor[3]; const postAction = animParams.postAction; animParams.action = (v, t) => { const x = startColor[0] + v * dx; const y = startColor[1] + v * dy; const z = startColor[2] + v * dz; const a = (startColor[3] + v * da) / 255; node.s(property, ('rgba(' + ([x, y, z, a]).join(', ')) + ')'); if (postAction) postAction(('rgba(' + ([x, y, z, a]).join(', ')) + ')'); } return ht.Default.startAnim(animParams); }
// 面板顯示 showPanel(data) { ht.Default.startAnim({ duration: 1000, easing: t => t, action: (v,t) => { data.setScaleX(data.getScaleX() + (1 - data.getScaleX()) * v); } }); } // 面板隱藏 hidePanel(data) { ht.Default.startAnim({ duration: 1000, easing: t => t, action: (v,t) => { data.setScaleX(data.getScaleX() + (0 - data.getScaleX()) * v); } }); }
elevatorAnimation(data) {
const g3d = this.g3d; const tag = data.getTag(); const e = data.getElevation(); const label = data.getChildAt(0); // 判斷如今所處樓層 let now = Math.ceil(e / 50); // 下一層樓層取1~7隨機數 let next = randomNumber(1, 7); // 根據如今的樓層和下一個樓層,判斷電梯運行的範圍 let range = numBetween(now, next); this.animationElevatorMap[tag] = ht.Default.startAnim({ duration: range * 800, easing: t => t, action: (v, t) => { // 電梯運行位置設定 data.setElevation(now < next ? (e + (range * 50) * v) : (e - (range * 50) * v)); // 設置電梯樓層面板顯示並根據電梯位置設定 if (!label) return; const floor = Math.ceil(data.getElevation() / 50); if (floor === label.a('text')) return; label.a('text', floor); // 手動刷新電梯面板信息 g3d.invalidateShape3dCachedImage(label); }, finishFunc: () => { // 銷燬電梯間隔動畫 delete this.timeoutElevatorMap[tag]; // 執行電梯間隔動畫後回調電梯運行動畫 this.timeoutElevatorMap[tag] = setTimeout(() => { this.elevatorAnimation1(data); }, Math.floor(Math.random() * 5000) + 2000); } }); }
park(car, key = 'Path', finishFunc) {
const dm = car.dm(); const tag = car.getTag(); const forwardPath = dm.getDataByTag(tag + '_forward' + key); const backwardPath = dm.getDataByTag(tag + '_backward' + key); this.animationMap[tag] = move(car, forwardPath, 'forward', undefined, 24, { pathEndFunc: () => { this.animationMap[tag].stop(); this.animationMap[tag] = move(car, backwardPath, 'backward', undefined, undefined, { pathEndFunc: () => { this.animationMap[tag].stop(); delete this.animationMap[tag]; if (finishFunc) finishFunc(); return true; } }); return true; } }); }
move 是節點沿着路徑平滑移動的封裝函數,主要參數爲:html
經過繪製一條運行路線的管道,ht.Default.getLineCacheInfo() 獲得這條管道的點位和分割信息 cache,而後管道信息經過 ht.Default.getLineLength() 獲得管道的長度,而且經過 ht.Default.getLineOffset() 來獲取連線或者管道指定比例的偏移信息,從而達到移動的效果,注意的是,這裏還設定了 direction 來規定動畫節點的朝向,主要是爲了經過 node.lookAtX() 來獲取節點下一個面對的朝向的位置信息,並設置節點此時的位置,從而達到節點沿着路徑平滑移動的效果。node
move(node, path, direction, step = 6, interval = 75, animParams) {
let cache = path.__cache__; if (!cache) cache = path.__cache__ = ht.Default.getLineCacheInfo(path.getPoints(), path.getSegments()); const len = ht.Default.getLineLength(cache); animParams = animParams || {}; const face = direction === 'forward' ? 'front' : direction === 'backward' ? 'back' : direction; let currentLen = 0; const pathEndFunc = animParams.pathEndFunc; const action = animParams.action; animParams.action = (v, t) => { if (currentLen >= len) { // 檔 pathEndFunc 返回 true 是,認爲是要結束動畫, 不執行後面檔 action if (pathEndFunc && pathEndFunc()) return; } currentLen = currentLen % len; const offset = ht.Default.getLineOffset(cache, currentLen); const point = offset.point; node.lookAtX([point.x, node.getElevation(), point.z], face); node.p3(point.x, node.getElevation(), point.z); currentLen = currentLen + step; if (action) action(); }; return loop(animParams.action, interval); }
與此同時,咱們還能夠看到車輛行駛到車位或者離開時,車位上方的紅綠燈則表示着這個車位的停放信息,是根據車輛的狀況實時設定車位的情況,經過改變其信號燈 image 的 json 圖標並手動刷新緩存來實現的。而緩存機制對於總體場景的流暢度是相當重要的,對於一些沒必要要實時刷新的面板信息,咱們能夠採起緩存的方式,而且在下一次更新的時候調用 Graph3dView.invalidateShape3dCachedImage(node)來手動刷新這個節點,從而大大提升了場景的性能,有關 3D 面板的屬性能夠參考 <HT 的 3D 手冊 billboard 公告板>。json
updateLight(view, light, color) {
light.s('shape3d.image', 'symbols/parking/' + color + 'Light.json'); view.invalidateShape3dCachedImage(light); }