闪闪发光的动画
Sparkling animation in flutter
我想在flutter中做一个闪闪发光的动画
如何在 flutter 中实现这个?
我建议使用 Lottie 动画。
如果您进行快速搜索,您会找到符合您需求的那个:
https://lottiefiles.com/search?q=star&category=animations
如果你现在找到了,点击它并按下载 -> lottie.json 然后在 flutter 中安装这个包:
https://pub.dev/packages/lottie
然后您只需将下载的 json 动画添加到您的资产文件夹中并像这样引用它:
Lottie.asset(
'assets/LottieLogo1.json',
width: 200,
height: 200,
fit: BoxFit.fill,
animate: true,
repeat: true
),
这样你就有了一个漂亮的重复动画。
您还可以使用控制器来进一步调整一切。
基本上您也可以在 After Effect 中制作动画并使用 bodymovin 插件将其导出为 json 动画
使用 https://rive.app/ 为 flutter
应用程序创建动画。你可以在rive.app
找到很多创建动画和集成到flutter app中的教程。
我建议使用自定义油漆方法。我的 awswer 是高度可定制的。我只更改 innerOuterRadiusRatio 和速度。您可以更改颜色或不透明度、星星的边数、旋转(angleOffsetToCenterStar)和光束长度。
import 'package:flutter/material.dart';
import 'dart:math' as math;
class Sparkling extends StatefulWidget {
const Sparkling({Key? key}) : super(key: key);
@override
_SparklingState createState() => _SparklingState();
}
class _SparklingState extends State<Sparkling>
with SingleTickerProviderStateMixin {
late AnimationController animationController;
late Animation animation;
late List<MyStar> myStars;
@override
void initState() {
super.initState();
myStars = <MyStar>[];
animationController = AnimationController(
vsync: this,
duration: const Duration(
milliseconds: 250,
));
animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
animationController.reverse();
} else if (status == AnimationStatus.dismissed) {
for (final star in myStars) {
star.isEnable = math.Random().nextBool();
}
animationController.forward();
}
});
animation = Tween<double>(begin: 0, end: 8).animate(CurvedAnimation(
parent: animationController, curve: Curves.easeInOutSine));
animation.addListener(() {
setState(() {});
});
animationController.forward();
}
void postFrameCallback(_) {
if (!mounted) {
return;
}
final size = MediaQuery.of(context).size;
if (myStars.isEmpty) {
myStars = List.generate(60, (index) {
double velocityX = 2 * (math.Random().nextDouble());//max velocity 2
double velocityY = 2 * (math.Random().nextDouble());
velocityX = math.Random().nextBool() ? velocityX : -velocityX;
velocityY = math.Random().nextBool() ? velocityY : -velocityY;
return MyStar(
isEnable: math.Random().nextBool(),
innerCirclePoints: 4,
beamLength: math.Random().nextDouble() * (8 - 2) + 2,
innerOuterRadiusRatio: 0.0,
angleOffsetToCenterStar: 0,
center: Offset(size.width * (math.Random().nextDouble()),
size.height * (math.Random().nextDouble())),
velocity: Offset(velocityX, velocityY),
color: Colors.white);
});
} else {
for (final star in myStars) {
star.center = star.center + star.velocity;
if (star.isEnable) {
star.innerOuterRadiusRatio = animation.value;
if (star.center.dx >= size.width) {
if (star.velocity.dy > 0) {
star.velocity = const Offset(-1, 1);
} else {
star.velocity = const Offset(-1, -1);
}
star.center = Offset(size.width, star.center.dy);
} else if (star.center.dx <= 0) {
if (star.velocity.dy > 0) {
star.velocity = const Offset(1, 1);
} else {
star.velocity = const Offset(1, -1);
}
star.center = Offset(0, star.center.dy);
} else if (star.center.dy >= size.height) {
if (star.velocity.dx > 0) {
star.velocity = const Offset(1, -1);
} else {
star.velocity = const Offset(-1, -1);
}
star.center = Offset(star.center.dx, size.height);
} else if (star.center.dy <= 0) {
if (star.velocity.dx > 0) {
star.velocity = const Offset(1, 1);
} else {
star.velocity = const Offset(-1, 1);
}
star.center = Offset(star.center.dx, 0);
}
}
}
}
}
@override
Widget build(BuildContext context) {
WidgetsBinding.instance!.addPostFrameCallback(postFrameCallback);
return CustomPaint(
size: MediaQuery.of(context).size,
painter: StarPainter(
myStars: myStars,
));
}
}
自定义画家
class StarPainter extends CustomPainter {
List<MyStar> myStars;
StarPainter({required this.myStars});
List<Map> calcStarPoints(
{required double centerX,
required double centerY,
required int innerCirclePoints,
required double innerRadius,
required double outerRadius,
required double angleOffsetToCenterStar}) {
final angle = ((math.pi) / innerCirclePoints);
final totalPoints = innerCirclePoints * 2; // 10 in a 5-points star
List<Map> points = [];
for (int i = 0; i < totalPoints; i++) {
bool isEvenIndex = i % 2 == 0;
final r = isEvenIndex ? outerRadius : innerRadius;
var currY = centerY + math.cos(i * angle + angleOffsetToCenterStar) * r;
var currX = centerX + math.sin(i * angle + angleOffsetToCenterStar) * r;
points.add({'x': currX, 'y': currY});
}
return points;
}
@override
void paint(Canvas canvas, Size size) {
for (final myStar in myStars) {
final innerRadius = myStar.beamLength / myStar.innerCirclePoints;
final outerRadius = innerRadius * myStar.innerOuterRadiusRatio;
List<Map> points = calcStarPoints(
centerX: myStar.center.dx,
centerY: myStar.center.dy,
innerCirclePoints: myStar.innerCirclePoints,
innerRadius: innerRadius,
outerRadius: outerRadius,
angleOffsetToCenterStar: myStar.angleOffsetToCenterStar);
var star = Path()..moveTo(points[0]['x'], points[0]['y']);
for (var point in points) {
star.lineTo(point['x'], point['y']);
}
canvas.drawPath(
star,
Paint()..color = myStar.color,
);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
还有 class MyStar。
class MyStar {
bool isEnable;
int innerCirclePoints; //how many edges you need?
double beamLength;
double
innerOuterRadiusRatio; // outter circle is x2 the inner // set star sharpness/chubbiness
double angleOffsetToCenterStar;
Offset center;
Offset velocity;
Color color;
MyStar(
{required this.isEnable,
required this.innerCirclePoints,
required this.beamLength,
required this.innerOuterRadiusRatio,
required this.angleOffsetToCenterStar,
required this.center,
required this.velocity,
required this.color});
}
我想在flutter中做一个闪闪发光的动画
如何在 flutter 中实现这个?
我建议使用 Lottie 动画。 如果您进行快速搜索,您会找到符合您需求的那个:
https://lottiefiles.com/search?q=star&category=animations
如果你现在找到了,点击它并按下载 -> lottie.json 然后在 flutter 中安装这个包:
https://pub.dev/packages/lottie
然后您只需将下载的 json 动画添加到您的资产文件夹中并像这样引用它:
Lottie.asset(
'assets/LottieLogo1.json',
width: 200,
height: 200,
fit: BoxFit.fill,
animate: true,
repeat: true
),
这样你就有了一个漂亮的重复动画。 您还可以使用控制器来进一步调整一切。
基本上您也可以在 After Effect 中制作动画并使用 bodymovin 插件将其导出为 json 动画
使用 https://rive.app/ 为 flutter
应用程序创建动画。你可以在rive.app
找到很多创建动画和集成到flutter app中的教程。
我建议使用自定义油漆方法。我的 awswer 是高度可定制的。我只更改 innerOuterRadiusRatio 和速度。您可以更改颜色或不透明度、星星的边数、旋转(angleOffsetToCenterStar)和光束长度。
import 'package:flutter/material.dart';
import 'dart:math' as math;
class Sparkling extends StatefulWidget {
const Sparkling({Key? key}) : super(key: key);
@override
_SparklingState createState() => _SparklingState();
}
class _SparklingState extends State<Sparkling>
with SingleTickerProviderStateMixin {
late AnimationController animationController;
late Animation animation;
late List<MyStar> myStars;
@override
void initState() {
super.initState();
myStars = <MyStar>[];
animationController = AnimationController(
vsync: this,
duration: const Duration(
milliseconds: 250,
));
animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
animationController.reverse();
} else if (status == AnimationStatus.dismissed) {
for (final star in myStars) {
star.isEnable = math.Random().nextBool();
}
animationController.forward();
}
});
animation = Tween<double>(begin: 0, end: 8).animate(CurvedAnimation(
parent: animationController, curve: Curves.easeInOutSine));
animation.addListener(() {
setState(() {});
});
animationController.forward();
}
void postFrameCallback(_) {
if (!mounted) {
return;
}
final size = MediaQuery.of(context).size;
if (myStars.isEmpty) {
myStars = List.generate(60, (index) {
double velocityX = 2 * (math.Random().nextDouble());//max velocity 2
double velocityY = 2 * (math.Random().nextDouble());
velocityX = math.Random().nextBool() ? velocityX : -velocityX;
velocityY = math.Random().nextBool() ? velocityY : -velocityY;
return MyStar(
isEnable: math.Random().nextBool(),
innerCirclePoints: 4,
beamLength: math.Random().nextDouble() * (8 - 2) + 2,
innerOuterRadiusRatio: 0.0,
angleOffsetToCenterStar: 0,
center: Offset(size.width * (math.Random().nextDouble()),
size.height * (math.Random().nextDouble())),
velocity: Offset(velocityX, velocityY),
color: Colors.white);
});
} else {
for (final star in myStars) {
star.center = star.center + star.velocity;
if (star.isEnable) {
star.innerOuterRadiusRatio = animation.value;
if (star.center.dx >= size.width) {
if (star.velocity.dy > 0) {
star.velocity = const Offset(-1, 1);
} else {
star.velocity = const Offset(-1, -1);
}
star.center = Offset(size.width, star.center.dy);
} else if (star.center.dx <= 0) {
if (star.velocity.dy > 0) {
star.velocity = const Offset(1, 1);
} else {
star.velocity = const Offset(1, -1);
}
star.center = Offset(0, star.center.dy);
} else if (star.center.dy >= size.height) {
if (star.velocity.dx > 0) {
star.velocity = const Offset(1, -1);
} else {
star.velocity = const Offset(-1, -1);
}
star.center = Offset(star.center.dx, size.height);
} else if (star.center.dy <= 0) {
if (star.velocity.dx > 0) {
star.velocity = const Offset(1, 1);
} else {
star.velocity = const Offset(-1, 1);
}
star.center = Offset(star.center.dx, 0);
}
}
}
}
}
@override
Widget build(BuildContext context) {
WidgetsBinding.instance!.addPostFrameCallback(postFrameCallback);
return CustomPaint(
size: MediaQuery.of(context).size,
painter: StarPainter(
myStars: myStars,
));
}
}
自定义画家
class StarPainter extends CustomPainter {
List<MyStar> myStars;
StarPainter({required this.myStars});
List<Map> calcStarPoints(
{required double centerX,
required double centerY,
required int innerCirclePoints,
required double innerRadius,
required double outerRadius,
required double angleOffsetToCenterStar}) {
final angle = ((math.pi) / innerCirclePoints);
final totalPoints = innerCirclePoints * 2; // 10 in a 5-points star
List<Map> points = [];
for (int i = 0; i < totalPoints; i++) {
bool isEvenIndex = i % 2 == 0;
final r = isEvenIndex ? outerRadius : innerRadius;
var currY = centerY + math.cos(i * angle + angleOffsetToCenterStar) * r;
var currX = centerX + math.sin(i * angle + angleOffsetToCenterStar) * r;
points.add({'x': currX, 'y': currY});
}
return points;
}
@override
void paint(Canvas canvas, Size size) {
for (final myStar in myStars) {
final innerRadius = myStar.beamLength / myStar.innerCirclePoints;
final outerRadius = innerRadius * myStar.innerOuterRadiusRatio;
List<Map> points = calcStarPoints(
centerX: myStar.center.dx,
centerY: myStar.center.dy,
innerCirclePoints: myStar.innerCirclePoints,
innerRadius: innerRadius,
outerRadius: outerRadius,
angleOffsetToCenterStar: myStar.angleOffsetToCenterStar);
var star = Path()..moveTo(points[0]['x'], points[0]['y']);
for (var point in points) {
star.lineTo(point['x'], point['y']);
}
canvas.drawPath(
star,
Paint()..color = myStar.color,
);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
还有 class MyStar。
class MyStar {
bool isEnable;
int innerCirclePoints; //how many edges you need?
double beamLength;
double
innerOuterRadiusRatio; // outter circle is x2 the inner // set star sharpness/chubbiness
double angleOffsetToCenterStar;
Offset center;
Offset velocity;
Color color;
MyStar(
{required this.isEnable,
required this.innerCirclePoints,
required this.beamLength,
required this.innerOuterRadiusRatio,
required this.angleOffsetToCenterStar,
required this.center,
required this.velocity,
required this.color});
}