做flutter技術(shù)已經(jīng)有小半年了寨昙,最近在使用Flutter自帶的ViewPage組件面哥,滑動效果有一絲絲微妙感。最后收尾效果是物理線形的彈簧的效果(就這么奇葩)毅待。
先給大家感受一下原有的ViewPage效果吧。
關(guān)于修改這個動畫效果的參考資料很少归榕。今天簡單說一下修改方式:
flutter動畫基本上分為兩大派系:補(bǔ)間動畫 和 物理動畫(比如失重尸红、超重、動量守恒)。我就不展開說了外里,這個參考資料應(yīng)該很多怎爵。
源碼探索
從physics屬性開始找起,它是一個控制物理動畫的組件:
page_view.data
class _PageViewState extends State<PageView> {
...
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);
...
}
從這里看出盅蝗,如果不設(shè)置pageSnapping的話_kPagePhysics就是viewpage動畫的實現(xiàn)類鳖链。再往下找:
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;
}
...
}
嗯,在這里我們找到里每次滑動的實現(xiàn)類墩莫,ScrollSpringSimulation(漩渦彈簧模擬)對每次滑動進(jìn)行渲染芙委。
找到這個,我們就可以嘗試修改一下源碼:
return ScrollSpringSimulation(SpringDescription({
this.mass, //質(zhì)量狂秦,控制滾動的慣性
this.stiffness,//剛性灌侣,滾動收尾速度
this.damping,//阻尼,俗稱摩擦力
}), position.pixels, target, velocity, tolerance: tolerance);
要說明一下:(對物理感知敏感的小伙伴繞道)
mass:控制質(zhì)量裂问,數(shù)值越大越容易還原到靜止?fàn)顟B(tài)
stiffness:控制滑動力度侧啼,數(shù)值越大滑動速度遠(yuǎn)快(勁越大)
damping:阻力,當(dāng)小于1時則可以取消回彈動畫
現(xiàn)在堪簿,可以整點騷東西:
畢竟我們改源碼是沒有意義的痊乾,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协饲。
調(diào)整實現(xiàn)
第一步:重寫一個正常的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設(shè)置物理動畫空間
PageView.builder(
pageSnapping:false,//必須
physics:ClampingScrollPhysics()
itemBuilder: (context, index) {
return item(data);
});
效果:
大功告成、可喜可賀缴川。下次分享見茉稠!