Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
Xiao CAI wants to customize a water ripple button, that is, the default outward diffusion of water wave style; There are many ways to achieve this, small dishes try the most basic AnimationController layer by layer to draw to deal with, small dishes simply record the attempt process;
ACEWaterButton
Side dish draws a simple diagram as follows, the expected water ripple button consists of two layers, based on the central circle (blue) gradually diffused to the periphery (green), and the cycle repeats;
1. The built-in circle
The first step is to draw the inner circle and the innerIcon. The inner circle provides the innerSize and innerIcon properties to customize the inner circle style. Cut a complete inner circle by ClipOval;
InnerSize = 48.0 by default. If the inner circle is not set to innerSize, the default value is innerSize * 2.
Container(
width: widget.outSize ?? widget.innerSize * 2,
height: widget.outSize ?? widget.innerSize * 2,
child: widget.innerIcon == null
? Container() : Center(child: ClipOval(
child: Container(
width: widget.innerSize,
height: widget.innerSize,
color: widget.color,
child: widget.innerIcon))))
Copy the code
2. Water ripple
There are many ways to use Animation. You can inherit the AnimatedWidget or use the AnimationController to customize the Animation style.
It is expected that the water ripple will not only expand gradually, but also decrease gradually in the diffusion process and disappear to the maximum range of the periphery. For the side dish, the most basic CustomPainter custom Canvas.drawCircle is used to draw water ripples layer by layer according to the time progress.
2.1 the transparency
Dishes. Use Paint draw according to AnimationController value. Set the color gradually withOpacity gradually become low transparency;
Paint _paint = Paint().. style = PaintingStyle.fill; _paint.. Color = color.withopacity (1.0-progress);Copy the code
2.2 peripheral circle
Outer circle was based largely on AnimationController. Updating radius value progress step by step; The expected water ripple range only gradually changes from the default inner circle to the outer circle, so the change range is (outSize – innerSize) * 0.5 * progress. Meanwhile, the starting position is the inner circle, so the initial radius should be added with the inner circle radius.
double _radius = ((outSize ?? InnerSize * 2) * 0.5 - innerSize * 0.5) * progress + innerSize * 0.5; Canvas. DrawCircle (Offset(size.width * 0.5, size.height * 0.5), _radius, _paint);Copy the code
Xiao CAI also tried to expand other scopes during the test. If the starting position is the center, there is no need to add the inner circle radius. If want to increase or effective water ripple range can freely adjust the AnimationController. Value progress;
// double _radius = innerSize * 0.5 * progress; Double _radius = innerSize * 2 * progress;Copy the code
class ACEWaterPainter extends CustomPainter { final double progress; final Color color; final double innerSize; final double outSize; Paint _paint = Paint().. style = PaintingStyle.fill; ACEWaterPainter(this.progress, this.color, this.innerSize, this.outSize); @override void paint(Canvas canvas, Size size) { _paint.. Color = color.withopacity (1.0-progress); double _radius = ((outSize ?? InnerSize * 2) * 0.5 - innerSize * 0.5) * progress + innerSize * 0.5; Canvas. DrawCircle (Offset(size.width * 0.5, size.height * 0.5), _radius, _paint); } @override bool shouldRepaint(CustomPainter oldDelegate) => true; }Copy the code
3. The little reflection
3.1 Can built-in circles be default?
In the process of drawing water ripples with ACEWaterPainter, the starting position starts from the inner circle, can we omit the inner circle in the first step?
We don’t want to default the innerIcon at the moment, because the opacity gradient is set in the process of water ripple diffusion. If we default the inner circle, the innerIcon will be affected. However, the position of the inner circle can be adjusted and can also be drawn in ACEWaterPainter.
3.2 Should shouldRepaint always be repainted?
Whether the ACEWaterPainter needs to be repainted all the time; When creating a new CustomPaint object using the CustomPaint delegate class, return true if the new instance is different from the old one, false otherwise. Therefore, during the water ripple process, the side dish defaults to True for redrawing;
ACEWaterButton case source
The simple effect of the side dish on the ACEWaterButton water ripple button has been satisfied, but it is not perfect enough. The redrawing mechanism needs to be optimized. If there are mistakes, please give more guidance!
Source: Little Monk A Ce