Flutter中使用canvas绘制贝塞尔曲线
贝塞尔曲线简介
贝塞尔曲线(Bézier curve)是一种通过给定的控制点来定义的数学曲线,广泛应用于计算机图形学、动画、几何建模等领域。贝塞尔曲线由法国工程师皮埃尔·贝塞尔(Pierre Bézier)在20世纪60年代为汽车设计而开发,后来被广泛应用于计算机图形学中。
在计算机图形学中,贝塞尔曲线通常用于绘制平滑的曲线和形状,如字体设计、动画路径、3D建模等。此外,贝塞尔曲线也是许多图形软件(如Adobe Illustrator、AutoCAD等)中常用的绘图工具。贝塞尔曲线有多种类型,包括:
-
二阶贝塞尔曲线:由三个控制点定义,形状类似于抛物线。
-
三阶贝塞尔曲线:由四个控制点定义,可以创建更复杂的形状。
-
高阶贝塞尔曲线:由更多控制点定义,可以创建更复杂的曲线。
如下图中选中曲线可以理解为二阶贝塞尔曲线,1、3两点为端点,1、3端点之间的2称为控制点如下图中选中曲线可以理解为三阶贝塞尔曲线,1、4两点为端点,1、4端点之间的2、3称为控制点
到这儿其实就可以知道计算机绘图中,大量使用了贝塞尔曲线。
一些与贝塞尔曲线有关的网站:
在线钢笔路径绘制:bezier.method.ac/
贝塞尔曲线-数学表达式:www.desmos.com/calculator/…
小结:贝塞尔曲线在计算机图形领域经常使用到,贝塞尔曲线可由数学表达式表达
Flutter中绘制
在UI开发中经常会遇到一些,使用常规内置基础控件实现不了的设计图,或者一些控件自定义度不够高,那么就可以考虑使用canvas利用贝塞尔曲线手动绘制形状。
例如上图中的底部导航栏中间有一个圆形凹槽,并且凹槽顶部两角为圆滑过度,使用常规的ClipPath路径剪裁并不好处理
值得一提的是canvas绘制的原点是父布局的左上角,y轴向下为正数值,如下图所示
在sketch、figma等绘图软件中的有标尺,其实就是绘制的x,y轴,原点同样为左上角x轴向右为正,y轴向下为正
双击路径选中点,就可看到每个点的x,y坐标,就可以使用canvas开始绘制了。在flutter使用CustomPaint控件,在CustomPainter中paint实现使用canvas具体绘制逻辑,其中常用的API有path.moveTo()移动画笔到某点,lineTo()连接到某点,quadraticBezierTo()二阶贝塞尔,cubicTo()三阶贝塞尔
二阶贝塞尔曲线(Quadratic Bézier Curve)
quadraticBezierTo() 方法用于绘制二阶贝塞尔曲线。它需要两个参数:控制点的坐标和结束点的坐标。二阶贝塞尔曲线由起始点、一个控制点和结束点组成。
void quadraticBezierTo(
double x1, // 控制点的x坐标
double y1, // 控制点的y坐标
double x2, // 结束点的x坐标
double y2, // 结束点的y坐标
)
三阶贝塞尔曲线(Cubic Bézier Curve)
cubicTo() 方法用于绘制三阶贝塞尔曲线。它需要四个参数:第一个控制点的坐标和第二个控制点的坐标,以及结束点的坐标。三阶贝塞尔曲线由起始点、两个控制点和结束点组成。
void cubicTo(
double x1, // 第一个控制点的x坐标
double y1, // 第一个控制点的y坐标
double x2, // 第二个控制点的x坐标
double y2, // 第二个控制点的y坐标
double x3, // 结束点的x坐标
double y3, // 结束点的y坐标
)
下面以带凹槽的导航栏为例,在Flutter中使用CustomPaint以及使用canvas自定义绘制实现
CustomPaint(
size: Size(375.w, 84.5.h),
painter: NavPainter(),
//foregroundPainter: NavPainter(),
),
class NavPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = Colors.white
..style = PaintingStyle.fill // 填充样式
..isAntiAlias = true; // 开启抗锯齿
var path = Path();
path.moveTo(0, 84.5.h);
path.lineTo(0, 31.5.h);
path.cubicTo(0, 14.38.h, 13.88.w, 0.5.h, 31.w, 0.5.h);
path.lineTo(139.w, 0.5.h);
path.quadraticBezierTo(149.5.w, 0.5.h, 149.5.w, 11.h);
path.cubicTo(149.5.w, 31.99.h, 166.6.w, 49.h, 187.5.w, 49.h);
path.cubicTo(208.4.w, 49.h, 225.5.w, 31.99.h, 225.5.w, 11.h);
path.quadraticBezierTo(225.5.w, 0.5.h, 236.w, 0.5.h);
path.lineTo(344.w, 0.5.h);
path.cubicTo(361.12.w, 0.5.h, 375.w, 14.38.h, 375.w, 31.5.h);
path.lineTo(375.w, 84.5.h);
path.lineTo(0, 84.5.h);
// 创建阴影用的Paint,设置阴影颜色和透明度
final shadowPaint = Paint()
..color = const Color(0xFF000000).withOpacity(0.12)
..style = PaintingStyle.fill
..isAntiAlias = true
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 8); // 设置阴影的模糊度
canvas.drawPath(path, shadowPaint);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
在真机上的效果
小结:使用canvas结合贝塞尔曲线可以绘制出和UI还原度较高的图形,若绘制包含大量节点和曲线的复杂路径可能会消耗更多的资源。
转载自:https://juejin.cn/post/7368421971385040908