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

相关推荐
y先森26 分钟前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy26 分钟前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu108301891129 分钟前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿2 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡3 小时前
commitlint校验git提交信息
前端
虾球xz3 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇3 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒3 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员3 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐3 小时前
前端图像处理(一)
前端