美文网首页
flutter实现沿圆角矩形边框的高光运动

flutter实现沿圆角矩形边框的高光运动

作者: 非新生代菜鸟 | 来源:发表于2023-11-30 15:32 被阅读0次

基本思路

1、创建圆角矩形,高光模型(小球),使用Stack来定义高光与矩形的相对位置关系;
2、利用AnimatedBuilder创建动画,通过_controller.addListener来更新高光位置坐标,从而实现动画效果。
3、拆分运动行程,四条直线和四个弧度,dart中坐标系原点为左上角(0,0),顺时针方向从除圆角外直线向右开始行程。在案例中,行程分别对应line1、arc1(右上角)P、line2、arc2(右下角)、line3、arc3(左下角)、line4、arc4(左上角)。

实现方法

1、定义变量:

  late AnimationController _controller;
  late Animation<double> _animation;
  double w = 400; #矩形宽
  double h = 300; #矩形高
  double r = 30; #圆角半径
  late double totalLength; #圆角矩形周长
  double ballR = 2;  #高光小球半径
  double x = 0; #高光小球x坐标
  double y = 0; #高光小球y坐标

2、初始化:

@override
  void initState() {
    super.initState();
    totalLength = (w + h) * 2 - 8 * r + 4 * r * pi / 2;
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 10000),
    )..repeat();
    _animation = Tween<double>(begin: 0, end: totalLength).animate(_controller);
    // 初始化 x 和 y 的值 运动起点(r,0)
    x = r;
    y = 0;
    _controller.addListener(() {
      // 更新 x 和 y 的值
      updatePosition();
    });
  }

3、路径坐标算法:(圆角弧度的theta角计算:使用运动距离的增量除以半径r):

delta _animation = _animation.value - 已走过的行程;
theta t = delta _animation / r;
x = r * cos(t);
y = r * sin(t);

void updatePosition() {

      // 定义行程
      double line1 = w - 2 * r;
      double line2 = h - 2 * r;
      double line3 = w - 2 * r;
      double line4 = h - 2 * r;
      double arc1 = r * pi / 2;
      double arc2 = r * pi / 2;
      double arc3 = r * pi / 2;
      double arc4 = r * pi / 2;

    setState(() {
      // 在这里更新 x 和 y 的值
      if (_animation.value <= line1) {
        // 第1段,向右直线 line1 从坐标(r,0)开始,向右走直线,距离为 w-2*r
        x = r + _animation.value;
        y = 0;
      } else if (_animation.value > line1 && _animation.value <= (line1 + arc1)) {
        // 第2段,右上圆角 arc1
        double t = (_animation.value - line1) / r - pi / 2;
        x = (w - r) + r * cos(t);
        y = r + r * sin(t);
      } else if (_animation.value > (line1 + arc1) && (_animation.value <= (line1 + arc1 + line2))) {
        // 第3段,下行直线 line2
        x = w;
        y = _animation.value - (line1 + arc1) + r;
      } else if (_animation.value > (line1 + arc1 + line2) && _animation.value <= (line1 + arc1 + line2 + arc2)) {
        // 第4段,右下圆角 arc2
        double t = (_animation.value - (line1 + arc1 + line2)) / r;
        x = (w - r) + r * cos(t);
        y = (h - r) + r * sin(t);
      } else if (_animation.value > (line1 + arc1 + line2 + arc2) && _animation.value <= (line1 + arc1 + line2 + arc2 + line3)) {
        // 第5段,向左直线 line3
        y = h;
        x = (w - r) - (_animation.value - (line1 + arc1 + line2 + arc2));
      } else if (_animation.value > (line1 + arc1 + line2 + arc2 + line3) && _animation.value <= (line1 + arc1 + line2 + arc2 + line3 + arc3)) {
        // 第6段,左下圆角 arc3
        double t = (_animation.value - (line1 + arc1 + line2 + arc2 + line3)) / r - 3 * pi / 2;
        x = r + r * cos(t);
        y = (h - r) + r * sin(t);
      } else if (_animation.value > (line1 + arc1 + line2 + arc2 + line3 + arc3) && _animation.value <= (line1 + arc1 + line2 + arc2 + line3 + arc3 + line4)) {
        // 第7段,上行直线 line4
        x = 0;
        y = (h - r) - (_animation.value - (line1 + arc1 + line2 + arc2 + line3 + arc3));
      } else if (_animation.value > (line1 + arc1 + line2 + arc2 + line3 + arc3 + line4) && _animation.value <= totalLength) {
        // 第8段,左上圆角 arc4
        double t = (_animation.value - (line1 + arc1 + line2 + arc2 + line3 + arc3 + line4)) / r - pi;
        x = r + r * cos(t);
        y = r + r * sin(t);
      }
    });
  }

4、相关数学公式:

# 在圆的极坐标系中,弧度R、半径 radius 和角度 theta 之间的关系可以通过以下公式表示:
R = theta ×  radius
# 这个公式表明,在圆上,给定半径和角度,弧度可以通过将角度除以半径来计算。
# 从弧度转换为角度,可以使用以下公式:
theta = R / radius
# 使用 theta 计算 x 和 y 坐标:
x = r * cos(theta);
y = r * sin(theta);
# 最后注意要相对dart直角坐标原点做坐标平移转换:
x = (平移量)+ r * cos(theta);
y = (平移量)+ r * sin(theta);

完整代码

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

class TestPage extends StatefulWidget {
  const TestPage({Key? key}) : super(key: key);

  @override
  _TestPageState createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'Animation Example',
          style: TextStyle(fontSize: 16, color: Colors.red),
        ),
      ),
      body: const Center(
        child: MyCustomRectangle(),
      ),
    );
  }
}

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

  @override
  _MyCustomRectangleState createState() => _MyCustomRectangleState();
}

class _MyCustomRectangleState extends State<MyCustomRectangle> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;
  double w = 400;
  double h = 300;
  double r = 30;
  late double totalLength;
  double ballR = 2;
  double x = 0;
  double y = 0;

  @override
  void initState() {
    super.initState();
    totalLength = (w + h) * 2 - 8 * r + 4 * r * pi / 2;
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 10000),
    )..repeat();
    _animation = Tween<double>(begin: 0, end: totalLength).animate(_controller);

    // 初始化 x 和 y 的值 运动起点(r,0)
    x = r;
    y = 0;

    _controller.addListener(() {
      // 更新 x 和 y 的值
      updatePosition();
    });
  }

  void updatePosition() {
    setState(() {
      // 在这里更新 x 和 y 的值
      // 定义行程
      double line1 = w - 2 * r;
      double line2 = h - 2 * r;
      double line3 = w - 2 * r;
      double line4 = h - 2 * r;
      double arc1 = r * pi / 2;
      double arc2 = r * pi / 2;
      double arc3 = r * pi / 2;
      double arc4 = r * pi / 2;
      if (_animation.value <= line1) {
        // 第1段,向右直线 line1 从坐标(r,0)开始,向右走直线,距离为 w-2*r
        x = r + _animation.value % line1;
        y = 0;
      } else if (_animation.value > line1 && _animation.value <= (line1 + arc1)) {
        // 第2段,右上圆角 arc1
        double t = (_animation.value - line1) / r - pi / 2;
        x = (w - r) + r * cos(t);
        y = r + r * sin(t);
      } else if (_animation.value > (line1 + arc1) && (_animation.value <= (line1 + arc1 + line2))) {
        // 第3段,下行直线 line2
        x = w;
        y = _animation.value - (line1 + arc1) + r;
      } else if (_animation.value > (line1 + arc1 + line2) && _animation.value <= (line1 + arc1 + line2 + arc2)) {
        // 第4段,右下圆角 arc2
        double t = (_animation.value - (line1 + arc1 + line2)) / r;
        x = (w - r) + r * cos(t);
        y = (h - r) + r * sin(t);
      } else if (_animation.value > (line1 + arc1 + line2 + arc2) && _animation.value <= (line1 + arc1 + line2 + arc2 + line3)) {
        // 第5段,向左直线 line3
        y = h;
        x = (w - r) - (_animation.value - (line1 + arc1 + line2 + arc2));
      } else if (_animation.value > (line1 + arc1 + line2 + arc2 + line3) && _animation.value <= (line1 + arc1 + line2 + arc2 + line3 + arc3)) {
        // 第6段,左下圆角 arc3
        double t = (_animation.value - (line1 + arc1 + line2 + arc2 + line3)) / r - 3 * pi / 2;
        x = r + r * cos(t);
        y = (h - r) + r * sin(t);
      } else if (_animation.value > (line1 + arc1 + line2 + arc2 + line3 + arc3) && _animation.value <= (line1 + arc1 + line2 + arc2 + line3 + arc3 + line4)) {
        // 第7段,上行直线 line4
        x = 0;
        y = (h - r) - (_animation.value - (line1 + arc1 + line2 + arc2 + line3 + arc3));
      } else if (_animation.value > (line1 + arc1 + line2 + arc2 + line3 + arc3 + line4) && _animation.value <= totalLength) {
        // 第8段,左上圆角 arc4
        double t = (_animation.value - (line1 + arc1 + line2 + arc2 + line3 + arc3 + line4)) / r - pi;
        x = r + r * cos(t);
        y = r + r * sin(t);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: w + 2 * ballR,
      height: h + 2 * ballR,
      child: Stack(
        children: [
          Positioned(
            left: ballR,
            top: ballR,
            child: Container(
              width: w,
              height: h,
              decoration: BoxDecoration(
                  color: Colors.yellow,
                  border: Border.all(
                    color: Colors.blue,
                    width: 2,
                  ),
                  borderRadius: BorderRadius.circular(r)),
            ),
          ),
          // Moving dot along the border
          AnimatedBuilder(
            animation: _animation,
            builder: (context, child) {
              return Positioned(
                left: x,
                top: y,
                child: Container(
                  width: ballR * 2,
                  height: ballR * 2,
                  decoration: const BoxDecoration(
                    shape: BoxShape.circle,
                    color: Colors.purple,
                  ),
                ),
              );
            },
          ),
        ],
      ),
    );
  }

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

相关文章

  • CSS边框圆角--跟着李南江学编程

    1.什么是边框圆角? 就是把矩形边框变成圆角边框,就叫做边框圆角。 2.设置边框圆角的格式 2.1 border-...

  • 动态设置shape

    GitHub 问题: 在平时的项目开发中,为了实现圆角矩形、带边框的矩形、圆、椭圆等效果,然后在drawble文件...

  • 基础学习

    矩形 属性功能x横向偏移量width宽height高rx圆角ry圆角fill背景色stroke边框颜色stroke...

  • Hacks控件篇-Hack4 为背景添加圆角边框

    作者:李旺成 时间:2016年5月14日 这个 Hack 将介绍使用自定义形状实现圆角边框背景。 圆角矩形按钮 曾...

  • HTML5 Canvas笔记——绘制方形钟

    利用矩形的绘制,颜色与透明度,编程绘制方形钟 要求: (1)钟面的矩形边框应当是圆角矩形, (2)边框线要采用除默...

  • 绘制圆角边框、文字 生成图片

    思路1、画布 width、height,2、外圆角矩形 width、height,使用边框颜色color3、若边框...

  • 如何实现圆角矩形, 有哪些方法可以实现扇形View

    如何实现圆角矩形, 有哪些方法可以实现扇形View 圆角矩形使用view.layer.cornerRadius来实...

  • css实现四种常见边框内外角组合

    首先让我们先来看看效果图吧: 其中,div1:边框内外直角矩形;div2:边框内外圆角矩形;div3:边框内直角外...

  • 边框 背景

    1 边框 能够创建圆角边框,向矩形添加阴影,使用图片来绘制边框 - 并且不需使用设计软件 对于 border-im...

  • css之border

    css边框属性 通过css3,能够创建圆角边框,向矩形添加阴影,使用图片来绘制边框 border-radius b...

网友评论

      本文标题:flutter实现沿圆角矩形边框的高光运动

      本文链接:https://www.haomeiwen.com/subject/ufyqgdtx.html