寫這篇文章是由於某天看到這樣一個公式 r=a(1-cosθ)
,我上網搜了下,原來是笛卡爾心形線的極座標方程,這個方程裏面的確有一個浪漫又悲情的愛情故事,感興趣的朋友能夠點這裏看看,而至於這個故事是真是假,這 並不重要。html
而這篇文章的目的是要用前端的方式,畫出笛卡爾心形線。
原本我想,這麼經典的公式,網上應該已經有人實現過了吧。
我搜了搜,不得不佩服網友們,有 Java 實現的,有 C# 實現的,也有 canvas 實現的,還能用 ECharts 畫 ,能夠學習學習。前端
好的,開始正文!
先來了解下心形線canvas
心形線,是一個圓上的固定一點在它繞着與其相切且半徑相同的另一個圓周滾動時所造成的軌跡,因其形狀像心形而得名。
由於 canvas 是直角座標系的,因此先來看segmentfault
先貼出網上搜來的 心形線的平面直角座標系方程表達式
分別爲 x^2+y^2+a*x=a*sqrt(x^2+y^2)
和 x^2+y^2-a*x=a*sqrt(x^2+y^2
數組
爲何會有兩個方程表達式?
由於心形線的水平方向 和 垂直方向 對應的方程表達式不一樣,而用相同的方程表達式畫的心形線,把每一個點的 x 座標和 y 座標交換下,又會改變方向,因此會有兩個方程表達式。echarts
好了,開始畫吧,看看這位朋友的作法
思路
根據方程表達式獲得全部點的座標,而後把每一個點鏈接起來,而後填充,最後就行成一個心形了。 函數
參數方程學習
x=a*(2*sin(t)+sin(2*t)) y=a*(2*cos(t)+cos(2*t))
x,y
分別表示一個點的 x 座標 和 y 座標,a
:是一個常數,用來控制心形的大小,t
:表明 弧度t
的取值範圍:-pi<=t<=pi 或 0<=t<=2*pi
動畫
代碼spa
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <canvas width="400" height="400"></canvas> <script> var canvas = document.querySelector('canvas'); var context = canvas.getContext('2d'); context.lineWidth = 3; // 將畫布的原點(0,0),移動到(200,200) // 移動原點是爲了能讓整個心形顯示出來 context.translate(200,200); // t 表明弧度 var t=0; // maxt 表明 t 的最大值 var maxt = 2*Math.PI; // vt 表明 t 的增量 var vt = 0.01; // 須要循環的次數 var maxi = Math.ceil(maxt/vt); // 保存全部點的座標的數組 var pointArr=[]; // x 用來暫時保存每次循環獲得的 x 座標 var x=0; // y 用來暫時保存每次循環獲得的 y 座標 var y=0; // 根據方程獲得全部點的座標 for(var i=0;i<=maxi;i++){ // x=a*(2*sin(t)+sin(2*t)) x=50*(2*Math.sin(t)+Math.sin(2*t)); // y=a*(2*cos(t)+cos(2*t)) y=50*(2*Math.cos(t)+Math.cos(2*t)); t+=vt; pointArr.push([x,y]); } // 根據點的座標,畫出心形線 context.moveTo(pointArr[0][0],pointArr[0][1]); draw(); function draw(){ context.fillStyle='#c00'; // 把每一個點鏈接起來 for(var i=1;i<pointArr.length;i++){ x = pointArr[i][0]; y = pointArr[i][1]; context.lineTo(x,y); } context.fill(); } </script> </body> </html>
效果圖
上面的代碼是畫一個實心的心形,固然咱們也能夠畫空心的,只須要作出一點點的修改就能夠。
咱們只須要改改 draw( ) 函數就好,把原來的 fill( ) 方法,改成 stroke( ) 方法,而且把 strokeStyle 設置了顏色就好了。
function draw(){ //context.fillStyle='#c00'; context.strokeStyle='#c00'; // 把每一個點鏈接起來 for(var i=1;i<pointArr.length;i++){ x = pointArr[i][0]; y = pointArr[i][1]; context.lineTo(x,y); } //context.fill(); context.stroke(); }
極座標系是這樣的
極座標系中肯定一個點的位置,靠的是極點(圖中點O),和 角度 來肯定的。
更多關於極座標系的知識,能夠看看這裏
看看這位朋友的作法
思路
根據極座標方程 r=a(1+sinθ)
,獲得 r ,以 r 做爲半徑,根據 r 連續的去畫圓弧,畫完一圈後,心形就出來了。
心形線 極座標方程r=a(1+sinθ)
代碼
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <canvas width="400" height="400"></canvas> <script> var canvas = document.querySelector('canvas'); var context = canvas.getContext('2d'); // 將畫布的原點(0,0),移動到(200,100) // 移動原點是爲了能讓整個心形顯示出來 context.translate(200, 100); // 畫心形 draw(); function draw() { // 畫圓弧時,圓的半徑 var r = 0; // start 表明畫弧線時的 起始角 var start = 0; // end 表明畫弧線時的 結束角 var end = 0; // 一個常數,用來控制心形的大小 var a = 100; context.fillStyle = '#e21f27'; //連續的畫圓弧 for (var q = 0; q < 500; q++) { start += Math.PI * 2 / 500; // 當 結束角 是 Math.PI * 2 時也就是已經畫了一圈了,心形就出來了 end = start + Math.PI * 2 / 500; // 根據極座標方程 r=a(1+sinθ),獲得 r(半徑) r = a * (1 + Math.sin(start)); // 畫弧線 context.arc(0, 0, r, start, end, false); } context.fill(); } </script> </body> </html>
效果圖
用極座標系 畫法,畫空心心形,也是同樣的須要改改 draw( ) 函數,把原來的 fill( ) 方法,改成 stroke( ) 方法,而且把 strokeStyle 設置了顏色就好了。
function draw() { var r = 0; var start = 0; var end = 0; var a = 100; //context.fillStyle = '#e21f27'; context.strokeStyle = '#e21f27'; for (var i = 0; i < 500; i++) { start += Math.PI * 2 / 500; end = start + Math.PI * 2 / 500; r = a * (1 + Math.sin(start)); context.arc(0, 0, r, start, end, false); } //context.fill(); // 改用 stroke() 方法 context.stroke(); }
可能你會以爲這樣的心形並很差看。
看看這個參數方程吧!
x=16 * (sin(t)) ^ 3; y=13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t)。
根據這個參數方程,用上面說的平面直角座標系的畫法,把代碼裏的方程換一下,就能夠畫出這樣的心形。
代碼
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <canvas width="400" height="400"></canvas> <script> var canvas = document.querySelector('canvas'); var context = canvas.getContext('2d'); context.lineWidth = 3; // 將畫布的原點(0,0),移動到(200,200) // 移動原點是爲了能讓整個心形顯示出來 context.translate(200,200); // t 表明弧度 var t=0; // vt 表明 t 的增量 var vt = 0.01; // maxt 表明 t 的最大值 var maxt = 2*Math.PI; // 須要循環的次數 var maxi = Math.ceil(maxt/vt); // 保存全部點的座標的數組 var pointArr=[]; // 控制心形大小 var size = 10; // x 用來暫時保存每次循環獲得的 x 座標 var x=0; // y 用來暫時保存每次循環獲得的 y 座標 var y=0; // 根據方程獲得全部點的座標 for(var i=0;i<=maxi;i++){ // x=16 * (sin(t)) ^ 3; var x = 16 * Math.pow(Math.sin(t),3); // y=13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t) var y = 13 * Math.cos(t) - 5 * Math.cos(2 * t) -2 * Math.cos(3 * t)- Math.cos(4 * t); t+=vt; pointArr.push([x*size,-y*size]); } // 根據點的座標,畫出心形線 context.moveTo(pointArr[0][0],pointArr[0][1]); draw(); function draw(){ context.fillStyle='#c00'; // 把每一個點鏈接起來 for(var i=1;i<pointArr.length;i++){ x = pointArr[i][0]; y = pointArr[i][1]; context.lineTo(x,y); } context.fill(); } </script> </body> </html>
也許咱們還能夠再作點什麼,好比加點動畫,看看下面這個吧。
點這裏下載源碼,裏面已經加了很詳細的註釋了。
這篇文章主要是說用笛卡爾心形線方程畫心形,可是想要畫出心形的方式絕對是多種多樣的,單純的用CSS也能夠,複雜點 用貝塞爾曲線也能畫出來,你們不妨去試試,說不定又有什麼新發現呢。