貝塞爾曲線:css
彷佛是在Windows XP的屏幕保護選項裏面看到過貝塞爾曲線,一直對這個名字比較感興趣,恰好最近想起來了便百度了一下。html
參考:貝塞爾曲線掃盲 是當時第一次看的,講的挺通俗易懂的;Wiki: wikipedia-Bézier curve ;繪製算法: de Casteljau's 算法ios
學習過Photoshop,對裏面的鋼筆工具印象頗深,看到鋼筆工具用的就是貝塞爾曲線的時候心裏也是激動不已。git
wiki中給出的數學公式仍是蠻考研耐心的,不事後面的那個算法仍是很容易描述的,大體思路:github
生成點算法
枚舉 0.001 - 1.000 的每一個 T,得到T值對應的座標:chrome
將生成的點集合從前到後依次連線canvas
在每條線段的t值處取新點瀏覽器
將新的點集做爲點集合重複上述操做直到只剩下一個點服務器
繪製剩下的這個點
其中的T值實際上就是一個位置,更具體的信息能夠參考連接中的描述。
使用HTML5的canvas,經過JavaScript實現上述算法:
大體思路上面也說過了,具體的話就是使用canvas的fillrect來繪製點了
另外,因爲JavaScript自己是單線程的,而個人程序須要一個死循環(隨機嘛,不停地畫曲線~),所以須要使用Worker進行多線程操做,它的邏輯就更簡單了,主線程:
1 var worker = new Worker("caculatePoint.js"); 2 worker.postMessage(data);
1 onmessage = function startHere(event) { 2 ... 3 postMessage(data); 4 }
看起來應該是至關易懂了。主線程經過另外一個js文件建立新線程,發消息啓動子線程,子線程發消息通知主線程。
實際效果(一次畫6條貝塞爾曲線(尾首相連)而後清空畫布):
JavaScript做業:
做業沒什麼好說的,主要是要畫執行模型和對象模型的內存圖,已經貼出,歡迎指正
須要注意的是,使用引入的iframe以及JavaScript時最好是在搭建好的服務器上運行,否則使用chrome瀏覽器運行時可能會出現錯誤,不過用ie,edge以及Firefox時沒問題的。
因爲貝塞爾曲線部分是JavaScript做業的一部分,所以詳細代碼包括 兩個HTML文件,兩個JS文件,一個css文件和幾個圖標。代碼以下:
main.html:
1 <!DOCTYPE html> 2 <html lang=zh-cmn-Hans> 3 4 <head> 5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 6 <title>20152480227_SJF</title> 7 <link rel="stylesheet" type="text/css" media="screen" href="main.css" /> 8 <script src="main.js"> 9 </script> 10 </head> 11 12 <body style="background: whitesmoke;background: url(bg_partical.jpg)"> 13 <div style="float: left;"> 14 <div> 15 <iframe src="iframe.htm" id="myiframe" class="my_iframe"> 16 </iframe> 17 </div> 18 <div> 19 <textarea id="myinput" class="my_text_area"></textarea> 20 </div> 21 <div class="my_bot_button"> 22 <input type="radio" checked name="myradio" value="add" onclick="setOKButtonText('添加')" />添加 23 <input type="radio" name="myradio" value="replace" onclick="setOKButtonText('替換')" />替換 24 <input type="radio" name="myradio" value="delete" onclick="setOKButtonText('刪除')" />刪除 25 <input type="radio" name="myradio" value="insert" onclick="setOKButtonText('插入')" />插入 26 <select id="myselect"> 27 <option value="0">0</option> 28 <option value="1">1</option> 29 <option value="2">2</option> 30 <option value="3">3</option> 31 </select> 32 <input type="submit" value="添加" id="myokbutton" onclick="goHere()"> 33 </div> 34 <br /> 35 <br /> 36 <br /> 37 </div> 38 <div class="my_right_canvas" id="mycanvasdiv" ondblclick="changeCanvasVisibility()"> 39 <canvas id="canvas" width="850" height="720" style="background: white"></canvas> 40 <script> 41 var width = 850, height = 720; 42 canvas = document.getElementById("canvas"); 43 ctx = canvas.getContext("2d"); 44 ctx.fillStyle = "#FF0000"; 45 46 var cnt = 0; 47 var worker = new Worker("caculatePoint.js"); 48 worker.postMessage(''); 49 worker.addEventListener('message', function (e) { 50 if (cnt != e.data.cnt) { 51 ctx.fillStyle = "#" + parseInt(Math.random() * 256).toString(16) + 52 parseInt(Math.random() * 256).toString(16) + 53 parseInt(Math.random() * 256).toString(16); 54 } 55 ctx.fillRect(e.data.x, e.data.y, 3, 3); 56 if (cnt != e.data.cnt && e.data.cnt % 6 == 0) { 57 ctx.fillStyle = "#FFFFFF"; 58 ctx.fillRect(0, 0, width, height); 59 ctx.fillStyle = "#ff0000" 60 } 61 cnt = e.data.cnt; 62 }, false); 63 </script> 64 </div> 65 </body> 66 67 </html>
iframe.html
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <link rel="stylesheet" type="text/css" media="screen" href="main.css" /> 5 </head> 6 <body class="my_iframe_body" id="myiframebody"> 7 <p style="font-size: 28px;font-family: Jokerman, 漢儀力量黑簡; color: rgb(165, 25, 25)"> 8 JS Homework.<br/>JavaScript 大做業 9 </p> 10 <p> 11 You can choose to add, replace, delete or insert one paragraph when you have input your text. 12 <br/>增刪改查隨便選~ 13 </p> 14 <p> 15 Make sure you choose the right place if you do insertion. 16 <br/> 插入段落時確保你已經選擇了正確的序號~ 17 </p> 18 <p> 19 Empty input will be ignored! 20 <br/>不輸入任何字符將不會產生任何操做! 21 </p> 22 </body> 23 24 </html>
main.js
1 var myifbody; 2 3 4 /** 5 * 啓動時獲取iframe的body 6 */ 7 window.onload = function init() { 8 myifbody = document.getElementById("myiframe").contentDocument.body; 9 } 10 11 /** 12 * 將 {@link #myokbutton} 的值替換爲 s 13 * @param {string} s 待替換的字符串 14 */ 15 function setOKButtonText(s) { 16 document.getElementById("myokbutton").setAttribute("value", s); 17 } 18 19 /** 20 * 更改容納canvas的div的visibility屬性 21 */ 22 function changeCanvasVisibility() { 23 canvasdiv = document.getElementById("canvas"); 24 console.debug("double here"); 25 if (canvasdiv.style.visibility == "hidden") { 26 canvasdiv.style.visibility = "visible"; 27 } 28 else { 29 canvasdiv.style.visibility = "hidden"; 30 } 31 } 32 33 /** 34 * 點擊 {@link #myokbutton} 時執行此函數 35 */ 36 function goHere() { 37 // text area 38 s = document.getElementById("myinput").value; 39 40 //radio input, know what to do(append, replace, delete, insert) 41 myradios = document.getElementsByName("myradio"); 42 cho = 0; 43 for (cho; cho < myradios.length; cho++) { 44 if (myradios[cho].checked) 45 break; 46 } 47 //ignore empty string 48 if (s.length <= 0 && cho != 2) 49 return; 50 //clear text area 51 document.getElementById("myinput").value = ""; 52 ind = document.getElementById("myselect").selectedIndex; 53 54 switch (cho) { 55 case 0: 56 doAppend(s); 57 break; 58 case 1: 59 doReplace(ind, s); 60 break; 61 case 2: 62 doDelete(ind, s); 63 break; 64 case 3: 65 doInsert(ind, s); 66 break; 67 default: 68 break; 69 } 70 } 71 72 /** 73 * 將序號爲ind的元素的innertext替換爲s 74 * @param {number} ind 待替換的HTMLElement序號 75 * @param {string} s 用來替換的新字符串 76 */ 77 function doReplace(ind, s) { 78 myifbody.getElementsByTagName("p")[ind].innerText = s; 79 } 80 81 /** 82 * 在iframe.body的ind序號處插入一個內容爲s的p標籤 83 * @param {number} ind 待插入的位置 84 * @param {string} s 待插入的字符串 85 */ 86 function doInsert(ind, s) { 87 p = document.createElement("p"); 88 p.innerText = s; 89 myifbody.insertBefore(p, myifbody.getElementsByTagName("p")[ind]); 90 addOrRemoveSelect(true); 91 } 92 93 /** 94 * 將iframe.body的ind序號處的元素刪除 95 * @param {number} ind 待刪除的序號 96 */ 97 function doDelete(ind) { 98 myifbody.removeChild(myifbody.getElementsByTagName("p")[ind]); 99 addOrRemoveSelect(false); 100 } 101 102 /** 103 * 將一個內容爲s的p標籤添加到iframe.body尾部 104 * @param {string} s 待添加的字符串 105 */ 106 function doAppend(s) { 107 p = document.createElement("p"); 108 p.innerText = s; 109 myifbody.appendChild(p); 110 addOrRemoveSelect(true); 111 } 112 113 /** 114 * 從select下拉框中添加或移除一個元素 115 * @param {boolean} add true爲添加,false爲移除 116 */ 117 function addOrRemoveSelect(add) { 118 myselect = document.getElementById("myselect"); 119 if (add) { 120 toAppendOption = document.createElement("option"); 121 toAppendOption.setAttribute("value", myselect.length); 122 toAppendOption.innerText = myselect.length; 123 myselect.appendChild(toAppendOption); 124 } 125 else if (!add) { 126 myselect.removeChild(myselect.getElementsByTagName("option")[myselect.length - 1]); 127 } 128 }
caculatePoint.js
1 function sleep(numberMillis) { 2 var now = new Date(); 3 var exitTime = now.getTime() + numberMillis; 4 while (true) { 5 now = new Date(); 6 if (now.getTime() > exitTime) 7 return; 8 } 9 } 10 11 var cnt = 1; 12 function drawCurve(points, t) { 13 if (points.length == 1) { 14 // ctx.fillStyle = "#4F33DD"; 15 // document.write("<br/>" + "drawing: " + points[0].x + " " + points[0].y); 16 // ctx.fillRect(points[0].x, points[0].y, 1, 1); 17 var data = new Object(); 18 data.x = points[0].x; 19 data.y = points[0].y; 20 data.cnt = cnt; 21 this.postMessage(data); 22 sleep(1); 23 } 24 else { 25 newpoints = new Array(points.length - 1); 26 for (var i = 0; i < newpoints.length; i++) { 27 x = (1 - t) * points[i].x + t * points[i + 1].x; 28 y = (1 - t) * points[i].y + t * points[i + 1].y; 29 newpoints[i] = new Object(); 30 newpoints[i].x = x; 31 newpoints[i].y = y; 32 } 33 drawCurve(newpoints, t); 34 } 35 } 36 onmessage = function startHere(event) { 37 var width = 900, height = 750; 38 var n = 7; 39 var fir = new Object(), sec = new Object(); 40 fir.x = Math.random() * width; 41 fir.y = Math.random() * height; 42 sec.x = Math.random() * width; 43 sec.y = Math.random() * height; 44 while (true) { 45 var points = new Array(); 46 points[0] = fir; 47 points[1] = sec; 48 for (var i = 2; i < n; i++) { 49 var x = Math.random() * width; 50 var y = Math.random() * height; 51 points[i] = new Object(); 52 points[i].x = x; 53 points[i].y = y; 54 } 55 for (var t = 0.0001; t < 1; t += 0.0007) { 56 drawCurve(points, t); 57 } 58 cnt++; 59 fir.x = points[n - 1].x; fir.y = points[n - 1].y; 60 sec.x = points[n - 2].x; sec.y = points[n - 2].y; 61 } 62 }
main.css
1 @charset "utf-8"; 2 .my_iframe{ 3 box-shadow: lightgrey 1px 3px 5px; 4 border: none; 5 margin: 5px; 6 background:steelblue; 7 width: 1000px; 8 height: 450px; 9 } 10 .my_iframe_body{ 11 color: white; 12 font-family: Ubuntu Mono, '說說體'; 13 font-size: 23px; 14 } 15 .my_text_area{ 16 width: 995px; 17 height: 200px; 18 font-family: Ubuntu Mono, '說說體'; 19 display: block; 20 box-shadow: gray 1px 3px 5px; 21 font-size: 23px; 22 margin: 5px; 23 } 24 .my_bot_button{ 25 font-family: 說說體; 26 font-size: 19px; 27 float: left; 28 background: white; 29 padding: 10px; 30 margin-left: 5px; 31 display: block; 32 box-shadow: gray 1px 3px 5px; 33 } 34 .my_right_canvas{ 35 display: block; 36 box-shadow: gray 1px 3px 5px; 37 float: right; 38 margin-top: 5px; 39 margin-right: 20px 40 }
JS執行模型,對象模型,BOM以及DOM內存圖: