ClipRect
ClipRect组件使用矩形裁剪子组件,通常情况下,ClipRect作用于CustomPaint
、 CustomSingleChildLayout
、 CustomMultiChildLayout
、 Align
、 Center
、 OverflowBox
、 SizedOverflowBox
组件,例如ClipRect作用于Align,可以仅显示上半部分(可以通过alignment控制显示的位置),代码如下:
ClipRect(
child: Align(
alignment: Alignment.topCenter,
heightFactor: 0.5,
child: Container(
height: 200,
width: 200,
child: Image.network('https://img2.baidu.com/it/u=1974206649,3691483036&fm=26&fmt=auto',fit: BoxFit.cover,),
),
),
)
clipper
参数定义裁剪规则,下面具体介绍。
clipBehavior
参数定义了裁剪的方式,只有子控件超出父控件的范围才有裁剪的说法,各个方式说明如下:
-
none:不裁剪,系统默认值,如果子组件不超出边界,此值没有任何性能消耗。
-
hardEdge:裁剪但不应用抗锯齿,速度比
none
慢一点,但比其他方式快。 -
antiAlias:裁剪而且抗锯齿,此方式看起来更平滑,比
antiAliasWithSaveLayer
快,比hardEdge
慢,通常用于处理圆形和弧形裁剪。 -
antiAliasWithSaveLayer:裁剪、抗锯齿而且有一个缓冲区,此方式很慢,用到的情况比较少。
ClipRRect
构造函数字段:
const ClipRRect({
Key key,
this.borderRadius = BorderRadius.zero,// 圆角
this.clipper, // CustomClipper 对象,如果为空,则裁剪区域为 child 指定的大小
this.clipBehavior = Clip.antiAlias,// 裁剪的方式, 不能为 null 或者 Clip.none
Widget child,
})
ClipRRect组件可以对子组件进行圆角裁剪,默认圆角半径为0,注意ClipRRect有2个R,不是上面介绍的ClipRect。
ClipRRect(
borderRadius: BorderRadius.vertical(top: Radius.circular(20),bottom: Radius.circular(100)),
child: Container(
height: 300,
width: 300,
child:Image.network('https://img2.baidu.com/it/u=1974206649,3691483036&fm=26&fmt=auto',fit: BoxFit.cover,) ,
),
)
通过一下代码可以调整任何一个交的圆角裁剪:
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
bottomLeft: Radius.circular(20),
topRight: Radius.circular(40),
bottomRight:Radius.circular(80)
),
ClipOval
ClipOval裁剪为椭圆形,椭圆形的大小为正切父组件,因此如果父组件为正方形,切出来是圆形,用法如下:
ClipOval(
child: Container(
height: 200,
width: 300,
child: _image,
),
)
ClipPath
ClipPath组件根据路径进行裁剪,我们自定义裁剪路径也可以使用系统提供的,用法如下:
ClipPath.shape(
child: Container(
height: 200,
width: 200,
child: _image,
),
shape: StadiumBorder(),
)
shape
参数是ShapeBorder类型,系统已经定义了很多形状,介绍如下:
-
RoundedRectangleBorder:圆角矩形
-
ContinuousRectangleBorder:直线和圆角平滑连续的过渡,和RoundedRectangleBorder相比,圆角效果会小一些。
-
StadiumBorder:类似于足球场的形状,两端半圆。
-
BeveledRectangleBorder:斜角矩形。
CustomClipper
CustomClipper并不是一个组件,而是一个abstract
(抽象)类,使用CustomClipper可以绘制出任何我们想要的形状,比如三角形,代码如下:
_buildClipCustom1(){
return Center(
child: ClipPath(
clipper: MyClippper(),
child: Container(
height: 300,
width: 300,
child: _image,
),
),
);
}
自定义Myclipper代码如下:
class MyClippper extends CustomClipper<Path>{
Path path =Path();
@override
Path getClip(Size size) {
Path path = Path();
// 从 60,0 开始
path.moveTo(60, 0);
// 二阶贝塞尔曲线画弧
path.quadraticBezierTo(0, 0, 0, 60);
// 连接到底部
path.lineTo(0, size.height / 1.2);
// 三阶贝塞尔曲线画弧
path.cubicTo(size.width / 4, size.height, size.width / 4 * 3, size.height / 1.5, size.width, size.height / 1.2);
// 再连接回去
path.lineTo(size.width, 60);
// 再用二阶贝塞尔曲线画弧
path.quadraticBezierTo(size.width - 60, 60, size.width - 60, 0);
return path;
}
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
return false;
// TODO: implement shouldReclip
}
}
效果如下:
绘制三角形:
class TrianglePath extends CustomClipper<Path>{
@override
Path getClip(Size size) {
var path = Path();
path.moveTo(size.width/2, 0);
path.lineTo(0, size.height);
path.lineTo(size.width, size.height);
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return true;
}
}
绘制五角星:
class StarPath extends CustomClipper<Path> {
StarPath({this.scale = 2.5});
final double scale;
double perDegree = 36;
/// 角度转弧度公式
double degree2Radian(double degree) {
return (pi * degree / 180);
}
@override
Path getClip(Size size) {
var R = min(size.width / 2, size.height / 2);
var r = R / scale;
var x = size.width / 2;
var y = size.height / 2;
var path = Path();
path.moveTo(x, y - R);
path.lineTo(x - sin(degree2Radian(perDegree)) * r,
y - cos(degree2Radian(perDegree)) * r);
path.lineTo(x - sin(degree2Radian(perDegree * 2)) * R,
y - cos(degree2Radian(perDegree * 2)) * R);
path.lineTo(x - sin(degree2Radian(perDegree * 3)) * r,
y - cos(degree2Radian(perDegree * 3)) * r);
path.lineTo(x - sin(degree2Radian(perDegree * 4)) * R,
y - cos(degree2Radian(perDegree * 4)) * R);
path.lineTo(x - sin(degree2Radian(perDegree * 5)) * r,
y - cos(degree2Radian(perDegree * 5)) * r);
path.lineTo(x - sin(degree2Radian(perDegree * 6)) * R,
y - cos(degree2Radian(perDegree * 6)) * R);
path.lineTo(x - sin(degree2Radian(perDegree * 7)) * r,
y - cos(degree2Radian(perDegree * 7)) * r);
path.lineTo(x - sin(degree2Radian(perDegree * 8)) * R,
y - cos(degree2Radian(perDegree * 8)) * R);
path.lineTo(x - sin(degree2Radian(perDegree * 9)) * r,
y - cos(degree2Radian(perDegree * 9)) * r);
path.lineTo(x - sin(degree2Radian(perDegree * 10)) * R,
y - cos(degree2Radian(perDegree * 10)) * R);
return path;
}
@override
bool shouldReclip(StarPath oldClipper) {
return oldClipper.scale != this.scale;
}
}
下面用动画动态设置scale
,代码如下:
class StartClip extends StatefulWidget {
const StartClip({Key? key}) : super(key: key);
@override
_StartClipState createState() => _StartClipState();
}
class _StartClipState extends State<StartClip> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation _animation;
@override
void initState() {
// TODO: implement initState
super.initState();
_controller = AnimationController(vsync: this,duration: Duration(seconds: 2))
..addStatusListener((status) {
if(status == AnimationStatus.completed){
_controller.reset();
}else if(status == AnimationStatus.dismissed){
_controller.forward();
}
});
_animation = Tween(begin: 1,end: 3.0).animate(_controller);
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
builder: (context,child){
return ClipPath(
clipper: StarPath(scale: _animation.value),
child: Container(
height: 150,
width: 150,
color: Colors.red,
),
);
},
animation: _animation,
),
);
}
}