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

相关推荐
timeweaver12 分钟前
深度解析 Nginx 前端 location 配置与优先级:你真的用对了吗?
前端·nginx·前端工程化
鲸落落丶13 分钟前
网络通信---Axios
前端
wwy_frontend14 分钟前
React性能优化实战:从卡顿到丝滑的8个技巧
前端·react.js
小高00729 分钟前
面试官:npm run build 到底干了什么?从 package.json 到 dist 的 7 步拆解
前端·javascript·vue.js
天选打工圣体30 分钟前
个人学习笔记总结(四)抽离elpis并发布npm包
前端
JayceM2 小时前
Vue中v-show与v-if的区别
前端·javascript·vue.js
HWL56792 小时前
“preinstall“: “npx only-allow pnpm“
运维·服务器·前端·javascript·vue.js
德育处主任2 小时前
p5.js 掌握圆锥体 cone
前端·数据可视化·canvas
mazhenxiao2 小时前
qiankunjs 微前端框架笔记
前端
无羡仙2 小时前
事件流与事件委托:用冒泡机制优化前端性能
前端·javascript