使用Canvas实现贝塞尔曲线显示以及控制

贝塞尔曲线

场景:

  创建简易的绘图工具,实现控制画布上的四个锚点的拖拽修改已经绘制的曲线图,通过注释实时显示锚点的具体实时坐标,并通过反色清楚的显示控制点的位置

要求:
  1. 创建一个512*512的canvas画布
  2. 在该画布上任意创建四个锚点,使用贝塞尔曲线进行连接
  3. 可在画布上自由拖拽上面的锚点,从而实现贝塞尔曲线的联动,每个点都有一个跟随点移动的注释(文字+箭头),说明该点的坐标
  4.在此基础上,实现一个黑白渐变色的背景,然后在锚点周围20个像素内,将锚点所处的背景作反色,突出锚点的显示

 

思路整理: 

  明确:

    贝塞尔曲线通过canvas绘制,canvas自带贝塞尔曲线绘制方法 bezierCurveTo()

     显示锚点可以在canvas上覆盖div 将锚点放在div中

    锚点拖动,添加事件监听, 鼠标按下  为被点击的锚点 添加鼠标移动事件,松开 注销鼠标移动事件

   拖动锚点时要做什么?

    修改注释内的坐标

    canvas根据锚点坐标重绘贝塞尔曲线

    锚点周围20像素背景色取反

 效果展示

代码(随手写 可自行优化完善)

  1 <style>
  2     * {
  3         margin: 0;
  4         padding: 0;
  5     }
  6 
  7     #myCanvas {
  8         position: absolute;
  9     }
 10 
 11     span {
 12         position: absolute;
 13         width: 20px;
 14         height: 20px;
 15         background: #21e40f;
 16         border: 20px solid #fff;
 17         border-radius: 50%;
 18         color: rgb(25, 6, 204);
 19         text-align: center;
 20         font-size: 14px;
 21         top: 50px;
 22         left: 50px;
 23     }
 24 
 25     #end {
 26         top: 50px;
 27         left: 350px;
 28     }
 29 
 30     #dot1 {
 31         top: 350px;
 32         left: 50px;
 33     }
 34 
 35     #dot2 {
 36         top: 350px;
 37         left: 350px;
 38     }
 39 
 40     i {
 41         position: absolute;
 42         display: block;
 43         width: 60px;
 44         height: 40px;
 45         background: rgba(121, 212, 170, 0.3);
 46         left: 30px;
 47         top: 30px;
 48     }
 49 </style>
 50 
 51 <body>
 52     <!-- 要求:
 53     1. 创建一个512*512的canvas画布
 54     2. 在该画布上任意创建四个锚点,使用贝塞尔曲线进行连接
 55     3. 可在画布上自由拖拽上面的锚点,从而实现贝塞尔曲线的联动,每个点都有一个跟随点移动的注释(文字+箭头),说明该点的坐标
 56     4. 在此基础上,实现一个黑白渐变色的背景,然后在锚点周围20个像素内,将锚点所处的背景作反色,突出锚点的显示 -->
 57 
 58     <!-- 画布 -->
 59     <canvas id="myCanvas" width="512" height="512" style="border:1px solid #000000;">
 60     </canvas>
 61     <!-- 画布遮罩层  用于锚点显示控制 -->
 62     <div id="box">
 63         <!-- 四个锚点 开始  结束  锚点1  锚点2-->
 64         <span id="start">1 <i>1</i></span>
 65         <span id="dot1">2 <i>2</i></span>
 66         <span id="dot2">3 <i>3</i></span>
 67         <span id="end">4 <i>4</i></span>
 68     </div>
 69 
 70     <script>
 71         //画布渐变等
 72         var c, ctx, grd;
 73         //四个锚点
 74         var start, end, dot1, dot2, liArr;
 75 
 76         //坐标等
 77         var x = 0;
 78         var y = 0;
 79         var l = 0;
 80         var t = 0;
 81         var isDown = false;
 82 
 83         init();
 84         function init() {
 85             // 获取锚点
 86             start = document.querySelector('#start')
 87             end = document.querySelector('#end')
 88             dot1 = document.querySelector('#dot1')
 89             dot2 = document.querySelector('#dot2')
 90             //锚点文字说明以及跟随样式
 91             liArr = document.querySelectorAll('i');
 92             //获取画布
 93             c = document.getElementById("myCanvas");
 94             ctx = c.getContext("2d");
 95             // 创建渐变
 96             grd = ctx.createLinearGradient(0, 0, 512, 0);
 97             grd.addColorStop(0, "black");
 98             grd.addColorStop(1, "white");
 99             // 填充渐变
100             ctx.fillStyle = grd;
101             ctx.fillRect(0, 0, 512, 512);
102         
103             start.addEventListener('mousedown', downHandler)
104             end.addEventListener('mousedown', downHandler)
105             dot1.addEventListener('mousedown', downHandler)
106             dot2.addEventListener('mousedown', downHandler)
107 
108             start.addEventListener('mouseup', upHandler)
109             end.addEventListener('mouseup', upHandler)
110             dot1.addEventListener('mouseup', upHandler)
111             dot2.addEventListener('mouseup', upHandler)
112 
113             ctx.beginPath();
114             drawLine()
115             ctx.stroke();
116         }
117         //鼠标按下回调
118         function downHandler(e) {
119             e.stopPropagation();
120             // 获取x坐标和y坐标
121             x = e.clientX;
122             y = e.clientY;
123             //获取左部和顶部的偏移量
124             l = e.target.offsetLeft;
125             t = e.target.offsetTop;
126             //开关打开
127             isDown = true;
128             //设置样式  
129             e.target.style.cursor = 'move';
130             //添加移动侦听
131             e.target.addEventListener('mousemove', moveHandler)
132             e.target.addEventListener('mouseup', moveHandler)
133         }
134         //拖拽回调
135         function moveHandler(e) {
136             if (isDown == false) {
137                 return;
138             }
139             //获取x和y
140             var nx = e.clientX;
141             var ny = e.clientY;
142             //计算移动后的左偏移量和顶部的偏移量
143             var nl = nx - (x - l);
144             var nt = ny - (y - t);
145 
146             e.target.style.left = nl + 'px';
147             e.target.style.top = nt + 'px';
148 
149             ctx.beginPath();
150             drawLine();
151             ctx.stroke();
152         }
153 
154         function upHandler(e) {
155             //注销移动侦听事件
156             e.target.removeEventListener('mousemove', moveHandler)
157         }
158         //绘制贝塞尔曲线 /  设置 文字  /  背景反色
159         function drawLine() {
160             //获取锚点坐标
161             let s1 = setNum(start.offsetLeft);
162             let s2 = setNum(start.offsetTop);
163             let e1 = setNum(end.offsetLeft);
164             let e2 = setNum(end.offsetTop);
165             let dd1 = setNum(dot1.offsetLeft);
166             let dd2 = setNum(dot1.offsetTop);
167             let dd3 = setNum(dot2.offsetLeft);
168             let dd4 = setNum(dot2.offsetTop);
169 
170             ctx.clearRect(20, 20, 100, 50);
171             ctx.fillStyle = grd;
172             ctx.fillRect(0, 0, 512, 512);
173 
174             ctx.moveTo(s1, s2)
175             ctx.bezierCurveTo(dd1, dd2, dd3, dd4, e1, e2);
176             //显示更新锚点坐标
177             liArr[0].innerText = `x=> ${s1} x=> ${s2}`;
178             liArr[1].innerText = `x=> ${dd1} x=> ${dd2}`;
179             liArr[2].innerText = `x=> ${dd3} x=> ${dd4}`
180             liArr[3].innerText = `x=> ${e1} x=> ${e2}`
181 
182             //设置边框颜色
183             setColor(start, s1, s2);
184             setColor(end, e1, e2);
185             setColor(dot1, dd1, dd2);
186             setColor(dot2, dd3, dd4);
187         }
188         //处理贝塞尔曲线坐标点, 与div 中心重合
189         function setNum(str) {
190             return parseInt(str) + 30
191         }
192         //设置背景色反色
193         function setColor(target, x, y) {
194             let col = ctx.getImageData(x, y, 1, 1).data;
195             for (let i = 0; i < col.length; i++) {
196                 col[i] = 255 - col[i]
197             }
198             console.log(target, col);
199             target.style.border = `20px solid rgb(${col[0]},${col[1]},${col[2]})`
200         }
201     </script>
202 </body>

 

 

 

 

 

 

--

 

posted @ 2020-10-01 12:03  低调奢华有内涵的ID  阅读(845)  评论(0编辑  收藏  举报