使用canvas封装一个绘制圆角矩形的方法

  • 代码
  • 原理
  • 思路
  • 分析
  • 讲解
  • 缺陷
  • 改进
  • 改进圆角非正规圆角的问题
  • 改进后的代码实现的圆角
  • 改进圆角过大的问题
  • 总结
  • 完整代码


在canvas中绘制一个矩形是非常简单的,使用canvas的rect()方法即可,但是没有办法绘制一个带圆角的矩形,如图:

android vector 画矩形 android canvas绘制圆角矩形_android vector 画矩形

代码

class DrawBoard extends BasicCanvas{
  constructor(c){
    super(c)
  }
  // 如何绘制带圆角的矩形
  createRect(x,y,w,h,r = 0,{width,stroke,fill}={}){
    r > 1/2*w ? r = 1/2*w : null
    let x1 = x + r, x2 = x + w - r, x3 = x + w
    let y1 = y + r, y2 = y + h -r, y3 = y + h
    this.begin()
    .start(x+1/2*w,y)
    .link(x2, y)
    .qbc(x3, y, x3, y1)
    .link(x3,y2)
    .qbc(x3, y3, x2, y3)
    .link(x1, y3)
    .qbc(x, y3, x, y2)
    .link(x, y1)
    .qbc(x, y, x1, y)
    .end()
    .lineStyle(width)
    .stroke(stroke)
    .fill(fill)
  }
}

上面的代码是基于前几期写的玩转canvas系列封装的BasicCanvas类,有兴趣的可以去看一下,现在这个类是DrawBoard类,主要是用于封装一些图形方法

原理

思路

绘制一个圆角的矩形,主要使用的是canvas中的path路径方法,以及二次贝塞尔曲线方法,主要思路是直线的部分使用lineTo方法,曲线部分使用二次贝塞尔曲线方法(下面简称称为 qbc)

分析

根据上面的思路,我们大致可以将矩形分割为这几个部分(见下图),横轴的部分主要有四个区域x,x1,x2,x3,纵轴的区域是y,y1,y2,y3。
x,y是需要我们传入的参数,主要用于定义矩形左上角在canvas画布中的位置
x1 = x + r
x2 = x + w - r
x3 = x + w
y1 = y + r
y2 = y + h - r
y3 = y + h
其中 r 是圆角的半径,w 是矩形的宽,h 是矩形的高

讲解

首先需要定义起点
起点不应定义为左上角,应定义在矩形上方的中心,也是下图标定的地方。

this.begin()
// 起点坐标(x+1/2w, y+1/2h)
.start(x+1/2*w,y)
// 然后连接到点(x2, y)
.link(x2, y)
// 使用qbc方法,控制点(x3, y)结束点(x3, y1)
.qbc(x3, y, x3, y1)
// 然后连接到点(x3,y2)
.link(x3,y2)
// 使用qbc方法,控制点(x3, y3)结束点(x2, y3)
.qbc(x3, y3, x2, y3)
// 然后连接到点(x1, y3)
.link(x1, y3)
// 使用qbc方法,控制点(x, y3)结束点(x, y2)
.qbc(x, y3, x, y2)
// 然后连接到点(x, y1)
.link(x, y1)
// 使用qbc方法,控制点(x, y)结束点(x1, y)
.qbc(x, y, x1, y)
// 最后结束 
.end()
.lineStyle(width)
.stroke(stroke)
.fill(fill)

看下图

android vector 画矩形 android canvas绘制圆角矩形_canvas_02

缺陷

这个方法其实是有一定缺陷的,也就是使用qbc方法实现的圆角并非真正的圆角,我们将其圆角设为宽度的一半的时候,这个图像展现的应该是一个圆,但实际图像并非正规的圆

android vector 画矩形 android canvas绘制圆角矩形_android vector 画矩形_03

改进

改进圆角非正规圆角的问题

将使用qbc方法制作圆角的方法改成使用arc方法来制作圆角,这样即可实现真正的圆角

this.begin()
// 开始的点还是上方中心点
.start(x+1/2*w,y)
.link(x2, y)
// 使用四分之一的圆做圆角,起始角度是3/2*pi,结束角度是0
.circular(x2, y1, r, 3/2*pi, 0, false)
.link(x3,y2)
// 使用四分之一的圆做圆角,起始角度是0,结束角度是1/2*pi
.circular(x2, y2, r, 0, 1/2*pi, false)
.link(x1, y3)
// 使用四分之一的圆做圆角,起始角度是1/2*pi,结束角度是pi
.circular(x1, y2, r, 1/2*pi, pi, false)
.link(x, y1)
// 使用四分之一的圆做圆角,起始角度是pi,结束角度是3/2*pi
.circular(x1, y1, r, pi, 3/2*pi, false)
.end()

这里使用的circlar是自己封装的绘制圆形的方法,与arc()方法传递的参数几乎相同,只是我的方法可以链式调用使用起来更加便捷。

改进后的代码实现的圆角

android vector 画矩形 android canvas绘制圆角矩形_javascript_04


android vector 画矩形 android canvas绘制圆角矩形_圆角_05


这样就是很正规的圆角了

并且由于传入的圆角半径不能超过其最短边的一半,否则就会这样

android vector 画矩形 android canvas绘制圆角矩形_javascript_06

改进圆角过大的问题

因此我们需要加上短边判断
首先判断出短边,然后判断短边的一半与圆角的大小关系

w >= h ? r > 1/2*h ? r = 1/2*h : null : r > 1/2*w ? r = 1/2*w : null

改进后

android vector 画矩形 android canvas绘制圆角矩形_贝塞尔曲线_07


这样就是正常的圆角了

总结

实现圆角矩形的方法起始有多种,有兴趣的童鞋可以自己发挥一下脑洞,只要判断好直线与圆角的绘制就可以实现了。

完整代码

createRect(
x, // 矩形左上角x坐标
y, // 矩形左上角y坐标
w, // 矩形宽
h, // 矩形高
r = 0, // 圆角半径
{width,stroke,fill}={} // 样式 边框宽度,边框颜色,填充颜色
){
  w >= h ? r > 1/2*h ? r = 1/2*h : null : r > 1/2*w ? r = 1/2*w : null
  console.log(r)
  let x1 = x + r, x2 = x + w - r, x3 = x + w
  let y1 = y + r, y2 = y + h -r, y3 = y + h
  let pi = Math.PI
  this.begin()
  .start(x+1/2*w,y)
  .link(x2, y)
  // .qbc(x3, y, x3, y1)
  .circular(x2, y1, r, 3/2*pi, 0, false)
  .link(x3,y2)
  // .qbc(x3, y3, x2, y3)
  .circular(x2, y2, r, 0, 1/2*pi, false)
  .link(x1, y3)
  // .qbc(x, y3, x, y2)
  .circular(x1, y2, r, 1/2*pi, pi, false)
  .link(x, y1)
  // // .qbc(x, y, x1, y)
  .circular(x1, y1, r, pi, 3/2*pi, false)
  .end()
  .lineStyle(width)
  .stroke(stroke)
  .fill(fill)
}