Flutter编程:使用贝塞尔曲线实现添加购物车的效果
贝塞尔曲线就是这样一条曲线。它是根据四个位置任意点的坐标绘制的平滑曲线。历史上,研究贝塞尔曲线的人们最初基于利用已知的曲线参数方程确定四个点的思想提出了这种绘制矢量曲线的方法。贝塞尔曲线更有趣的地方在于它的“橡皮筋效应”,即随着点有规律的移动,曲线像橡皮筋一样变换,给人一种视觉冲击。 1962年,法国人数学家Pierre和Bézier最先研究了这种绘制曲线的矢量方法,并给出了详细的计算公式。因此,根据此类公式绘制的曲线就以他的名字命名。这个名字就是贝塞尔曲线的名字。 - 百度百科
它的出现奠定了计算机矢量图形的基础,那么我们能用它做什么呢?
网上查了很多,有人说这是必须的,比如“波形”、“抛物线效应”等等。
Bézier 曲线的效果和计算公式
这里不讲 Bézier 曲线各阶之间的差异,只是直接发布效果和公式。
一阶贝塞尔曲线

二阶贝塞尔曲线

三阶贝塞尔曲线

其余都是高阶的,就不详细说了。推导公式如下:
实现将商品添加到购物车的效果
回顾了贝塞尔曲线的原理,我们来看今天要实现的效果:
实现之前
实现之前我们先明确一下当我们思考这个问题时,我们首先可以确定的是,我们需要使用二阶贝塞尔曲线来实现“抛物线效果”。
二阶贝塞尔曲线所需参数:
- 起点p0
- 控制点p1
- 终点p2我就不提了,但怎么看就不提了图片。很重要的一点就是“小红点”,按照抛物线相互落入。
如何显示“小红点”?我们继续吧。
开始实现
让我们开始实现上图中的效果。从哪儿开始?
1。首先我们得到起点和终点
,页面很简单,代码如下:
Column( children: <Widget>[ Expanded( child: ListView.builder( itemBuilder: (BuildContext context, int index) { return Row( // 隐藏无用代码 ); }, itemCount: 100, ), ), Container( height: 1, color: Colors.grey.withOpacity(0.5), ), Container( height: 60, color: Colors.white, child: Row( children: <Widget>[ Padding( padding: EdgeInsets.only(left: 20), child: Icon( Icons.shop_two, key: _key, ), ) ], ), ) ], ) 复制代码
A
Column
,其上方是Sheet“购物车图标”。起点是我们的
ListView
中每一项的+号,终点是左下角的“购物车图标”。给定端点的坐标很容易知道
GlobalKey
,然后您可以在第一帧回调中获得关于什么的位置:方向?
因为起点在
ListView
,而且还能滚动。这时候,很多小朋友可能会说:“给每个图标一个GlobalKey,那不是很好吗!”小朋友,你确定不是找死吗?
GlobalKey 使用 Map 静态常量来存储相应的元素。通过GlobalKey可以找到持有GlobalKey的Widget、State和Element。注意:GlobalKey 非常昂贵,应谨慎使用。
- Vadaski - 颤振 |简单易懂Key
如果现在有10000个商品列表,那不就爆炸了吗?
我们回顾一下刚才获取“购物车”位置的代码。我们实际上使用
GlobalKey
来获取context
和context
来获取位置。那么为什么我们不使用具有context
的组件呢?代码如下:
Builder( builder: (context) { return IconButton( icon: Icon(Icons.add_circle_outline), onPressed: () { // 通过 Builder 组件来获取 context RenderBox box = context.findRenderObject(); var offset = box.localToGlobal(Offset.zero); }, ); }, ) 复制代码
直接使用
Builder
找到组件的位置。现在我们有了起点和终点的坐标,那么控制点呢? ?你可以自由发挥,根据你的效果来决定。这是我得到的:
var x1 = widget.startPosition.dx - 250; var y1 = widget.startPosition.dy - 100; 复制代码
现在二阶贝塞尔曲线所需的所有值都可用,下面可以计算位置。
3。找到每帧中小红点的位置
首先我们得到这个图像和公式。在它们之间,我们都有值P0(起点)、P1(检查点)和P2(终点)。然后还有t,我们可以使用Flutter的
Tween
来获取,最后我们代入公式:@override void initState() { super.initState(); _controller = AnimationController(duration: Duration(milliseconds: 800), vsync: this); _animation = Tween(begin: 0.0, end: 1.0).animate(_controller); // 二阶贝塞尔曲线用值 var x0 = widget.startPosition.dx; var y0 = widget.startPosition.dy; var x1 = widget.startPosition.dx - 250; var y1 = widget.startPosition.dy - 100; var x2 = widget.endPosition.dx; var y2 = widget.endPosition.dy; _animation.addListener(() { // t 动态变化的值 var t = _animation.value; if (mounted) setState(() { left = pow(1 - t, 2) * x0 + 2 * t * (1 - t) * x1 + pow(t, 2) * x2; top = pow(1 - t, 2) * y0 + 2 * t * (1 - t) * y1 + pow(t, 2) * y2; }); }); // 初始化小圆点的位置 left = widget.startPosition.dx; top = widget.startPosition.dy; // 显示小圆点的时候动画就开始 _controller.forward(); } 复制代码
这样,在开始动画之后,我们就得到了每一帧中小红点的位置。
4。显示小红点
如何显示小红点?点击后需要做什么?
我首先想到的是把
IndexStack
包裹起来,设置点击时小红点的位置,然后显示出来。然后我突然想到了
叠加
?。 Button中ListView
的代码如下:IconButton( icon: Icon(Icons.add_circle_outline), onPressed: () { // 点击的时候获取当前 widget 的位置,传入 overlayEntry var _overlayEntry = OverlayEntry(builder: (_) { RenderBox box = context.findRenderObject(); var offset = box.localToGlobal(Offset.zero); return RedDotPage( startPosition: offset, endPosition: _endOffset, ); }); // 显示Overlay Overlay.of(context).insert(_overlayEntry); // 等待动画结束 Future.delayed(Duration(milliseconds: 800), () { _overlayEntry.remove(); _overlayEntry = null; }); }, ) 复制代码
其中
IconButton( icon: Icon(Icons.add_circle_outline), onPressed: () { // 点击的时候获取当前 widget 的位置,传入 overlayEntry var _overlayEntry = OverlayEntry(builder: (_) { RenderBox box = context.findRenderObject(); var offset = box.localToGlobal(Offset.zero); return RedDotPage( startPosition: offset, endPosition: _endOffset, ); }); // 显示Overlay Overlay.of(context).insert(_overlayEntry); // 等待动画结束 Future.delayed(Duration(milliseconds: 800), () { _overlayEntry.remove(); _overlayEntry = null; }); }, ) 复制代码
是小定义的页面红点起点,让出现叠加 。显示时,贝塞尔曲线动画开始。动画做好后,把这个
OverlayEntry
去掉就可以了。作者:Flutter Notes
链接:https://juejin.im/post/5ecd16c2518825433549c283
来源:掘金版权商业转载请联系作者获得许可。非商业转载请注明来源。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。