zoukankan      html  css  js  c++  java
  • 简单贝塞尔曲线实现

    贝塞尔曲线:

    似乎是在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作业内存图

  • 相关阅读:
    常用的Intent.Action(转)
    Android存储--SharedPreferences
    Linux虚拟机网络配置
    SSH的两种登录方式以及配置
    Docker学习のC/S模式
    Docker学习のDocker镜像
    Docker学习のDocker中部署静态页网站
    Docker学习のWindows下如何访问Docker本身的虚拟机
    Docker学习のDocker的简单应用
    Docker学习の更改Docker的目录
  • 原文地址:https://www.cnblogs.com/bolderic/p/9161546.html
Copyright © 2011-2022 走看看