Flutter - 自定义计时器,带有路径和图标的自定义图像

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

所以,我想展示一个计时器。下面是我想要实现的简单设计。随着时间逐渐减少,汽车应该绕赛道行驶。我目前不知道如何实施它。还有一个问题是,汽车绕圈行驶时,车面应该旋转。如果有人过去做过类似的事情,请告诉我。任何指南/教程将不胜感激。 enter image description here

flutter flutter-dependencies flutter-animation
1个回答
0
投票

添加包:path_drawing

并尝试运行此代码。目前,我使用计时器来更新汽车的位置,您可以根据您的要求进行操作。

汽车图像已添加到资产文件夹中。 本例中使用的图像

import 'dart:async';
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_drawing/path_drawing.dart' as pd;

class CustomRoadWithCar extends StatefulWidget {
  const CustomRoadWithCar({super.key});

  @override
  State<CustomRoadWithCar> createState() => _CustomRoadWithCarState();
}

class _CustomRoadWithCarState extends State<CustomRoadWithCar> {
  Timer? _timer;
  int _elapsedSeconds = 0;

  // Variable to control the movement of the car and path fill
  double _carProgress = 0.0;

  ui.Image? image;

  @override
  void initState() {
    super.initState();
    loadImageFromAsset('assets/car.png');
    startTimer();
  }

  void startTimer() {
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        _elapsedSeconds++;
        // Calculate the progress based on elapsed seconds
        _carProgress = _elapsedSeconds / 59.0;

        // Check if the elapsed time has reached 59 seconds
        if (_elapsedSeconds >= 59) {
          // Stop the timer
          _timer?.cancel();
        }
      });
    });
  }

  @override
  void dispose() {
    // Cancel the timer when the widget is disposed
    _timer?.cancel();
    super.dispose();
  }

  // Load an image from assets and convert it to a ui.Image object
  Future<void> loadImageFromAsset(String path) async {
    final ByteData data = await rootBundle.load(path);
    final Uint8List bytes = data.buffer.asUint8List();
    final ui.Codec codec = await ui.instantiateImageCodec(bytes);
    final ui.FrameInfo frameInfo = await codec.getNextFrame();
    final ui.Image loadedImage = frameInfo.image;

    setState(() {
      image = loadedImage; // Set the loaded image as the state
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Circle Border Drawing'),
      ),
      body: Center(
        child: Stack(
          children: [
            //grey color road
            CustomPaint(
              size: const Size(
                  200, 200), // Set the size of the CustomPaint widget
              painter: RoadPainter(25.0), // Set the border width
            ),
            //to be filled by blue color
            CustomPaint(
              size: const Size(
                  200, 200), // Set the size of the CustomPaint widget
              painter: CircleBorderPainter(
                  _carProgress, // Pass the current progress to the painter
                  25.0,
                  image,
                  const Color(0xff243347)),
            ),
            Text("$_elapsedSeconds")
          ],
        ),
      ),
    );
  }
}

class CircleBorderPainter extends CustomPainter {
  final double carProgress; // Progress of the car and path fill (0.0 to 1.0)
  final double borderWidth;
  final ui.Image? image;
  final Color fillColor;

  CircleBorderPainter(
      this.carProgress, this.borderWidth, this.image, this.fillColor);

  @override
  void paint(Canvas canvas, Size size) {
    final double radius = size.width / 2;
    final Paint paint = Paint()
      ..color = fillColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = borderWidth
      ..strokeCap = StrokeCap.round;

    // Calculate the arc angle based on progress
    double sweepAngle = carProgress * 2 * pi;

    final center = size.center(Offset.zero);

    // Draw the arc up to the current progress
    Rect rect = Rect.fromCircle(center: center, radius: radius);
    canvas.drawArc(rect, -pi / 2, sweepAngle, false, paint);

    // Calculate the car's position along the arc based on progress
    double carAngle = -pi / 2 + sweepAngle;
    double carX = size.width / 2 + radius * cos(carAngle);
    double carY = size.height / 2 + radius * sin(carAngle);
    Offset carPosition = Offset(carX, carY);

    DashedCirclePainter dashedCirclePainter = DashedCirclePainter(
      strokeWidth: 1.0,
      color: Colors.white,
      dashPattern: [10.0, 5.0], // Dash length and gap length
    );
    dashedCirclePainter.paint(canvas, size, center, radius);

    // Draw the car image at the calculated position
    if (image != null) {
      // Desired image width set to 24
      double desiredImageWidth = 24;

      // Calculate the image aspect ratio
      double imageAspectRatio =
          image!.width.toDouble() / image!.height.toDouble();

      // Calculate the height based on the desired width and aspect ratio
      double desiredImageHeight = desiredImageWidth / imageAspectRatio;

      // Save the canvas state
      canvas.save();

      // Translate the canvas to the car position
      canvas.translate(carPosition.dx, carPosition.dy);

      // Rotate the canvas based on the car's angle
      canvas.rotate(carAngle);

      // Draw the car image at the car position
      canvas.drawImageRect(
        image!,
        Rect.fromLTWH(0, 0, image!.width.toDouble(), image!.height.toDouble()),
        Rect.fromCenter(
            center: Offset.zero,
            width: desiredImageWidth,
            height: desiredImageHeight),
        Paint(),
      );

      // Restore the canvas state
      canvas.restore();
    }
  }

  @override
  bool shouldRepaint(CircleBorderPainter oldDelegate) {
    return oldDelegate.carProgress != carProgress ||
        oldDelegate.borderWidth != borderWidth ||
        oldDelegate.image != image;
  }
}

class RoadPainter extends CustomPainter {
  final double borderWidth;

  RoadPainter(this.borderWidth);

  @override
  void paint(Canvas canvas, Size size) {
    // Paint for the base road
    final Paint roadPaint = Paint()
      ..color = Colors.grey // Grey color for the road
      ..style = PaintingStyle.stroke
      ..strokeWidth = borderWidth;

    // Calculate the radius and center of the canvas
    final double radius = size.width / 2;
    final Offset center = size.center(Offset.zero);

    // Draw the base circle (road) with specified border width
    canvas.drawCircle(center, radius, roadPaint);

    DashedCirclePainter dashedCirclePainter = DashedCirclePainter(
      strokeWidth: 1.0,
      color: Colors.white,
      dashPattern: [10.0, 5.0], // Dash length and gap length
    );
    dashedCirclePainter.paint(canvas, size, center, radius);
  }

  @override
  bool shouldRepaint(RoadPainter oldDelegate) {
    return oldDelegate.borderWidth != borderWidth;
  }
}

class DashedCirclePainter {
  final double strokeWidth;
  final Color color;
  final List<double> dashPattern;

  DashedCirclePainter({
    required this.strokeWidth,
    required this.color,
    required this.dashPattern,
  });

  void paint(Canvas canvas, Size size, Offset center, double radius) {
    // Paint for the red dashed circle
    final Paint dashedCirclePaint = Paint()
      ..color = color // Color for the dashed circle
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth;

    // Create a Path for the red dashed circle
    Path dashedCirclePath = Path();
    dashedCirclePath.addOval(Rect.fromCircle(center: center, radius: radius));

    // Draw the dashed red circle using dashPath function
    canvas.drawPath(
      pd.dashPath(
        dashedCirclePath,
        dashArray: pd.CircularIntervalList<double>(dashPattern),
      ),
      dashedCirclePaint,
    );
  }
}

希望有帮助...

© www.soinside.com 2019 - 2024. All rights reserved.