iT邦幫忙

4

[筆記][HTML][JavaScript]canvas的基本用法(4)-一點都不基本的極座標!

嗨!大家好,到了每個禮拜的發文時間了(才怪這禮拜已經四篇了XD),今天要來說說[筆記][HTML][JavaScript]canvas的基本用法(3)-最後用滑鼠互動動畫做個結尾!這篇文章裡稍微提到的極座標。


好的,那什麼是極座標?讓我們先看看從維基百科裡查到的解釋:

極座標系(英語:Polar coordinate system)是一個二維座標系統。該座標系統中任意位置可由一個夾角和一段相對原點—極點的距離來表示」

OK,讓我們搭配圖形,來簡單說說這是怎麼一回事:
https://ithelp.ithome.com.tw/upload/images/20180819/20106935K9QKfu2G4O.jpg
上圖中的黑色橫線是X軸、直線是Y軸,藍點是座標為(0,0)的原點,以原點當圓心畫一個半徑為r的圓形,這時候我們在圓邊上的任一點上標記一個綠點,也就是上文說的極點,將極點與圓心相連畫出橘色與紫色的線,當然,這兩條線的長度也是圓的半徑r,仔細看一下,這兩條線和X軸之間的夾角不同,會導致線的方向也不一樣,在三角函數中中我們稱這一角度為θ他的英文是theta,唸做夕塔XD。

那接著我們如果要取得某一極點的座標該怎麼做呢?這就要吐出剛剛提到的三角函數了,不過大家不要緊張,我們只需要知道兩種而已,那就是正弦sin和餘弦cos,以下會用最簡單的方式來講解這兩個函數求出來的值,那還是先上圖:
https://ithelp.ithome.com.tw/upload/images/20180819/20106935r6uECuFCms.jpg
這是一張非常簡單的圖,圖中藍點是我們的圓點,t是夾角θ,而下邊xX軸距離原點的距離,右邊的y則是Y軸距離原點的距離,在右上方的綠點則是極點,所以我們可以透過三角型可以得知,極點的座標是(原點的X軸座標 + x,原點的y軸座標 + y),但我們該怎麼知道xy的數值各是多少?先看看以下關於sincos的公式:

sinθ = y / r
cosθ = x / r

因為除法太不親民了,所以我們把,公式中的x和三角函數交換位置,轉換成乘法:

y = sinθ * r
x = cosθ * r

看起來是不是簡單多了!只要我們知道半徑rsinθcosθ,就有辦法取得任一極點的座標,也就是今天的主題極座標。

也許你會想說,我知道半徑r,但我對那個sinθcosθ實在不曉得該怎麼辦才好,放心在JavaScript中這兩個函數已經妥妥的準備好!剩下的就是動手實做!讓我們來做個簡單的小例子吧!

以上一次的太陽為例子來重新繪製他身邊的光線,我先刪減了背景顏色變化和之前繪製光線的部分,只留下太陽的本體,程式碼如下:
HTML

<canvas id="myCanvas"></canvas>

JavaSript

let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");

//紀錄滑鼠位置
let mouse = {
    x : 0,
    y : 0,
};

//監聽滑鼠事件
window.addEventListener('mousemove',(event) => {
    mouse.x = event.pageX;
    mouse.y = event.pageY;
});

//一個繪製的function
const draw = () =>{
    //把視窗大小重新設定給canvas
    canvas.height = window.innerHeight;
    canvas.width = window.innerWidth;
    
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    //之後繪製太陽
    ctx.beginPath();
    ctx.arc(mouse.x,mouse.y,30,0,Math.PI*2);
    ctx.strokeStyle="#FF5511";
    ctx.stroke();
    ctx.fillStyle="#FFFF00";
    ctx.fill();
    
    //繪製光芒線條
    lightLine();
};

//繪製光芒線條的function
const lightLine = () =>{
    
};
//每隔50毫秒重新繪製一次
setInterval('draw()',50);

https://ithelp.ithome.com.tw/upload/images/20180819/20106935HstW3pFHPR.jpg
這時候我們的太陽會回到上圖,只有本體的樣子,再來會專注在lightLinefunction中,但在這之前先複習一下[筆記][HTML][JavaScript]canvas的基本用法(1)-來繪製一些簡單的圖形吧!講的兩件事情

  1. JavaScript中的Math.PI是代表角度180度。
  2. 在一個座標中,角度從0度,也就是貼著X軸開始,越下面角度越大,直到繞一圈回到0度,可以參照下圖:
    https://ithelp.ithome.com.tw/upload/images/20180819/20106935otJE60FQqb.jpg

複習完畢後,就來畫光線吧!先整理一下剛剛講過的東西,從原點開始到任一極點的線段我們可以把它當成半徑r,而這時候的原點,是太陽的中心;太陽的中心又是跟著滑鼠跑的,所以原點為mouse物件中的xy,接著試想一下太陽的半徑是30,光線又一定會超越太陽本身,所以r的長度會大於30,這裡先給定60,有了原點、半徑後,我們還需要角度,不過現階段我們先設角度為0,繪製出如同X軸的右線:
JavaScript

//繪製光芒線條的function
const lightLine = () =>{
    //開始繪圖
    ctx.beginPath();
    //開始點為原點,滑鼠目前的所在座標
    ctx.moveTo(mouse.x,mouse.y);
    
    //從原點到某一極座標,用文章開頭說的公式帶入數字,設角度為0,r為60,取得X軸及Y軸
    //求X的公式為x = cosθ * r
    let X = Math.cos(0) * 60;
    //求y的公式為x = sinθ * r
    let Y = Math.sin(0) * 60;
    
    //上方取得的X及Y為從原點到某一極點的距離,所以我們把原點的所在座標加上該數值
    ctx.lineTo(mouse.x + X,mouse.y + Y);
    ctx.stroke();
};

如此一來就能夠畫出從原點到某一極點的線段了!
https://ithelp.ithome.com.tw/upload/images/20180819/20106935C24edNr19X.jpg
看了結果後可能你會覺得奇怪,線條的起始點不是應該要距離太陽有些距離的嗎?沒錯!所以我們稍微想一下,目前設定的起始點是原點,如果要讓他在太陽的本體外面開始,那要改變哪個數值呢?

  1. 半徑
  2. 角度

嘿嘿!對的!就是半徑,既然可以讓結束的點用極座標控制在太陽本體之外,那一定也可以讓起始點做到一樣的事情,剛剛預設結束點的半徑r是60,那我們讓起始點的半徑r大於太陽的半徑30一點點,就設定為40。以下程式:
JavaScript

const lightLine = () =>{
    ctx.beginPath();
    //把r設為40取x軸和y軸距離原點的距離
    let X = Math.cos(0) * 40;
    let Y = Math.sin(0) * 40;
    //用原點加上該距離
    ctx.moveTo(mouse.x + X,mouse.y + Y);
    
    //結束點不變
    X = Math.cos(0) * 60;
    Y = Math.sin(0) * 60;
    ctx.lineTo(mouse.x + X,mouse.y + Y);
    ctx.stroke();
};

https://ithelp.ithome.com.tw/upload/images/20180819/20106935ANWNMMRs00.jpg
經過上段程式的調整我們會得出上面的太陽,距離太陽一些間隔的線段,這就是我們要的,但還不完全,我們要能夠去畫出每一個角度的線條,並用迴圈繪製每條線段。

那該怎麼得到每個角度呢?目前我們知道的只有代表180度的Math.PI,如果今天要得到60度,可以用360度,也就是先把Math.PI*2(180度乘上2)後在除於6來處理,但是如果要取的角度是17度呢?147度?273度?該怎麼辦?就用比例原則吧!

所謂的比例原則,就是先得出該角度占了360度的多少。以17度為例,用17/360來算出該角度在360度中的比例,大約是0.047222...左右,那在以這個數字去乘回代表360度的Math.PI*2就有辦法取得任一圓的任一角度!我們把這個公式帶進程式中:
JavaScript

const lightLine = () =>{
    ctx.beginPath()
    //設定一個變數為角度
    let t = 0;
    //假設我們要畫夾角為17度的線段
    t = 17
    /*把計算cosθ和sinθ的切出來
    並以角度t除上360度取得比例後在乘上Math.PI*2獲得角度
    */
    let cosT = Math.cos((t/360) * (Math.PI*2))
    let sinT = Math.sin((t/360) * (Math.PI*2))
    
    let X = cosT * 40;
    let Y = sinT * 40;
    ctx.moveTo(mouse.x + X,mouse.y + Y);
    
    X = cosT * 60;
    Y = sinT * 60;
    ctx.lineTo(mouse.x + X,mouse.y + Y);
    ctx.stroke();
};

https://ithelp.ithome.com.tw/upload/images/20180819/20106935A97GepX1kp.jpg
如上圖,會微妙的發現線段往下偏移了17度。那現在各位大大們應該已經了解極座標了!剩下光芒線條,就可以直接用迴圈處理:

const lightLine = () =>{
    ctx.beginPath()
    
    //把角度t放到迴圈中,從0開始到360度,每隔20度就畫繪製一條光線
    for(let t=0;t<360;t+=20){
        let cosT = Math.cos((t/360) * (Math.PI*2))
        let sinT = Math.sin((t/360) * (Math.PI*2))
        let X = cosT * 40;
        let Y = sinT * 40;
        ctx.moveTo(mouse.x + X,mouse.y + Y);
        X = cosT * 60;
        Y = sinT * 60;
        ctx.lineTo(mouse.x + X,mouse.y + Y);
        ctx.stroke();
    };
};

登愣!使用極座標繪製的太陽完成了!有沒有覺得用這種方式繪製出來的光線比較自然XD,如果要增加或減少線條的話,主要是在t+=20這個地方做設置,如果希望光線密集一點,就讓間隔的角度小一些,如果希望他寬鬆一點,就加大間隔,小弟我在此附上CodePen連結,如果有興趣的話可以試試看他的變化,基本上就和上次的差不多XD。
https://ithelp.ithome.com.tw/upload/images/20180819/20106935t6TWRsY0yh.jpg


終於找了個時間研究一下極座標這玩意兒,也沒想到原來三角函數這麼簡單XD,這篇應該不會是Canvas的最後一篇,只是之後可能不會繼續用canvas的基本用法基本用法這個系列名稱了,因為之後的可能一點都不基本,哈哈哈,不過如果有還是會繼續加上去啦!

之後我會打算做一個小遊戲,就之前參加六角學院那個活動的其中一個題目,雖然當時有先交了最低標準的作品出去,但還是會想要把他完成,如果好奇的話可以點這裡看設計稿,剛剛看了一下還是覺得很難XD,如果各位大大有什麼建議,或經驗分享還請留言指教我了,小弟我會感激不盡的/images/emoticon/emoticon41.gif

那最後謝謝大家的觀看!如果文章中有任何建議、解釋錯誤或是不清楚的地方,都可以留言告訴我,我會盡快修改的!謝謝大家!

參考文章:

  1. https://zh.wikipedia.org/wiki/%E6%9E%81%E5%9D%90%E6%A0%87%E7%B3%BB
  2. https://zh.wikipedia.org/wiki/%E4%B8%89%E8%A7%92%E5%87%BD%E6%95%B0

尚未有邦友留言

立即登入留言