and*_*ras 17
我知道这个问题有一个公认的答案,但我想展示这个问题的替代解决方案。
首先,从单个点创建自定义路径对于以下情况不是最佳选择:
就像在旧的 Android 中有这种路径跟踪方法一样,PathMetricsFlutter 中也存在非常相似的方法。
基于这个问题的公认答案,这里有一种更通用的方法来为任何路径设置动画。
所以给定一个路径和一个动画百分比,我们需要从开始到那个百分比提取一条路径:
Path createAnimatedPath(
Path originalPath,
double animationPercent,
) {
// ComputeMetrics can only be iterated once!
final totalLength = originalPath
.computeMetrics()
.fold(0.0, (double prev, PathMetric metric) => prev + metric.length);
final currentLength = totalLength * animationPercent;
return extractPathUntilLength(originalPath, currentLength);
}
Run Code Online (Sandbox Code Playgroud)
所以现在我只需要提取一条路径直到给定长度(而不是百分比)。我们需要组合所有现有路径直到一定距离。然后将最后一个路径段的某些部分添加到此现有路径中。
这样做非常简单。
Path extractPathUntilLength(
Path originalPath,
double length,
) {
var currentLength = 0.0;
final path = new Path();
var metricsIterator = originalPath.computeMetrics().iterator;
while (metricsIterator.moveNext()) {
var metric = metricsIterator.current;
var nextLength = currentLength + metric.length;
final isLastSegment = nextLength > length;
if (isLastSegment) {
final remainingLength = length - currentLength;
final pathSegment = metric.extractPath(0.0, remainingLength);
path.addPath(pathSegment, Offset.zero);
break;
} else {
// There might be a more efficient way of extracting an entire path
final pathSegment = metric.extractPath(0.0, metric.length);
path.addPath(pathSegment, Offset.zero);
}
currentLength = nextLength;
}
return path;
}
Run Code Online (Sandbox Code Playgroud)
整个示例所需的其余代码:
void main() => runApp(
new MaterialApp(
home: new AnimatedPathDemo(),
),
);
class AnimatedPathPainter extends CustomPainter {
final Animation<double> _animation;
AnimatedPathPainter(this._animation) : super(repaint: _animation);
Path _createAnyPath(Size size) {
return Path()
..moveTo(size.height / 4, size.height / 4)
..lineTo(size.height, size.width / 2)
..lineTo(size.height / 2, size.width)
..quadraticBezierTo(size.height / 2, 100, size.width, size.height);
}
@override
void paint(Canvas canvas, Size size) {
final animationPercent = this._animation.value;
print("Painting + ${animationPercent} - ${size}");
final path = createAnimatedPath(_createAnyPath(size), animationPercent);
final Paint paint = Paint();
paint.color = Colors.amberAccent;
paint.style = PaintingStyle.stroke;
paint.strokeWidth = 10.0;
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
class AnimatedPathDemo extends StatefulWidget {
@override
_AnimatedPathDemoState createState() => _AnimatedPathDemoState();
}
class _AnimatedPathDemoState extends State<AnimatedPathDemo>
with SingleTickerProviderStateMixin {
AnimationController _controller;
void _startAnimation() {
_controller.stop();
_controller.reset();
_controller.repeat(
period: Duration(seconds: 5),
);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: const Text('Animated Paint')),
body: SizedBox(
height: 300,
width: 300,
child: new CustomPaint(
painter: new AnimatedPathPainter(_controller),
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _startAnimation,
child: new Icon(Icons.play_arrow),
),
);
}
@override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
bio*_*arl 11
我为此创建了一个库:drawing_animation
您只需要向小部件提供 Path 对象:
导致此图像输出:imgur
import 'package:drawing_animation/drawing_animation.dart';
//...
List<Paths> dottedPathArray = ...;
bool run = true;
//...
AnimatedDrawing.paths(
this.dottedPathArray,
run: this.run,
animationOrder: PathOrders.original,
duration: new Duration(seconds: 2),
lineAnimation: LineAnimation.oneByOne,
animationCurve: Curves.linear,
onFinish: () => setState(() {
this.run = false;
}),
)),
Run Code Online (Sandbox Code Playgroud)
你实际上并不需要一个堆栈;您可以在地图图像上使用foregroundPainter。动画CustomPainter传递AnimationController到其构造函数和super构造函数。在paint使用value动画来决定绘制多少路径。例如,如果value是 0.25,则只绘制路径的前 25%。
class AnimatedPainter extends CustomPainter {
final Animation<double> _animation;
AnimatedPainter(this._animation) : super(repaint: _animation);
@override
void paint(Canvas canvas, Size size) {
// _animation.value has a value between 0.0 and 1.0
// use this to draw the first X% of the path
}
@override
bool shouldRepaint(AnimatedPainter oldDelegate) {
return true;
}
}
class PainterDemo extends StatefulWidget {
@override
PainterDemoState createState() => new PainterDemoState();
}
class PainterDemoState extends State<PainterDemo>
with SingleTickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _startAnimation() {
_controller.stop();
_controller.reset();
_controller.repeat(
period: Duration(seconds: 5),
);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: const Text('Animated Paint')),
body: new CustomPaint(
foregroundPainter: new AnimatedPainter(_controller),
child: new SizedBox(
// doesn't have to be a SizedBox - could be the Map image
width: 200.0,
height: 200.0,
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _startAnimation,
child: new Icon(Icons.play_arrow),
),
);
}
}
void main() {
runApp(
new MaterialApp(
home: new PainterDemo(),
),
);
}
Run Code Online (Sandbox Code Playgroud)
大概你会有一个定义路径的坐标列表。假设一些点列表,您将使用以下内容绘制完整路径:
if (points.isEmpty) return;
Path path = Path();
Offset origin = points[0];
path.moveTo(origin.dx, origin.dy);
for (Offset o in points) {
path.lineTo(o.dx, o.dy);
}
canvas.drawPath(
path,
Paint()
..color = Colors.orange
..style = PaintingStyle.stroke
..strokeWidth = 4.0,
);
Run Code Online (Sandbox Code Playgroud)
当value小于 1.0 时,您需要设计一种方法来绘制小于 100% 的路径。例如,当值为 0.25 时,您可能只将第一季度的点添加到路径中。如果您的路径由相对较少的点组成,则如果您计算路径的总长度并仅绘制路径的第一段,加起来等于总长度的四分之一,则您可能会获得最流畅的动画。
| 归档时间: |
|
| 查看次数: |
2567 次 |
| 最近记录: |