今天來講解“雷達掃描”效果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>
至此,完成效果,符合預期,完美~
****************************************************
感謝各位收看,下期再見~~~
