Flutter 研究:基于 lerp 的线性过渡

一、基础概念

在 Flutter 中,lerp 是 'linear interpolation' 的缩写,中文通常称为线性插值。线性插值是用于在两个值之间进行平滑过渡的一种方法。它在动画、颜色过渡、尺寸变化等许多场景中广泛使用。lerp " "的基本原理是通过给定的参数 t,在两个值 a 和 b 之间进行插值。参数 t 通常在 0 到 1 的范围内,其中 t = 0 时返回 a,t = 1 时返回 b,而 t 在 0 和 1 之间时返回一个介于 a 和 b 之间的值。

二、示例

less 复制代码
//
//  LerpDemo.dart
//  flutter_templet_project
//
//  Created by shang on 2024/8/6 12:09.
//  Copyright © 2024/8/6 shang. All rights reserved.
//

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_templet_project/basicWidget/n_section_header.dart';
import 'package:flutter_templet_project/basicWidget/n_slider.dart';
import 'package:flutter_templet_project/extension/ddlog.dart';
import 'package:get/get.dart';

class LerpDemo extends StatefulWidget {
  const LerpDemo({
    super.key,
    this.arguments,
  });

  final Map<String, dynamic>? arguments;

  @override
  State<LerpDemo> createState() => _LerpDemoState();
}

class _LerpDemoState extends State<LerpDemo>
    with SingleTickerProviderStateMixin {
  final _scrollController = ScrollController();

  Map<String, dynamic> arguments = Get.arguments ?? <String, dynamic>{};

  late final AnimationController _controller = AnimationController(
    duration: Duration(seconds: 2),
    vsync: this,
  )..repeat(reverse: true);

  late final tween = ColorTween(
    begin: Colors.red,
    end: Colors.blue,
  );
  late final Animation<Color?> _animation = tween.animate(_controller);

  final String desc = r"在 Flutter 中,lerp 是 'linear interpolation' "
      "的缩写,中文通常称为线性插值。线性插值是用于在两个值之间进行平滑过渡的一种方法。它在动画、颜色过渡、尺寸变化等许多场景中广泛使用。lerp "
      "的基本原理是通过给定的参数 t,在两个值 a 和 b 之间进行插值。参数 t 通常在 0 到 1 的范围内,其中 t = 0 时返回 a,t = 1 时返回 b,而 t 在 0 和 1 之间时返回一个介于 a 和 b 之间的值。";

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("$widget"),
      ),
      body: buildBody(),
    );
  }

  Widget buildBody() {
    return Scrollbar(
      controller: _scrollController,
      child: SingleChildScrollView(
        controller: _scrollController,
        child: Container(
          padding: EdgeInsets.all(12),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              NSectionHeader(
                text: Text(desc),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: [
                    Text("其数学公式为:"),
                    Padding(
                      padding: const EdgeInsets.symmetric(vertical: 8.0),
                      child: Text(
                        "lerp(a,b,t)=a+(b−a)×t",
                        style: TextStyle(
                          fontSize: 26,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
              NSectionHeader(
                text: Text(
                  "二. 随滑块变化的颜色和尺寸",
                  style: TextStyle(
                    fontWeight: FontWeight.w600,
                  ),
                ),
                child: buildLerpColor(),
              ),
              NSectionHeader(
                text: Text(
                  "三. 基于 ColorTween 的动画",
                  style: TextStyle(
                    fontWeight: FontWeight.w600,
                  ),
                ),
                child: ValueListenableBuilder(
                  valueListenable: _animation,
                  builder: (context, value, child) {
                    return Container(
                      width: 100,
                      height: 100,
                      color: _animation.value,
                      child: Column(
                        children: [
                          Text("a: Colors.red"),
                          Text("b: Colors.blue"),
                        ]
                            .map((e) => Padding(
                                  padding: EdgeInsets.symmetric(vertical: 4),
                                  child: e,
                                ))
                            .toList(),
                      ),
                    );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget buildLerpColor({
    double min = 0,
    double max = 1,
    double current = 0,
  }) {
    return StatefulBuilder(
      builder: (context, setState) {
        Color a = Colors.red;
        Color b = Colors.blue;
        Color? color = Color.lerp(a, b, current);

        Size sizeStart = Size(240, 150);
        Size sizeEnd = Size(300, 200);
        Size? size = Size.lerp(sizeStart, sizeEnd, current);

        final desc = "t: ${current.toStringAsFixed(2)}";

        return Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Row(
              children: [
                Expanded(
                  child: Slider(
                    value: current,
                    onChanged: (double value) {
                      current = value;
                      setState(() {});
                    },
                    min: 0,
                    max: 1,
                    divisions: 100,
                  ),
                ),
                Text(desc),
              ],
            ),
            Container(
              width: size?.width,
              height: size?.height,
              alignment: Alignment.center,
              decoration: BoxDecoration(
                color: color,
                border: Border.all(color: Colors.blue),
                borderRadius: BorderRadius.all(Radius.circular(0)),
              ),
              child: Column(
                children: [
                  Text("a: Colors.red"),
                  Text("b: Colors.blue"),
                  Text("sizeStart: $sizeStart"),
                  Text("sizeEnd: $sizeEnd"),
                ]
                    .map((e) => Padding(
                          padding: EdgeInsets.symmetric(vertical: 4),
                          child: e,
                        ))
                    .toList(),
              ),
            ),
          ],
        );
      },
    );
  }
}

最后

1、凡是遵循以下格式的静态方法的类型均支持线性变化

dart 复制代码
static T? lerp(T? a, T? b, double t) {

2、lerp公式函数有SDK底层支持

dart 复制代码
double? lerpDouble(num? a, num? b, double t) {
  if (a == b || (a?.isNaN ?? false) && (b?.isNaN ?? false)) {
    return a?.toDouble();
  }
  a ??= 0.0;
  b ??= 0.0;
  return a * (1.0 - t) + b * t;
}

double _lerpDouble(double a, double b, double t) {
  return a * (1.0 - t) + b * t;
}


double _lerpInt(int a, int b, double t) {
  return a + (b - a) * t;
}

github

相关推荐
YUFENGSHI.LJ8 分钟前
Flutter 如何使用fvm进行多项目sdk管理
flutter
程序猿_极客11 分钟前
【期末网页设计作业】HTML+CSS+JavaScript 蜡笔小新 动漫主题网站设计与实现(附源码)
前端·javascript·css·html·课程设计·期末网页设计
zl_vslam15 分钟前
SLAM中的非线性优-3D图优化之轴角在Opencv-PNP中的应用(一)
前端·人工智能·算法·计算机视觉·slam se2 非线性优化
CDwenhuohuo1 小时前
用spark-md5实现切片上传前端起node模拟上传文件大小,消耗时间
前端
阿桂有点桂1 小时前
React使用笔记(持续更新中)
前端·javascript·react.js·react
自由日记1 小时前
实例:跳动的心,火柴人
前端·css·css3
柯腾啊1 小时前
一文简单入门 Axios
前端·axios·apifox
im_AMBER2 小时前
React 15
前端·javascript·笔记·学习·react.js·前端框架
How_doyou_do2 小时前
模态框的两种管理思路
java·服务器·前端
snow@li2 小时前
前端:前端/浏览器 可以录屏吗 / 实践 / 录制 Microsoft Edge 标签页、应用窗口、整个屏幕
前端·浏览器录屏·前端录屏·web录屏