有什么方法可以实现在边缘上弯曲移动线吗?在 Flutter 中实现

问题描述 投票:0回答:1

我现在的代码:

class RoundedRectanglePainter extends CustomPainter {
  final Color strokeColorGradientStart;
  final Color strokeColorGradientEnd;
  final double strokeWidth;
  final double borderRadius;
  final Color fillColorGradientStart;
  final Color fillColorGradientEnd;
  final Animation<double> animation;

  RoundedRectanglePainter({
    required this.strokeColorGradientStart,
    required this.strokeColorGradientEnd,
    required this.strokeWidth,
    required this.borderRadius,
    required this.fillColorGradientStart,
    required this.fillColorGradientEnd,
    required this.animation,
  }) : super(repaint: animation);

  @override
  void paint(Canvas canvas, Size size) {
    final strokeGradient = LinearGradient(
      colors: [strokeColorGradientStart, strokeColorGradientEnd],
      begin: Alignment.topLeft,
      end: Alignment.bottomRight,
    );

    final fillGradient = LinearGradient(
      colors: [fillColorGradientStart, fillColorGradientEnd],
      begin: Alignment.topLeft,
      end: Alignment.bottomRight,
    );

    final strokePaint = Paint()
      ..shader = strokeGradient
          .createShader(Rect.fromLTWH(0, 0, size.width, size.height))
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.stroke;

    final fillPaint = Paint()
      ..shader = fillGradient
          .createShader(Rect.fromLTWH(0, 0, size.width, size.height))
      ..style = PaintingStyle.fill;

    final outerRect = Rect.fromLTWH(0, 0, size.width, size.height);
    final outerRRect =
        RRect.fromRectAndRadius(outerRect, Radius.circular(borderRadius));
    canvas.drawRRect(outerRRect, strokePaint);

    final innerWidth = size.width - (strokeWidth * 2) - 10;
    final innerHeight = size.height - (strokeWidth * 2) - 10;
    final innerRect = Rect.fromLTWH((size.width - innerWidth) / 2,
        (size.height - innerHeight) / 2, innerWidth, innerHeight);
    final innerRRect =
        RRect.fromRectAndRadius(innerRect, Radius.circular(borderRadius));

    canvas.drawRRect(innerRRect, fillPaint);

    Path outerPath = Path()..addRRect(outerRRect);
    PathMetrics pathMetrics = outerPath.computeMetrics();
    PathMetric pathMetric = pathMetrics.first;

    double currentLength = pathMetric.length * animation.value;
    Tangent? tangent = pathMetric.getTangentForOffset(currentLength);

    if (tangent != null) {
      final movingLinePaint = Paint()
        ..color = Colors.red
        ..strokeWidth = 10.0
        ..strokeCap = StrokeCap.round
        ..style = PaintingStyle.stroke;

      final lineStart = tangent.position;
      final lineDirection = tangent.vector;

      final lineEnd = lineStart +
          Offset(
            lineDirection.dx * 30,
            lineDirection.dy * 30,
          );

       canvas.drawLine(lineStart, lineEnd, movingLinePaint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

我希望移动的线应遵循定义外部矩形的确切路径。我尝试并测试了不同的东西,但无法获得所需的行为。任何人都可以建议我如何进一步实现所需的功能或行为或一些相关的代码片段,这可以帮助我在代码中取得进展。

在图像中你可以看到,当它到达弯曲路径时,移动的线并不遵循曲线,它只是向右转。这就是问题所在。 (https://i.sstatic.net/pQxVTVfg.jpg)

flutter widget flutter-animation custom-painter
1个回答
0
投票

请尝试此代码

import 'package:flutter/material.dart';
import 'dart:ui';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: AnimatedPathScreen(),
    );
  }
}

class AnimatedPathScreen extends StatefulWidget {
  @override
  _AnimatedPathScreenState createState() => _AnimatedPathScreenState();
}

class _AnimatedPathScreenState extends State<AnimatedPathScreen>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: const Duration(seconds: 5),
      vsync: this,
    )..repeat(reverse: false);

    _animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Animated Path'),
      ),
      body: Center(
        child: CustomPaint(
          size: Size(300, 300),
          painter: AnimatedPathPainter(animation: _animation),
        ),
      ),
    );
  }
}

class AnimatedPathPainter extends CustomPainter {
  final Animation<double> animation;
  final double lineLength = 50.0; 

  AnimatedPathPainter({required this.animation}) : super(repaint: animation);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.red
      ..strokeWidth = 4.0 
      ..style = PaintingStyle.stroke;

    final path = Path();
    final rect = Rect.fromLTWH(20, 20, size.width - 40, size.height - 40);
    path.addRRect(RRect.fromRectAndRadius(rect, Radius.circular(20)));

    final pathMetrics = path.computeMetrics().toList();
    final totalLength = pathMetrics.fold<double>(
      0.0,
      (double prev, PathMetric metric) => prev + metric.length,
    );

    double currentLength = (animation.value * totalLength) % totalLength;
    double startLength = currentLength;
    double endLength = (currentLength + lineLength) % totalLength;

    final pathSegment = Path();

    for (PathMetric metric in pathMetrics) {
      if (startLength < metric.length) {
        if (startLength < endLength) {
          pathSegment.addPath(metric.extractPath(startLength, endLength), Offset.zero);
        } else {
          pathSegment.addPath(metric.extractPath(startLength, metric.length), Offset.zero);
          pathSegment.addPath(metric.extractPath(0, endLength), Offset.zero);
        }
        break;
      } else {
        startLength -= metric.length;
        endLength -= metric.length;
      }
    }

    canvas.drawPath(pathSegment, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
© www.soinside.com 2019 - 2024. All rights reserved.