当前位置:网站首页>Draw a wave ball with the curve of flutter and Bessel
Draw a wave ball with the curve of flutter and Bessel
2022-07-23 07:33:00 【biyezuopinvip】
When flutter The existing components of cannot meet the requirements of the product UI In effect , We need to implement it through self drawing components . This article will introduce how to use flutter Custom implementation of a wave ball with text , The effect is as follows :

Let's summarize WaveLoadingWidget Characteristics , In this way, the steps required to achieve this effect can be summarized :
- widget The main body of is an irregular semicircle , The top curve moves up and down from left to right in a wave like form
- The wave ball can customize the color , Here to waveColor name
- The undulating line of the wave ball divides the embedded text into upper and lower colors , The color of the upper part is in backgroundColor name , The color of the lower part is in foregroundColor name , The overall color of the text has been changing dynamically according to the operation of the waves
Although the overall color of the text is constantly changing , But as long as you can draw one of the frames , The dynamic effect can be realized by changing the position parameters of the wave curve , So here's the widget As static , First realize its static effect
Disassemble the drawing steps into the following steps :
- The drawing color is backgroundColor The text of , Draw it on canvas The bottom of the
- according to widget The width and height information constructs a maximum circular path within the range circlePath
- With circlePath The horizontal middle line of the wave is used as the reference fluctuation line of the wave , Draw a continuous wave with Bessel curve on the upper and lower sides of the fluctuation line path, take path The ends of the are connected together in a rectangular way , constitute wavePath,wavePath The bottom of the will circlePath Intersect at the bottom of
- take circlePath and wavePath Intersection combinePath, use waveColor fill , At this point, you get a semicircular spherical wave
- utilize
canvas.clipPath(combinePath)Method to cut the canvas , Then draw the color as foregroundColor The text of , Drawn at this time foregroundColor The text will only show combinePath Part of the scope , That is, only the lower part will be displayed , Make the text drawn at two different times overlap , Thus, text with different color ranges is obtained - utilize AnimationController Constantly changing wavePath The starting point of X coordinate , At the same time, refresh UI, The undulating motion from left to right
Now let's implement the above drawing steps step by step
One 、 draw backgroundColor Text
flutter adopt CustomPainter It provides self drawing for developers UI Entrance , Inside void paint(Canvas canvas, Size size) Method provides a canvas canvas Object and contains widget Wide and high information size object
Here comes the inheritance CustomPainter class , stay paint Method, first draw the color as backgroundColor The text of .flutter Of canvas Object does not provide a direct drawText Of API, Therefore, the steps of drawing text are relatively native customization View A little more trouble
class _WaveLoadingPainter extends CustomPainter {
final String text;
final double fontSize;
final double animatedValue;
final Color backgroundColor;
final Color foregroundColor;
final Color waveColor;
_WaveLoadingPainter({
required this.text,
required this.fontSize,
required this.animatedValue,
required this.backgroundColor,
required this.foregroundColor,
required this.waveColor,
});
@override
void paint(Canvas canvas, Size size) {
final side = min(size.width, size.height);
_drawText(canvas: canvas, side: side, color: backgroundColor);
}
void _drawText(
{required Canvas canvas, required double side, required Color color}) {
ParagraphBuilder paragraphBuilder = ParagraphBuilder(ParagraphStyle(
textAlign: TextAlign.center,
fontStyle: FontStyle.normal,
fontSize: fontSize,
));
paragraphBuilder.pushStyle(ui.TextStyle(color: color));
paragraphBuilder.addText(text);
ParagraphConstraints pc = ParagraphConstraints(width: fontSize);
Paragraph paragraph = paragraphBuilder.build()..layout(pc);
canvas.drawParagraph(
paragraph,
Offset((side - paragraph.width) / 2.0, (side - paragraph.height) / 2.0),
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return animatedValue != (oldDelegate as _WaveLoadingPainter).animatedValue;
}
}

Two 、 structure circlePath
take widget The minimum value of width and height is taken as the diameter of the circle , In order to build a system that does not exceed widget The maximum circular path of the range circlePath
@override
void paint(Canvas canvas, Size size) {
final side = min(size.width, size.height);
_drawText(canvas: canvas, side: side, color: backgroundColor);
final circlePath = Path();
circlePath.addArc(Rect.fromLTWH(0, 0, side, side), 0, 2 * pi);
}
3、 ... and 、 Draw wave lines
The width and height of the wave are evaluated according to a fixed proportional value , With circlePath The middle separation line of is used as the horizontal line , Draw a continuous wave line up and down the horizontal line according to the Bessel curve
@override
void paint(Canvas canvas, Size size) {
final side = min(size.width, size.height);
_drawText(canvas: canvas, side: side, color: backgroundColor);
final circlePath = Path();
circlePath.addArc(Rect.fromLTWH(0, 0, side, side), 0, 2 * pi);
final waveWidth = side * 0.8;
final waveHeight = side / 6;
final wavePath = Path();
final radius = side / 2.0;
wavePath.moveTo(-waveWidth, radius);
for (double i = -waveWidth; i < side; i += waveWidth) {
wavePath.relativeQuadraticBezierTo(
waveWidth / 4, -waveHeight, waveWidth / 2, 0);
wavePath.relativeQuadraticBezierTo(
waveWidth / 4, waveHeight, waveWidth / 2, 0);
}
// For the convenience of the reader , Here is the wavePath Draw it out , There is no need for
final paint = Paint()
..isAntiAlias = true
..style = PaintingStyle.fill
..strokeWidth = 3
..color = waveColor;
canvas.drawPath(wavePath, paint);
}

At this time, the drawn curve is still in an open state , Need to put wavePath Connect the ends of the , In this way, we can communicate with circlePath intersect
wavePath.relativeLineTo(0, radius);
wavePath.lineTo(-waveWidth, side);
wavePath.close();
// For the convenience of the reader , Here is the wavePath Draw it out , There is no need for
final paint = Paint()
..isAntiAlias = true
..style = PaintingStyle.fill
..strokeWidth = 3
..color = waveColor;
canvas.drawPath(wavePath, paint);
wavePath After closing , At this time, the color of the semicircle will be covered

Four 、 intersect
take circlePath and wavePath Intersection , You get a semicircular wave ball
final paint = Paint()
..isAntiAlias = true
..style = PaintingStyle.fill
..strokeWidth = 3
..color = waveColor;
final combinePath = Path.combine(PathOperation.intersect, circlePath, wavePath);
canvas.drawPath(combinePath, paint);

5、 ... and 、 draw foregroundColor Text
The color of the text is divided into two parts , The color of the upper part is backgroundColor, The second half is foregroundColor. In the first step, the color has been drawn as backgroundColor The text of ,foregroundColor The text does not need to show the top half , So I'm drawing foregroundColor Before text, you need to limit the drawing area to combinePath Inside , Make the text drawn at two different times overlap , To get text with different color ranges
canvas.clipPath(combinePath);
_drawText(canvas: canvas, side: side, color: foregroundColor);

6、 ... and 、 Add animation
Now you have drawn the effect of static time , Consider how to make widget It's moving
It's also easy to achieve dynamic effects , Just keep changing the coordinates of the starting point of the Bezier curve , Keep it moving from left to right , You can create the effect of waves moving forward from left to right ._WaveLoadingPainter According to the animation value passed in from the outside animatedValue To set up wavePath The starting coordinate point of , Generate animatedValue The logic and other drawing parameters are determined by _WaveLoadingState To provide
class _WaveLoadingState extends State<WaveLoading>
with SingleTickerProviderStateMixin {
String get _text => widget.text;
double get _fontSize => widget.fontSize;
Color get _backgroundColor => widget.backgroundColor;
Color get _foregroundColor => widget.foregroundColor;
Color get _waveColor => widget.waveColor;
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 700), vsync: this);
_animation = Tween(
begin: 0.0,
end: 1.0,
).animate(_controller)
..addListener(() {
setState(() => {});
});
_controller.repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: CustomPaint(
painter: _WaveLoadingPainter(
text: _text,
fontSize: _fontSize,
animatedValue: _animation.value,
backgroundColor: _backgroundColor,
foregroundColor: _foregroundColor,
waveColor: _waveColor,
),
),
);
}
}
_WaveLoadingPainter according to animatedValue To set up wavePath The starting coordinate point of
wavePath.moveTo((animatedValue - 1) * waveWidth, radius);
7、 ... and 、 Use
The final will be _WaveLoadingState Package to StatefulWidget in , stay StatefulWidget Just open the parameters that can be customized in
class WaveLoading extends StatefulWidget {
final String text;
final double fontSize;
final Color backgroundColor;
final Color foregroundColor;
final Color waveColor;
WaveLoading({
Key? key,
required this.text,
required this.fontSize,
required this.backgroundColor,
required this.foregroundColor,
required this.waveColor,
}) : super(key: key) {
assert(text.isNotEmpty && fontSize > 0);
}
@override
State<StatefulWidget> createState() {
return _WaveLoadingState();
}
}
Usage mode :
SizedBox(
width: 300,
height: 300,
child: WaveLoading(
text: " Open ",
fontSize: 210,
backgroundColor: Colors.lightBlue,
foregroundColor: Colors.white,
waveColor: Colors.lightBlue,
)
边栏推荐
- Excel displays the link URL of the picture as picture to
- Stm32cubeide link script explanation
- 能量原理与变分法笔记11:形函数(一种降维思想)
- How to make a high-quality VR panorama? Are there any simple ones that can be taken?
- Inside the hard core of LAN SDN technology - 15 MPLS implementation of user roaming in the three from thing to person Park
- Redis common basic configuration files
- VR全景动物园,成就不一样的动物园名片
- 低代码服务商ClickPaaS与毕普科技完成战略合并,共同打造工业数字化底座
- Digital collections start the 100 billion level market
- NFT Insider #67:巴塞罗那足球俱乐部推出首个NFT作品,迪拜推出国家元宇宙战略
猜你喜欢

7、学习MySQL 选择数据库

Overview of the development of pseudo defense in cyberspace: from pseudo concept to "pseudo +" ecology

编写一个具有搜索提示的搜索框

每日刷题记录 (三十一)

rs485通信OSI模型网络层

Codeforces Round #809 (Div. 2) A - D1

Ambire Gas Tank 推出独家 NFT 投放

小程序毕设作品之微信校园二手书交易小程序毕业设计成品(5)任务书

Talk about repaintboundary in fluent

-bash: wget: 未找到命令
随机推荐
redis常用基础配置文件
小程序毕设作品之微信校园二手书交易小程序毕业设计成品(1)开发概要
【ARC127F】±AB
Attack and defense scheme based on Ethereum status database
支持多数不规则用户的隐私保护联邦学习框架
便利贴--46{HbuildX连接夜神模拟器}
基于网络防御知识图谱的0day攻击路径预测方法
[FAQ] common reasons and solutions for the failure of in app payment services to pull up the payment page
gnu 伪指令定义函数
千亿营收之后,阿里云生态有了新打法
聪明人的游戏提高篇:第三章第三课例题:素数的秘密(prime)
LAN SDN technology hard core insider - what's in the prequel CPU?
Flutter内存泄漏检测
Nftscan and ATEM network have reached strategic cooperation in the field of NFT data
String in SQL Server_ Implementation of split function
GNU LD script command language (I)
低代码服务商ClickPaaS与毕普科技完成战略合并,共同打造工业数字化底座
Redis——JedisConnectionException Could not get a resource from the pool
初识Flutter中的Layer
GNU LD script command language (II)