предисловие
На этот раз импульс Flutter становится все более и более яростным, и, как программа для Android, я, естественно, хочу попробовать его. Изучив эту часть анимации, чтобы углубить понимание анимации FLUTTER, я решил поставить небольшие проекты с открытым исходным кодом, написанные до предыдущего эффекта переключения карт, снова используя «перевод» флаттера.
Без лишних слов давайте посмотрим на эффект:
Android | iOS |
---|---|
Адрес на гитхабе:GitHub.com/Baker JQ/грипп…
идеи
Прежде всего, что касается каскадного эффекта карточек, в исходном Android-проекте он отражается разницей масштабов и переводом Y, и Flutter может продолжать использовать этот метод.
Во-вторых, для содержимого кастомной карточки исходный Android-проект реализован с помощью адаптера, а для Flutter — с помощью IndexedWidgetBuilder.
Наконец, это реализация пользовательской анимации.Исходный проект Android использует ValueAnimator от 0 до 1 для определения процесса отображения анимации, а во Flutter есть только соответствующие Animation и AnimationController, поэтому мы можем напрямую настраивать во время процесса анимации, конкретный метод отображения представления.
Обзор компонентов
Поскольку представление карты должно быть отображено в соответствии с ситуацией анимации, очевидно, что это StatefulWidget.
При этом мы даем три основных режима анимации:
enum AnimType {
TO_FRONT,//被选中的卡片通过自定义动效移至第一,其他的卡片通过通用动效补位
SWITCH,//选中的卡片和第一张卡片互换位置,并都是自定义动效
TO_END,//第一张图片通过自定义动效移至最后,其他卡片通过通用动效补位
}
И обрабатывать всю логику анимации через Helper и Controller
Контроллер передается конструктором
InfiniteCards({
@required this.controller,
this.width,
this.height,
this.background,
});
Помощник создается и инициализируется в initState, и в то же время помощник привязывается к контроллеру:
@override
void initState() {
...
_helper = AnimHelper(
controller: widget.controller,
//传入动画更新监听,动画时调用setState进行实时渲染
listenerForSetState: () {
setState(() {});
});
_helper.init(this, context);
if (widget.controller != null) {
widget.controller.animHelper = _helper;
}
}
В процессе сборки конкретный список виджетов возвращается через помощник, а стек предназначен для достижения каскадного эффекта.
Widget build(BuildContext context) {
...
return Container(
...
child: Stack(
children: _helper.getCardList(_width, _height),
),
);
}
Таким образом завершается основная инициализация и другие операции. Давайте посмотрим, как работают контроллер и помощник.
Controller
Давайте сначала посмотрим, что содержит контроллер:
class InfiniteCardsController {
//卡片构造器
IndexedWidgetBuilder _itemBuilder;
//卡片个数
int _itemCount;
//动画时长
Duration _animDuration;
//点击卡片是否触发切换动画
bool _clickItemToSwitch;
//动画Transform
AnimTransform _transformToFront,_transformToBack,...;
//排序Transform
ZIndexTransform _zIndexTransformCommon,...;
//动画类型
AnimType _animType;
//曲线定义(类Android插值器)
Curve _curve;
//helper
AnimHelper _animHelper;
...
void anim(int index) {
_animHelper.anim(index);
}
void reset(...) {
...
//重设各参数
setControllerParams();
_animHelper.reset();
...
}
}
Из этого видно, что контроллер в основном существует как прокси метода для конфигуратора параметров и помощника. Из этого детская обувь должна знать, что такие операции, как настройка динамического эффекта и запуск динамического эффекта, выполняются через контроллер.Демонстрация выглядит следующим образом:
//构建Controller
_controller = InfiniteCardsController(
itemBuilder: _renderItem,
itemCount: 5,
animType: AnimType.SWITCH,
);
//调用reset
_controller.reset(
itemCount: 4,
animType: AnimType.TO_FRONT,
transformToBack: _customToBackTransform,
);
//调用展示下一张卡片动画
_controller.reset(animType: AnimType.TO_END);
_controller.next();
Что касается конкретной настройки, мы поговорим об этом позже, давайте сначала посмотрим на Helper.
Helper
Helper — это основной класс для реализации всего эффекта анимации.Давайте сначала рассмотрим несколько основных членов, которые он содержит:
class AnimHelper {
final InfiniteCardsController controller;
//切换动画
AnimationController _animationController;
Animation<double> _animation;
//卡片列表
List<CardItem> _cardList = new List();
//需要向后切换的卡片,和需要向前切换的卡片
CardItem _cardToBack, _cardToFront;
//需要向后切换的卡片位置,和需要向前切换的卡片位置
int _positionToBack, _positionToFront;
}
Теперь давайте посмотрим, как эти элементы работают вместе, чтобы вызвать анимацию переключения.
Когда карта выбрана для переключения, эта карта является картой, которую необходимо переключить вперед (ToFront), а первая карта — это карта, которую необходимо переключить назад (ToBack).
void _cardAnim(int index, CardItem card) {
//记录要切换的卡片
_cardToFront = card;
_cardToBack = _cardList[0];
_positionToBack = 0;
_positionToFront = index;
//触发动画
_animationController.forward(from: 0.0);
}
Поскольку AnimationListener установлен, во время процесса анимации будет вызываться setState, который запускает сборку виджета, вызывая тем самым метод getCardList помощника. Рассмотрим, как вернуть список виджетов карточек в процессе переключения анимации.
List<Widget> getCardList(double width, double height) {
for (int i = 0; i < controller.itemCount; i++) {
...
if (_isSwitchAnim) {
//处理切换动画
_switchTransform(width, height, i);
}
...
}
//根据zIndex进行排序渲染
List<CardItem> copy = List.from(_cardList);
copy.sort((card1, card2) {
return card1.zIndex < card2.zIndex ? 1 : -1;
});
return copy.map((card) {
return card.transformWidget;
}).toList();
}
Как показано в приведенном выше коде, анимация выполняется первой, а затем сортируется по zIndex, потому что она гарантированно отрисовывается после фронта.
А как обрабатывается анимация, возьмем в качестве примера переключение на предыдущую карточку:
void _toFrontTransform(double width, double height, int fromPosition, int toPosition) {
CardItem cardItem = _cardList[fromPosition];
controller.zIndexTransformToFront(
cardItem, _animation.value,
_getCurveValue(_animation.value),
width, height, fromPosition, toPosition);
cardItem.transformWidget = controller.transformToFront(
cardItem.widget, _animation.value,
_getCurveValue(_animation.value),
width, height, fromPosition, toPosition);
}
Оказалось, что именно на этом этапе Helper получил виджет карты с помощью пользовательского метода анимации, настроенного в контроллере.
Итак, базовый процесс отображения анимации описан, переходим к самому важному - как настроить анимацию.
пользовательская анимация
Давайте возьмем общую анимацию в качестве примера, чтобы увидеть основной процесс пользовательской анимации.
Во-первых, AnimTransform — это определение следующего метода:
typedef AnimTransform = Transform Function(
Widget item,//卡片原始Widget
double fraction,//动画执行的系数
double curveFraction,//曲线转换后的系数
double cardHeight,//整体高度
double cardWidth,//整体宽度
int fromPosition,//卡片开始位置
int toPosition);//卡片要移动到的位置
Этот метод возвращает Transform, который специально используется для обработки Widget преобразования представления, и нам нужно построить Widget с соответствующими коэффициентами в соответствии с переданными параметрами. Возьмите DefaultCommonTransform в качестве примера:
Transform _defaultCommonTransform(Widget item,
double fraction, double curveFraction, double cardHeight, double cardWidth, int fromPosition, int toPosition)
//需要跨越的卡片数量{
int positionCount = fromPosition - toPosition;
//以0.8做为第一张的缩放尺寸,每向后一张缩小0.1
//(0.8 - 0.1 * fromPosition) = 当前位置的缩放尺寸
//(0.1 * fraction * positionCount) = 移动过程中需要改变的缩放尺寸
double scale = (0.8 - 0.1 * fromPosition) + (0.1 * fraction * positionCount);
//在Y方向的偏移量,每向后一张,向上偏移卡片宽度的0.02
//-cardHeight * (0.8 - scale) * 0.5 对卡片做整体居中处理
double translationY = -cardHeight * (0.8 - scale) * 0.5 -
cardHeight * (0.02 * fromPosition - 0.02 * fraction * positionCount);
//返回缩放后,进行Y方向偏移的Widget
return Transform.translate(
offset: Offset(0, translationY),
child: Transform.scale(
scale: scale,
child: item,
),
);
}
То же самое верно для выбранной карты, которая перемещается на первую позицию, но преобразование пользовательской анимации выполняется в соответствии с преобразователем, соответствующим карте.
Конечный эффект похож на первый щелчок на демонстрационном изображении, и изображение переворачивается вперед на первое место.
Суммировать
Поскольку Flutter использует декларативный метод построения представления, на ранней стадии кодирования на него будет несколько влиять мышление собственного метода кодирования, что делает его неудобным. Но после знакомства с ним вы обнаружите, что многие идеи являются общими, например, анимация, например, концепция интерполятора и т. д.
Кроме того, изучите исходный код по-прежнему наиболее эффективным способом решать проблемы, такие как операция ScrollView BE AnimateTo непосредственно сопоставимым Android, потребность в AnimateTo Repautter Plutter с помощью ScrollControlller, это то, что я нашел в трепетании, в результате чего достигнет бесконечностей. Методы.
Для получения более конкретных демонстраций перейдите к репозиторию Flutter-InfiniteCards на Github, Вы можете пометить и поднять вопросы.
Вставьте адрес Github еще раз:GitHub.com/Baker JQ/грипп…