前言:
咋一看,什麼是路徑裁剪,這是什麼鬼?路徑怎麼裁剪,請看下面示例圖片,是不是一目瞭然,左圖按照一定的路徑對紅色區域進行裁剪,最後變成右邊圖的樣子,有人說,這有什麼用,我們先不急,先來完成下面這種效果,然後再進行其他效果實現。
一:基本路徑裁剪
1,建立工程,main.dart程式碼如下
import 'package:flutter/material.dart';
import 'package:flutter_animation/animation/MyAnimation.dart';
import 'package:flutter_animation/clippath/MyClipPath.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyClipPath(),
);
}
}複製程式碼
2,建立MyClipPath元件類
import 'package:flutter/material.dart';
///create by:Administrator
///create at:2019-09-19 20:00
///des:
class MyClipPath extends StatefulWidget {
@override
_MyClipPathState createState() => _MyClipPathState();
}
class _MyClipPathState extends State<MyClipPath> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.blue,
child: Column(
children: <Widget>[
ClipPath(
clipper: LineClipOrign(),
child: Container(
width: double.infinity,
height: 300.0,
color: Colors.red,
),
),
],
),
),
);
}
}
class LineClip extends CustomClipper<Path>{
@override
Path getClip(Size size) {
var path = Path()
..moveTo(0.0, 0.0)
..lineTo(0.0, size.height)
..lineTo(size.width/2, size.height/2)
..lineTo(size.width, size.height)
..lineTo(size.width, 0.0)
..close();
return path;
}
//是否重新裁剪
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return true;
}
}複製程式碼
執行之後就是以上圖片展示效果
3,程式碼分析
裁剪功能歸功於ClipPath這個元件,你想裁剪哪個區域就在哪個區域外面包裹一層ClipPath。ClipPath有個路徑屬性clipper,它規定我們按照什麼規則去裁剪,需要一個CustomerClipper物件。為此,我們建立了一個名為LineClip的類繼承自CustomerClipper,來實現我們自己的裁剪規則,我們重寫了兩個方法,主要看getClip方法,該方法有個引數size,它代表我們裁剪區域的大小,裁剪區域寬高分別為size.with,size.height。
接下來我們看方法內部,建立了Path物件,它就是我們裁剪的路徑具體規則。大家都畫過圓吧,從起點到終點形成一個閉環。那麼我們如何畫上圖所示效果呢?我們把path物件想象成一個畫筆,把畫筆移動到(0.0, 0.0),開始畫線,所以使用lineTo()方法,方法引數就是第一條線的終點,然後下一條線的起點就是上一條線的終點,以此類推,最後通過方法close()結束我們的路徑,代表我們畫完了,是不是很好理解。
二:實現 '我的' 介面常見效果
效果圖:
1,直接上程式碼,main.dart
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyClipPath2(),
);
}
}複製程式碼
2,MyClipPath2.dart
import 'package:flutter/material.dart';
///create by:Administrator
///create at:2019-09-19 21:33
///des:
class MyClipPath2 extends StatefulWidget {
@override
_MyClipPath2State createState() => _MyClipPath2State();
}
class _MyClipPath2State extends State<MyClipPath2> {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: ClipPath(
child: Stack(
fit: StackFit.expand,
children: [
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
//漸變色
colors: [Colors.red, Colors.blue, Colors.amberAccent],
begin: Alignment.centerRight,
end: Alignment(-1.0, -1.0))),
),
Column(
children: [
Padding(
padding: EdgeInsets.only(top: 50.0),
child: Container(
width: 100.0,
height: 100.0,
decoration: new BoxDecoration(
border: Border.all(color: Colors.yellow, width: 1.0),
color: const Color(0xFFFFFFFF), // border color
shape: BoxShape.circle,
image: DecorationImage(
image: AssetImage("images/image2.jpeg"))))),
],
),
],
),
clipper: MyHeader(),
));
}
}
class MyHeader extends CustomClipper<Path> {
@override
Path getClip(Size size) {
var path = Path()
..lineTo(0.0, size.height / 2 + 50)
..lineTo(size.width, size.height / 2 - 80)
..lineTo(size.width, 0)
..close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
複製程式碼
三:實現曲線裁剪
效果圖:
1,直接上程式碼,main.dart
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyClipPath3(),
);
}
}複製程式碼
2,MyClipPath3.dart
import 'package:flutter/material.dart';
///create by:Administrator
///create at:2019-09-19 21:33
///des:
class MyClipPath3 extends StatefulWidget {
@override
_MyClipPath3State createState() => _MyClipPath3State();
}
class _MyClipPath3State extends State<MyClipPath3> {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: Container(
child: Column(
children: <Widget>[
ClipPath(
clipper: BottonClipper(),
child: Container(
width: double.infinity,
height: 300.0,
child: Stack(
fit: StackFit.expand,
children: [
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
//漸變色
colors: [Colors.red, Colors.blue, Colors.amberAccent],
begin: Alignment.centerRight,
end: Alignment(-1.0, -1.0))),
),
Column(
children: [
Padding(
padding: EdgeInsets.only(top: 50.0),
child: Container(
width: 100.0,
height: 100.0,
decoration: new BoxDecoration(
border: Border.all(color: Colors.yellow, width: 1.0),
shape: BoxShape.circle,
image: DecorationImage(
image: AssetImage("images/image2.jpeg"))))),
Padding(
padding: EdgeInsets.fromLTRB(0.0, 20, 0.0, 0.0),
child: Text("點我登入",style: TextStyle(color: Colors.white,fontSize: 20.0),),
),
],
),
],
),
),
),
],
),
),
);
}
}
class BottonClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
// 路徑
Path path = Path();
//曲度 值越大,曲線越陡峭
var curvature=50;
// 設定路徑的開始點
path.lineTo(0, 0);
path.lineTo(0, size.height-curvature);
// 設定曲線的開始樣式
var firstControlPoint = Offset(size.width / 2, size.height);
// 設定曲線的結束樣式
var firstEndPont = Offset(size.width, size.height - curvature);
// 把設定的曲線新增到路徑裡面
path.quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy,
firstEndPont.dx, firstEndPont.dy);
// 設定路徑的結束點
path.lineTo(size.width, size.height-curvature);
path.lineTo(size.width, 0);
// 返回路徑
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return false;
}
}
複製程式碼
3,說明
裁剪規則在程式碼註釋裡面很詳細,讀者可以自己執行起來看看效果,然後修改裁剪規則看看有什麼效果。
四:結束語
裁剪規則多種多樣,這裡只列舉了簡單的三種,其他的讀者可以自行嘗試。
案例中如有錯誤,煩請指正,不勝感激!