简单贝塞尔曲线实现 - Javascript大作业

贝塞尔曲线:

似乎是在Windows XP的屏幕保护选项里面看到过贝塞尔曲线,一直对这个名字比较感兴趣,刚好最近想起来了便百度了一下。

参考:贝塞尔曲线扫盲 是当时第一次看的,讲的挺通俗易懂的;Wiki: wikipedia-Bézier curve ;绘制算法:  de Casteljau's 算法

学习过Photoshop,对里面的钢笔工具印象颇深,看到钢笔工具用的就是贝塞尔曲线的时候内心也是激动不已。

wiki中给出的数学公式还是蛮考研耐心的,不过后面的那个算法还是很容易描述的,大致思路:

  生成点

  枚举 0.001 - 1.000 的每个 T,获得T值对应的坐标:

    将生成的点集合从前到后依次连线

    在每条线段的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作业内存图

posted @ 2018-06-09 21:58  EricJeffrey  阅读(2741)  评论(0编辑  收藏  举报