原生 canvas 绘制椭圆,计算角度、绘制线段、调整宽高、移动位置

435 阅读3分钟

实现效果(GIF)

无标题视频——使用Clipchamp制作.gif

代码

实现细节

canvas 实现鼠标绘图原理

image.png

  1. 鼠标按下时,记录下鼠标坐标
  2. 鼠标滑动时清空画布,循环记录列表绘制之前的元素,然后根据鼠标当前坐标鼠标按下时的坐标计算所需数据,绘制当前元素;
  3. 鼠标抬起时将鼠标当前坐标鼠标按下时的坐标信息添加到记录列表中。

绘制线段

直接执行提供的方法即可。

ctx.strokeStyle = '#bcd2ff' // 设置线段颜色
ctx.beginPath(); // 开始绘制
ctx.moveTo(x1, y1);// 画笔移动到x1,y1位置
ctx.lineTo(x2, y2);// 绘制到x2,y2位置
ctx.stroke();// 绘制

绘制网格

为了方便定位,给画布添加网格线。

实现思路就是从画布最左侧/最上测开始,每隔一段距离绘制一条线段,一直到最右侧/最下测。

let span = 20 // 网格间隔
let color = '#efefef' // 网格颜色

ctx.strokeStyle = color
ctx.beginPath()
for (let h = span; h < this.height; h += span) {
  ctx.moveTo(0, h);
  ctx.lineTo(this.width, h);
}
for (let w = span; w < this.width; w += span) {
  ctx.moveTo(w, 0);
  ctx.lineTo(w, this.height);
}
ctx.stroke();

绘制椭圆

canvas提供了ellipse方法,直接传参执行方法即可:

ctx.ellipse(
    圆心x,
    圆心y,
    椭圆长轴的半径,
    椭圆短轴的半径,
    椭圆的旋转角度(弧度表示),
    绘制的起始点角度(弧度),
    绘制的结束点角度(弧度),
    `true`:逆时针方向绘制椭圆
)

image.png

如图,已知条件鼠标按下的坐标滑动后当前坐标,剩下的就是简单的数学题了。

通过鼠标当前坐标减去鼠标按下时的坐标就是半径的长度,半径的长度加上按下时的坐标就是圆心的坐标了。

判断鼠标是否点在元素上

一个数学问题。

如图:

image.png

x1>=x0>=xx1>=x0>=x 并且y1>=y0>=yy1>=y0>=y时,点在矩形范围内。

每绘制一个元素,将元素的坐标信息存在记录列表中,鼠标点击时循环记录列表根据上边的逻辑挨个进行计算,即可判断有没有点到元素上。

移动元素

  1. 鼠标点击元素时,记录下鼠标按下时的坐标
  2. 鼠标移动时,根据鼠标按下时的坐标和被点击元素的坐标信息计算偏移距离,然后重新绘制元素。

image.png

放大缩小元素

这里的放大缩小不是鼠标滚珠滚动缩放,是点击四角的控制点拖拽调整大小。

image.png

和绘制的逻辑一样,通过对鼠标坐标进行计算,重新绘制。

// this.currentState = {
//     scope: { x1:'左上角x', y1:'左上角y', x2:'右下角x', y2:'右下角y' }
// }
switch (this.currentState) {
  // 左上角
  case 'zoomlt':
    // 设置鼠标样式
    body.style.cursor = "nwse-resize"
    设置
    this.startInfo = {x: this.currentShape.scope.x2, y: this.currentShape.scope.y2};
    break
  // 右上角
  case 'zoomrt':
    body.style.cursor = "nesw-resize"
    // 鼠标初始点击点设为当前选中的元素的左下坐标
    this.startInfo = {x: this.currentShape.scope.x1, y: this.currentShape.scope.y2}
    break
  // 左下角
  case 'zoomlb':
    body.style.cursor = "nesw-resize"
    this.startInfo = {x: this.currentShape.scope.x2, y: this.currentShape.scope.y1};
    break
  // 右下角
  case 'zoomrb':
    body.style.cursor = "nwse-resize"
    this.startInfo = {x: this.currentShape.scope.x1, y: this.currentShape.scope.y1};
    break
}

// 绘制当前元素[详见码上掘金]
this.drawEllipseBefore(
    this.startInfo.x,
    this.startInfo.y,
    this.mouseInfo.x, // 鼠标当前坐标
    this.mouseInfo.y,
    this.currentShape.id
)

计算角度

这个需求是在圆心处和椭圆正右方点之间圆心处和鼠标点击的位置之间各画一条线段,求夹角θθ的度数,鼠标滑动时实时计算度数。

image.png

画线按前文讲的就可以了,没什么可说的。

计算角度可以用反正切函数。

image.png

对边a长度用圆心Y轴减去鼠标当前Y坐标,侧边b长度用鼠标当前X坐标减去圆心X坐标。

然后用Math.atan方法求出θ的弧度。

 // 弧度
let rad = Math.atan(Math.abs((y - ev.offsetY) / (ev.offsetX - x))) 
// 角度
let deg = rad * 180 / Math.PI

完。