コンサルティング事業 2021.01.06

Flutter × UI #03 ~ agexコーポレートサイトをトレースしてみた (スライダー編 part2) ~

これは、Flutterの楽しさを分かち合いたい1人のバックエンドエンジニアが、Flutterを広めるために行った、FlutterでのUI作成についての小さな物語です。

FlutterでUIを作成する楽しさを感じながら、ゆるくながく続けるつもりです。

前回までのおはなし
Flutter × UI #01 ~ agexコーポレートサイトをトレースしてみた (ヘッダー編) ~
Flutter × UI #02 ~ agexコーポレートサイトをトレースしてみた (スライダー編 part1) ~

今回のおはなし
今回は、弊社HPの「スライダー」をFlutterで再現した際のおはなし part2です。

引きづづき、実装を進めていきます。

[ここまでの成果]
・アイテムの形は平行四辺形
・アイテムは水平方向に、一定間隔で配列されている
・アイテムは一定時間間隔で自動的にスライドされる
・アイテムのスライドは左方向
・アイテムが最左端へスライドする際、アイテムのサイズが大きくなる
・最左端のアイテムのみ、最右端へスライドする
・最右端へのスライド時、アイテムはフェードアウトアニメーションがついている
・最右端へのスライド時、アイテムは他要素の後ろ側を通過する
・マウスがアイテム内へ移動すると、アイテム内画像がズームされる
・マウスがアイテム外へ移動すると、アイテム内画像は元サイズへズームアウトされる
・スライダー下部には、最左端アイテムについてのアイテム説明が配置される
・アイテム説明は、アイテムのスライドに合わせてフェードアニメーションによって切り替わる
・スライダー下部のアイテム説明右側に、スライドボタンが配置される
・マウスがスライドボタン内に移動すると、スライドボタンは各方向に少し移動する
・マウスがスライドボタン外に移動すると、スライドボタンは元の位置に移動する

再現してみる
まだ1項目しか実装完了していませんが、のんびりやっていきます。

アイテムは水平方向に、一定間隔で配列されている

これは、「Stack」Widgetと、「Positioned」Widgetを組み合わせて再現していくことにします。
また、各アイテム間の間隔は「10」とします。

// アイテムの各パラメータ指定
double width = 200;
double height = 320;
double offset = 50;

Stack(
  children: [
    Positioned(
      top: 0,
      left: ((width – offset) + 10) * 0, // ((width – offset) + 10) * 要素の位置インデックス
      child: SliderItem( // n個目のアイテム
        height: height,
        width: width,
        offset: offset,
        imagePath: “foo/bar”, // 画像のパスを指定
      ),
    ),
    Positioned(
      top: 0,
      left: ((width – offset) + 10) * 1, // ((width – offset) + 10) * 要素の位置インデックス
      child: SliderItem( // n個目のアイテム
        height: height,
        width: width,
        offset: 50,
        imagePath: “foo/bar”, // 画像のパスを指定
      ),
    ),
   
      ・
      ・
      ・

    Positioned(
      top: 0,
      left: ((width – offset) + 10) * (アイテム数 – 1), // ((width – offset) + 10) * 要素の位置インデックス
      child: SliderItem( n個目のアイテム
        height: height,
        width: width,
        offset: offset,
        imagePath: “foo/bar”, // 画像のパスを指定
      ),
    ),  
  ],
)

これで、シンプルにアイテムを配列するところまでは再現できました。

アイテムは一定時間間隔で自動的にスライドされる
アイテムのスライドは左方向
これは、「Positioned」Widgetの「left」プロパティを、一定時間間隔で変化させることで実現します。
スライド方向は左方向なので、初期位置が最右端のアイテムを基準に、実装方法を考えていきます。

「Positioned」Widgetの「left」プロパティには「((width – offset) + 10) * 要素の位置インデックス」で算出される値を格納しています。
この中の「要素の位置インデックス」を時間経過とともに減少させることで、アイテムを左方向へとスライドさせることができます。

時間経過とともに減少する値(tIndex)は、「Timer」を利用して作成します。
pIndexは、このTimerを使って作成した値を使うことで算出することができます。

int itemCount = /* 全アイテム数 */
int tIndex = itemCount;
int iIndex = /* 各アイテムの初期位置インデックス */
int pIndex = ((tIndex % itemCount) + iIndex) % itemCount; /* 要素の位置インデックス */

Timer timer = Timer.periodic(
  Duration(seconds: 3),
  _timerHandler,
);

void _timerHandler(Timer timer) {
  tIndex = tIndex == 1 ? itemCount : tIndex – 1;
  setState(() {});
}

想定していたよりpIndexの計算が複雑になってしまいました。。
表にまとめると、各indexの値は下記の通りとなり、「要素の位置インデックスを時間経過とともに減少させる」ことができました!

○ 時間経過ごとの各index値

これらを利用して、スライドするアイテムを作成していきます。
アイテムには、現状「Positioned」Widgetを利用していますが、アイテムの位置をアニメーションで変化させたいため「AnimatedPositioned」Widgetに変更します。

AnimatedPositioned(
  key: ValueKey(iIndex),
  curve: Curves.easeOut,
  duration: Duration(seconds: 1),
  top: 0,
  left: ((width – offset) + 10) * pIndex,
  child: SliderItem(
    height: height,
    width: width,
    offset: offset,
    imagePath: “foo/bar”,
  ),
),

これらを組み合わせることで、下記のように時間経過とともに自動でスライドするスライダーを作ることができます。

[ここまでの成果]
・アイテムの形は平行四辺形
・アイテムは水平方向に、一定間隔で配列されている
・アイテムは一定時間間隔で自動的にスライドされる
・アイテムのスライドは左方向
・アイテムが最左端へスライドする際、アイテムのサイズが大きくなる
・最左端のアイテムのみ、最右端へスライドする
・最右端へのスライド時、アイテムはフェードアウトアニメーションがついている
・最右端へのスライド時、アイテムは他要素の後ろ側を通過する
・マウスがアイテム内へ移動すると、アイテム内画像がズームされる
・マウスがアイテム外へ移動すると、アイテム内画像は元サイズへズームアウトされる
・スライダー下部には、最左端アイテムについてのアイテム説明が配置される
・アイテム説明は、アイテムのスライドに合わせてフェードアニメーションによって切り替わる
・スライダー下部のアイテム説明右側に、スライドボタンが配置される
・マウスがスライドボタン内に移動すると、スライドボタンは各方向に少し移動する
・マウスがスライドボタン外に移動すると、スライドボタンは元の位置に移動する

今回はここまでにします。
基本的なスライドアニメーションは作成することができました。
各index値の時間変化ごとの値算出に関しては、さらっと紹介しましたが結構苦戦しました。。

次回も引き続き、スライダーを作成していきます。

この記事をシェアする
事業について詳しく知る

コンサルティング事業について詳しく知りたい方はこちら

RECOMMEND

おすすめ記事