I am building a CustomPaint heart shape that has animation on the size. An optimal state is the cirle GIF below. However, what i have been able to achieve so far is the heart GIF below.
Here is the code for my CustomPaint:
class MyPainter extends CustomPainter {
// The color of the heart
final Color color;
///[double] minimum radius of the painter
final double minRadius;
///[int] number of wave count in the animation
final int wavesCount;
///[Color] of the painter
final Animation<double>? _animation;
MyPainter(
this.color,
this.minRadius,
this.wavesCount,
this._animation,
) : super(repaint: _animation);
@override
void paint(Canvas canvas, Size size) {
for (int wave = 0; wave <= wavesCount; wave++) {
heart(
canvas,
size,
minRadius,
wave,
_animation!.value,
wavesCount,
color,
);
}
}
void heart(
Canvas canvas,
Size size,
double minRadius,
int wave,
double anim,
int? length,
Color heartColor,
) {
Color color = heartColor;
if (wave != 0) {
final double opacity =
(1 - ((wave - 1) / length!) - anim).clamp(0.0, 1.0);
color = color.withOpacity(opacity);
final Paint body = Paint();
body
..color = color
..style = PaintingStyle.fill
..strokeWidth = 0;
final double width = size.width * (1 + (wave * anim)) * anim;
final double height = size.height * (1 + (wave * anim)) * anim;
final Path path = Path();
path.moveTo(0.5 * width, height * 0.4);
path.cubicTo(0.2 * width, height * 0.1, -0.25 * width, height * 0.6,
0.5 * width, height);
path.moveTo(0.5 * width, height * 0.4);
path.cubicTo(0.8 * width, height * 0.1, 1.25 * width, height * 0.6,
0.5 * width, height);
canvas.drawPath(path, body);
}
}
@override
bool shouldRepaint(MyPainter oldDelegate) {
return true;
}
}
The CustomPaint class is used like so:
class MyRippleState extends State<MyRipple> with TickerProviderStateMixin {
AnimationController? _controller;
@override
void initState() {
_controller = AnimationController(
duration: widget.duration,
vsync: this,
);
// repeating or just forwarding the animation once.
Timer(widget.delay, () {
widget.repeat ? _controller?.repeat() : _controller?.forward();
});
super.initState();
}
@override
Widget build(BuildContext context) {
return CustomPaint(
size: const Size(150, 150),
painter: MyPainter(
vybPrimary,
widget.minRadius,
widget.ripplesCount + 2,
_controller,
),
);
}
@override
void dispose() {
_controller!.dispose();
super.dispose();
}
}
Any help/pointers to assist me in centering the heart shape in the middle of the page just like in the circle?
Replace your path with this:
final Path path = Path();
final startX =(size.width/2) - (width/2);
final startY =(size.height/2) - height * 0.6;
path.moveTo(startX + (0.5 * width), startY + (height * 0.4));
path.cubicTo(startX + (0.2 * width), startY + (height * 0.1), startX +(-0.25 * width), startY + (height * 0.6),
startX + (0.5 * width), startY + height);
path.moveTo(startX + (0.5 * width), startY + (height * 0.4));
path.cubicTo(startX + (0.8 * width), startY + (height * 0.1), startX + (1.25 * width), startY + (height * 0.6),
startX + (0.5 * width), startY + height);
canvas.drawPath(path, body);