Flutter ViewPage滑动速度调整

做flutter技术已经有小半年了,最近在使用Flutter自带的ViewPage组件,滑动效果有一丝丝微妙感。最后收尾效果是物理线形的弹簧的效果(就这么奇葩)。

先给大家感受一下原有的ViewPage效果吧。


原pageView实现效果.gif

关于修改这个动画效果的参考资料很少。今天简单说一下修改方式:
flutter动画基本上分为两大派系:补间动画 和 物理动画(比如失重、超重、动量守恒)。我就不展开说了,这个参考资料应该很多。

源码探索

从physics属性开始找起,它是一个控制物理动画的组件:

page_view.data
class _PageViewState extends State {
...
Widget build(BuildContext context) {
  final AxisDirection axisDirection = _getDirection(context);
  final ScrollPhysics physics = _ForceImplicitScrollPhysics(
    allowImplicitScrolling: widget.allowImplicitScrolling,
  ).applyTo(widget.pageSnapping
      ? _kPagePhysics.applyTo(widget.physics)
      : widget.physics);
...
}

从这里看出,如果不设置pageSnapping的话_kPagePhysics就是viewpage动画的实现类。再往下找:

const PageScrollPhysics _kPagePhysics = PageScrollPhysics();
/// Scroll physics used by a [PageView].
///
/// These physics cause the page view to snap to page boundaries.
///
/// See also:
///
///  * [ScrollPhysics], the base class which defines the API for scrolling
///    physics.
///  * [PageView.physics], which can override the physics used by a page view.
class PageScrollPhysics extends ScrollPhysics {
  /// Creates physics for a [PageView].
  const PageScrollPhysics({ ScrollPhysics parent }) : super(parent: parent);
  @override
  PageScrollPhysics applyTo(ScrollPhysics ancestor) {
    return PageScrollPhysics(parent: buildParent(ancestor));
  }
…
  @override
  Simulation createBallisticSimulation(ScrollMetrics position, double velocity) {
    // If we're out of range and not headed back in range, defer to the parent
    // ballistics, which should put us back in range at a page boundary.
    if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) ||
        (velocity >= 0.0 && position.pixels >= position.maxScrollExtent))
      return super.createBallisticSimulation(position, velocity);
    final Tolerance tolerance = this.tolerance;
    final double target = _getTargetPixels(position, tolerance, velocity);
    if (target != position.pixels)
      return ScrollSpringSimulation(spring, position.pixels, target, velocity, tolerance: tolerance);
    return null;
  }
...
}

嗯,在这里我们找到里每次滑动的实现类,ScrollSpringSimulation(漩涡弹簧模拟)对每次滑动进行渲染。
找到这个,我们就可以尝试修改一下源码:

return ScrollSpringSimulation(SpringDescription({
    this.mass, //质量,控制滚动的惯性
    this.stiffness,//刚性,滚动收尾速度
    this.damping,//阻尼,俗称摩擦力
}), position.pixels, target, velocity, tolerance: tolerance);

要说明一下:(对物理感知敏感的小伙伴绕道)
mass:控制质量,数值越大越容易还原到静止状态
stiffness:控制滑动力度,数值越大滑动速度远快(劲越大)
damping:阻力,当小于1时则可以取消回弹动画

现在,可以整点骚东西:


viewPage滑动测试.gif

毕竟我们改源码是没有意义的,git并不会记录我们对源码的修改。刚刚分析的途中,我们看到了部分猫腻

///  * [ScrollPhysics], the base class which defines the API for scrolling physics.

/// Set to false to disable page snapping, useful for custom scroll behavior.
final bool pageSnapping;

ok,只要我们重写一个PageScrollPhysics 再搭配上 pageSnapping 属性即可完成这一切:
怎么重写PageScrollPhysics?简单,command+c command+v。

调整实现

第一步:重写一个正常的PageScrollPhysics

class pageScrollPhysics extends PageScrollPhysics {
  const pageScrollPhysics({ScrollPhysics parent}) : super(parent: parent);

  @override
  pageScrollPhysics applyTo(ScrollPhysics ancestor) {
    return pageScrollPhysics(parent: buildParent(ancestor));
  }

  double _getPage(ScrollMetrics position) {
    if (position is _PagePosition) return position.page;
    return position.pixels / position.viewportDimension;
  }

  double _getPixels(ScrollMetrics position, double page) {
    if (position is _PagePosition) return position.getPixelsFromPage(page);
    return page * position.viewportDimension;
  }

  double _getTargetPixels(
      ScrollMetrics position, Tolerance tolerance, double velocity) {
    double page = _getPage(position);
    if (velocity < -tolerance.velocity)
      page -= 0.5;
    else if (velocity > tolerance.velocity) page += 0.5;
    return _getPixels(position, page.roundToDouble());
  }

  @override
  Simulation createBallisticSimulation(
      ScrollMetrics position, double velocity) {
    if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) ||
        (velocity >= 0.0 && position.pixels >= position.maxScrollExtent))
      return super.createBallisticSimulation(position, velocity);
    final Tolerance tolerance = this.tolerance;
    final double target = _getTargetPixels(position, tolerance, velocity);
    if (target != position.pixels)
      return ScrollSpringSimulation(SpringDescription(
        mass: 8,
        stiffness: 150,
        damping: 15,
      ), position.pixels, target, velocity,
          tolerance: tolerance);
    return null;
  }

  @override
  bool get allowImplicitScrolling => false;
}
……(剩下的自己copy)

第二步:pageView设置物理动画空间

PageView.builder(
    pageSnapping:false,//必须
    physics:ClampingScrollPhysics()
    itemBuilder: (context, index) {
      return item(data);
    });

效果:


viewPage滑动效果调整.gif

大功告成、可喜可贺。下次分享见!

你可能感兴趣的:(Flutter ViewPage滑动速度调整)