使用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>
--