簡單貝塞爾曲線實現 - Javascript大做業

貝塞爾曲線: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內存圖:

 

 

 

JS做業內存圖

相關文章
相關標籤/搜索