基於 HTML5 Canvas 的病毒模擬視覺試驗檯
前言
2020 年 1 月 12 日,世界衛生組織以武漢病毒肺炎病例命名了一種病毒,2019新型冠狀病毒 ( 2019-nCoV ) 。隨著春運的到來,該病毒迅速的蔓延開來,大家都唯恐避之不及。病毒的爆發並不單單隻有中國這人口大國,縱觀整個地球,2020 年,還有很多國家也在“水深火熱”中努力進行著“自救”。美國,近幾個月來爆發了非常致命的流感--乙型流感病毒,據相關資料統計,到目前為止全美範圍已經有超 1300 萬人感染了該病毒, 12 萬人住院治療,死亡人數可能高達 6600 多人;波蘭,2019 年 12 月 31 日至 2020 年 1 月 4 日,該國盧布林省和大波蘭省發生 8 起 H5N8 亞型高致病性禽流感,此次疫情可能導致多達 4 萬隻禽類被宰殺,方圓3公里多達 35 萬隻家禽受到威脅;中國,農業農村新聞辦公室 2 月 1 日釋出,湖南省邵陽市雙清區發生一起家禽 H5N1 亞型高致病性禽流感疫情,養殖戶存欄肉雞 7850 只,發病死亡 4500 只,疫情發生後,當地按照有關預案和防治技術規範要求切實做好疫情處置工作,已撲殺家禽 17828 只,全部病死和撲殺家禽均已無害化處理......在病毒面前,人類真的很脆弱,此次新型冠狀病毒雖然致死率低,但傳染性極強,如果傳播過程中發生了可怕的變異,導致致死率提升,那麼造成的後果難以估量。如果大家早期能夠認識到病毒的危害,提高自我防範意識,那麼就能有效的控制疫情的蔓延。此次春節在家,發現大多數的長輩都對此次疫情不以為然,依舊到處串門喝茶聊天,進出人群密集的地方時,不戴口罩,多番勸阻無果,很是無奈,依舊有部分人不瞭解病毒的危害。此篇文章就以 HIV 病毒為例,採用 2/3D 結合的視覺化效果,直觀展示病毒的微觀世界。補充說明:人類免疫缺陷病毒 ( Human Immunodeficiency Virus;abbe:HIV ),即艾滋病 ( AIDS,獲得性免疫缺陷綜合徵) 病毒,屬逆轉錄病毒的一種。
demo: http://www.hightopo.com/demo/hiv-test-bed/
效果圖:
1.HIV 活性實驗臺
該模組主要模擬在不同環境不同試劑的情況下,病毒活性的變化。
功能點1:左側為實驗控制檯,可拖拽其值進度條來改變數值,通過一定的計算公式進而去動態修改 3D 場景中病毒的活性面板的相關顯示值。
功能實現:監聽 2D 圖紙的互動器,在圖元拖拽中進行去進行相關引數值的修改( addInteractorListener,簡寫為 mi ),接著通過模型中 Data 元素屬性變化監聽器( addDataPropertyChangeListener,簡寫為 md )去監聽每一次引數的變化,然後通過一定的計算公式去計算活性面板的最終顯示值,最後以資料驅動方式去修改活性面板的資料。
主要程式碼:
this.operator = { temperature: 60, humidity: 60, time: 25, sorbitol: 35, FBS: 50 } // 溫度、溼度、時間、山梨醇、胎牛血清 this.g2d.dm().md(handleInterActivePanel, this); function handleInterActivePanel(e) { // 溫度、溼度、時間、山梨醇、胎牛血清 if (this.operator.hasOwnProperty(e.data.getTag()) && e.property === 'a:value') { this.operator[e.data.getTag()] = e.newValue; const operator = this.operator; let node = this.g3d.dm().getDataByTag('activePanel'); let dataSource = node.a('dataSource'); let calc = [ { value: parseFloat((100 * (1 - operator.temperature / 100) * (1 - operator.time / 60) * (1 - operator.sorbitol / 100) * (1 - operator.FBS / 100) * (operator.humidity / 100) ).toFixed(2)) }, { value: parseFloat((90 * (1 - operator.temperature / 100) * (1 - operator.time / 60) * (1 - operator.sorbitol / 100) * (1 - operator.FBS / 100) * (operator.humidity / 100) ).toFixed(2)) }, { value: parseFloat((60 * (1 - operator.temperature / 100) * (1 - operator.time / 60) * (1 - operator.sorbitol / 100) * (1 - operator.FBS / 100) * (operator.humidity / 100) ).toFixed(2)) }, { value: parseFloat((10 * (operator.temperature / 100) * (operator.time / 60) * (operator.sorbitol / 100) * (operator.FBS / 100) * (1 - operator.humidity / 100) + 0.01 ).toFixed(2)) }, ] dataSource = dataSource.map((i, index) => { return { rising: i.value > calc[index].value, value: calc[index].value, } }) node.a('dataSource', []); node.a('dataSource', dataSource); } }
功能點2:放大鏡檢視。
功能實現:根據放大鏡左側倍數控制器的值去動態設定放大鏡中圖元的縮放值。
主要程式碼:
// 獲取病毒圖元 this._virusNode = virus2D.dm().getDataByTag('virusNode'); // 對圖元進行縮放 function setScale(value) { this._virusNode.setScale(value, value); } this.g2d.dm().mp(function (e) { if (e.property === "a:virusScale") { if (this.virus2D) { setScale(e.newValue);
this._virusNode.setScale(value, value);
} } }, this);
功能點3:病毒成分剖面示意圖及其 RNA 鏈。 2D 及 3D 的效果展示,使微觀病毒更加具象化,幫助人們更加清晰的認識病毒。
功能點4: 3D 場景中病毒的呼吸效果。
功能實現:使用 HT 的動畫功能,不斷的去設定病毒圖元自身的縮放值。
主要程式碼:
let params = { duration: 10000, // 動畫週期毫秒數,預設採用`ht.Default.animDuration` easing: (t) => { return t }, // 動畫緩動函式,預設採用`ht.Default.animEasing` action: (v, t) => { this.virus.forEach((i, index) => { let count = 0; if (i.a('count')) { count = i.a('count'); } else { count = 0; } count += 0.5; let scale = this.virusScales[index] + 0.1 * Math.cos( (((count % 36) * Math.PI) / 180) * 10 + ((360 / this.virus.length) * index * Math.PI) / 180 ); i.eachChild(data => { data.setScale3d(scale, scale, scale); }); i.a('count', count) }) }, finishFunc: () => { if (this._continuous) { this.breating(); } }, // 動畫結束後呼叫的函式。 } this.anim = ht.Default.startAnim(params);
2.免疫系統侵入演示
動畫步驟描述:
① 病毒侵入,免疫啟用
開始:場景中只有三個白細胞(兩個小的白細胞離大號的較遠);
過程:病毒漸顯(粉色顆粒,上面無附著抗體標記);
結束:場景中只存在三個白細胞和病毒
② 白細胞釋放趨化因子和增值化學物質
開始:場景中有三個白細胞和六個病毒;
過程:趨化因子從大號白細胞內部釋放出來,漸顯,而後另外兩個白細胞向大號靠近到一定距離,然後趨化因子漸隱消失
結束:場景中存在三個白細胞(兩個小的白細胞靠近大號的)、六個病毒
③ 白細胞產生抗體標記靶向細菌/病毒
開始:場景中有三個白細胞(兩個小的白細胞靠近大號的)和六個病毒;
過程:抗體標記從大號白細胞裡漸顯飛出,最後附著在病毒上,此時出現提示面板(內容:病毒/細菌被白細胞標記抗體),最後面板漸隱
結束:場景中存在三個白細胞(兩個小的白細胞靠近大號的)、六個病毒和抗體標記(附著在病毒上)
④ 白細胞吞噬靶向細胞/病毒
開始:場景中存在三個白細胞(兩個小的白細胞靠近大號的)、六個病毒和抗體標記(附著在病毒上)
過程:大號白細胞出現提示面板(內容:白細胞被吸引到標記抗體處,面板漸顯漸隱後,大號白細胞移動至四個病毒區域,二號白細胞移動至兩個病毒區域,三號朝病毒區域中心移動輕微移動一小段,稍微停頓後,白細胞恢復初始位置,病毒、抗體標記消失
結束:場景中只存在三個白細胞
⑤ HIV 病毒侵入進入白細胞
開始:場景中存在三個白細胞
過程:出現幾個病毒,然後HIV病毒漸顯,再然後進入到一號白細胞內部
結束:場景中存在三個白細胞,一個 HIV 病毒,兩三個粉色病毒
⑥ HIV 病毒在白細胞內複製,白細胞死亡
開始:場景中存在三個白細胞,一個 HIV 病毒(在一號白細胞內部),兩三個粉色病毒
過程:HIV 病毒開始複製(體現在數量增多),然後三個白細胞同時縮小到原來的一半
結束:場景中存在三個白細胞(體積縮小),三個 HIV 病毒
⑦ HIV 病毒跑出,細菌增生免疫系統破壞成功
開始:場景中存在三個白細胞(體積縮小),三個 HIV 病毒
過程:HIV病毒跑出一號白細胞,同時白細胞漸隱直至消失,而後粉色病毒增多
結束:場景中只存在三個 HIV 病毒,多個粉色病毒
動畫實現:ht.Default.startAnim 的動畫函式,示例程式碼如下:
ht.Default.startAnim({ frames: 12, // 動畫幀數 interval: 10, // 動畫幀間隔毫秒數 easing: function(t){ return t * t; }, // 動畫緩動函式,預設採用`ht.Default.animEasing` finishFunc: function(){ console.log('Done!') }, // 動畫結束後呼叫的函式。 action: function(v, t){ // action函式必須提供,實現動畫過程中的屬性變化。 ... } });
除了以上動畫外,該模組還新增了兩個控制項:控制動畫播放速度(修改動畫的 duration 引數值)及 3D 場景的視角(修改 eye)。
實現程式碼:
function getPositionByZoom(arr, zoom) { return [arr[0] * zoom, arr[1] * zoom, arr[2] * zoom]; } this.g2d.dm().mp(function (e) { if (e.property === 'a:stepIndex') { // 做演示動畫 this.stepIndex = e.newValue; this['anim' + (e.newValue + 1)](); } else if (e.property === 'a:screenZoom') { let value = e.newValue; if (this._eye) { let newEye = getPositionByZoom(this._eye, value); this.g3d.setEye(newEye); } } else if (e.property === 'a:playSpeed') { this.speed = 1 / e.newValue; } })
3.藥物抑制原理演示
該模組主要演示生物酶抑制劑和化學消毒/去汙藥劑對病毒活性的影響。通過改變試劑的用量來模擬計算出病毒的活性率、複製速度、感染性、突變機率。
動畫演示中涉及的 3D 圖元的位移方法。通過繪製一條管道軌跡線來實現。
// 偏移動畫 let g3d = this.g3d; let params = { frames: 100, // 動畫幀數 interval: 30, // 動畫幀間隔毫秒數 easing: (t) => { return t }, // 動畫緩動函式,預設採用`ht.Default.animEasing` action: (v, t) => { let pipe; this.moveNodes.forEach((i, index) => { if (index < 2) { pipe = this.pipe1; } else { pipe = this.pipe2; } let length = g3d.getLineLength(pipe), offset = g3d.getLineOffset( pipe, length * t ), point = offset.point, px = point.x, py = point.y, pz = point.z, tangent = offset.tangent, tx = tangent.x, ty = tangent.y, tz = tangent.z; i.p3(px, py, pz); i.lookAt([px + tx, py + ty, pz + tz], 'right'); i.a('angle', t * Math.PI * 120); }) }, finishFunc: () => { if (this._continuous) { this.move(); } }, // 動畫結束後呼叫的函式。 } this.anim = ht.Default.startAnim(params);
現在新型冠狀病毒愈演愈烈,感染的人越來越多,但是很多人尤其是中老年人還沒有意識到嚴重性,依然堅持過完年要走親訪友的觀念,這走走那串串。為了避免感染新型冠狀病毒,如何勸說家人不走親訪友呢?今天就來跟大家分享下。
1.告訴家人目前新型冠狀病毒疫情的最新情況,告訴他們每天有多少病例增加,讓他們意識到疫情的傳播速度很快,很容易被感染的,如果出去走親訪友,被傳染的概率會增加。
2.單純的數字可能他們還不以為然,認為全國十幾億人口,自己“中獎機率”很低,沒啥概念,這時候我們可以給他們看看更為直觀的疫情地圖。( http://www.hightopo.com/demo/coronavirus/ )這裡面有每天最新的視覺化資料,幫助大家更為直觀瞭解當前疫情蔓延的分佈趨勢。
3.新聞報道!一般中老年人更相信電視上的新聞,讓他們瞭解病毒的嚴重性,從而打消出門走親訪友的念頭。
4.藥店的相關預防藥物及醫用口罩等已經賣空了,讓他們知道周圍的人都已經在進行自身防護了,建立家人自身防範的意識,從而自覺放棄走親訪友。
5.告知各地封村封路的訊息,讓他們知道疫情的嚴重性。
6.給他們看些硬核標語。
- 口罩還是呼吸機,您老看著二選一;
- 發燒不說的人,都是潛伏在人民群眾中的階級敵人;
- 老實在家防感染,丈人來了也得攆;
- 今年過年不串門,來串門的是敵人,敵人來了不開門;
- 串門就是互相殘殺,聚會就是自尋短見;
- 現在請吃飯的都是鴻門宴
- ......
這個春節,一場突如其來的疫情防控阻擊戰,在中華大地驟然打響,這是一場沒有硝煙的戰爭,無論是醫護人員,還是基礎工作者,他們在疫情爆發的那一刻,就拿起了“武器”奔赴“戰場一線”。病毒並不會因為他們是醫護人員而“網開一面”,歲月靜好,是因為有人在為我們負重前行,感謝那些在疫情中為我們奔赴前行的人,在疫情面前,我們沒有一個人能置身事外。“宅”在家,這是對自己負責,對他人負責,更是對社會、對國家最大的支援。中華兒女從不畏懼任何困難,不會退縮,我們堅信這場戰爭我們肯定能贏,也必須要