今天來講解“雷達掃描”效果demo,來源於QQ群里邊有群友說想要個雷達效果,就嘗試寫了一下。
效果圖:
demo鏈接: https://win7killer.github.io/demo_set/html_demo/canvas/can_demo/radar.html
********************************************************************
這個東西,背景圓,坐標、圓圈都很簡單實現,arc結合moveTo、lineTo就可以解決,背景色也不是問題,一句帶過。
那么,有挑戰的地方,就是這個掃描的東西
特點:
1、旋轉
2、漸變
開始實現:
1、誤入歧途
首先考慮了過渡色,實現過渡色之后,只需要旋轉canvas,恩,完美~(頭腦簡單的例子,后邊發現這思路行不通)
step1. *過渡色*
過渡色只有“線性過渡”、“輻射過渡(環形過渡)”,而這個效果需要的是一種類似於“扇形側面過渡”(木有這種過度,我瞎叫的)。環形過渡並不滿足需求,只能考慮線性過渡。
考慮到canvas路徑的填充(fillStyle)可以使用過渡色對象,先實現第一幀的過渡,開搞。
代碼如下:
1 var grd = ctx.createLinearGradient(175,100,can.width,150); 2 3 grd.addColorStop(0,"rgba(0,255,0,0)"); 4 grd.addColorStop(1,"rgba(0,255,0,1)");
然后繪制一個扇形,去填充
1 ctx.fillStyle = grd; 2 ctx.beginPath(); 3 ctx.moveTo(150,150); 4 ctx.arc(150, 150, 150, -90/180*Math.PI, 0/180*Math.PI); 5 ctx.fill();
加上背景色
1 ctx.fillStyle = 'rgba(0,0,0,1)'; 2 ctx.strokeStyle = 'rgba(0,255,0,1)'; 3 4 ctx.arc(150,150,150,0,2*Math.PI); 5 ctx.fill();
效果圖如下:
還算有那么點樣子哦~,接下來就是讓它動起來
step2. *旋轉*
旋轉思路:旋轉點在canvas的中心點,圍繞中心點旋轉,然后不停的繪制掃描區的扇形。
用了之前的旋轉函數
1 function drawRotate(deg, fn) { 2 ctx.save(); 3 ctx.translate(can.width/2, can.height/2); 4 ctx.rotate(deg); 5 fn && fn(ctx); 6 ctx.restore(); 7 }
但是!!!!真的轉起來的時候,問題來了。
扇形的旋轉完美,沒問題,說明這個旋轉函數也沒問題。
問題出在過渡色身上。。。
過渡色創建的時候,走向是固定的,在渲染到扇形后,依舊是一樣的走向(扇形每次都要重繪),導致出現錯誤的結果。
由於原來的錯誤代碼不全了,所以就沒圖給大家看了。大家可以自己試一下。
有考慮到在旋轉的過程中去改變過渡色走向,但是涉及到比較繁瑣的計算,還是放棄了(比較懶,如果真的去算位置,應該是可以達到效果的)。
於是放棄,去吃午飯了,大腦肯定是去能量了。
2、迷途折返
午飯過后,繼續思考,換思路。
經過考慮,想起以前做“字幕雨”(類似黑客帝國)的思路來。
附: 字幕雨鏈接:https://win7killer.github.io/demo_set/html_demo/canvas/text_rain.html
思路如下:
整體思路變化,先處理旋轉,再處理過渡。
step1. 旋轉
以小角度(1°-5°)繪制純色的扇形,沒錯,就是純色的,不要過渡色,然后旋轉,以保證掃描區前邊亮色。這樣,旋轉一周,會r讓整個雷達高亮。
注意,這里的旋轉不再是旋轉canvas,而是不斷改變繪制扇形的角度。
1 function drawRadar(iDeg) { 2 ctx.fillStyle = 'rgba(0,200,0,.7)'; 3 ctx.beginPath(); 4 ctx.moveTo(150, 150); 5 ctx.arc(150, 150, 150, (-2 * CFG.perDeg + iDeg) / 180 * Math.PI, (0 + iDeg) / 180 * Math.PI); 6 ctx.closePath(); 7 ctx.fill(); 8 }
step2. *扇形*
然后,處理過渡。仔細考慮, 這個並不是“過渡色”效果,真的不是,而是“漸進消隱”效果,就是出現后高亮,慢慢消失的效果。
此類“漸進消隱”效果的做法,很簡單,用rgba半透明色(飽和度1的時候與背景色相同)填充整個canvas,一層一層覆蓋上去,就會得到慢慢消失的效果。
loop以下代碼:
1 function cover() { 2 ctx.save(); 3 ctx.fillStyle = 'rgba(0,0,0,0.02)'; 4 ctx.arc(150, 150, 150, 0, 2 * Math.PI); 5 ctx.fill(); 6 ctx.restore(); 7 }
在整個loop中先去覆蓋之前的,然后去重繪坐標、圓環等,重繪該改變角度的扇形,就達到了效果,完美。
最終整體代碼如下:
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>radar</title> <style> canvas { margin: 20px auto; display: block; } </style> </head> <body> <canvas id="can" width=300 height=300></canvas> <script type="text/javascript"> var CFG = { perDeg: 1, }; var can = document.getElementById('can'); var ctx = can.getContext('2d'); var deg = 0; ctx.strokeStyle = 'rgba(0,255,0,1)'; function init() { ctx.fillStyle = 'rgba(0,50,0,1)'; ctx.arc(150, 150, 150, 0, 2 * Math.PI); ctx.fill(); var raf = window.requestAnimationFrame(loop); } function loop() { deg = (deg + CFG.perDeg); cover(); drawPosLine(); drawRadar(deg); raf = window.requestAnimationFrame(loop); } function cover() { ctx.save(); ctx.fillStyle = 'rgba(0,0,0,0.02)'; ctx.arc(150, 150, 150, 0, 2 * Math.PI); ctx.fill(); ctx.restore(); } function drawPosLine() { ctx.beginPath(); ctx.moveTo(150, 0); ctx.lineTo(150, 300); ctx.closePath(); ctx.stroke(); ctx.beginPath(); ctx.moveTo(0, 150); ctx.lineTo(300, 150); ctx.closePath(); ctx.stroke(); ctx.moveTo(150, 150); ctx.beginPath(); ctx.arc(150, 150, 100, 0 * Math.PI, 2 * Math.PI); ctx.closePath(); ctx.stroke(); ctx.moveTo(150, 150); ctx.beginPath(); ctx.arc(150, 150, 50, 0 * Math.PI, 2 * Math.PI); ctx.closePath(); ctx.stroke(); } function drawRadar(iDeg) { ctx.fillStyle = 'rgba(0,200,0,.7)'; ctx.beginPath(); ctx.moveTo(150, 150); ctx.arc(150, 150, 150, (-2 * CFG.perDeg + iDeg) / 180 * Math.PI, (0 + iDeg) / 180 * Math.PI); ctx.closePath(); ctx.fill(); } init(); </script> </body> </html>
至此,完成效果,符合預期,完美~
****************************************************
感謝各位收看,下期再見~~~