コンサルティング事業 2021.01.05

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

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

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

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

今回のおはなし
今回は、agexコーポレートサイトの「スライダー」をFlutterでトレースした際のおはなしです。

分析してみる
トレース対象となる「スライダー」の分析を行います。
以降は、「スライダー」内でスライドされている各要素を「アイテム」、スライダー下部の説明文を「アイテム説明」、アイテム説明右側のボタンを「スライドボタン」と呼ぶことにします。

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

お、おおおぉぉぉ、、、
ヘッダー編では7個の要素が出てきましたが、今回は15個出てきました。さらにアニメーションはヘッダー編より明らかに複雑なので、心が折れそうです。。。

実装してみる
前回と同様、分析内容を1つずつ実装していきます。

アイテムの形は平行四辺形
各アイテムには、「Container」Widgetを利用することにし、まずはシンプルな長方形のアイテムを作成します。
各アイテムに表示する画像の取得方法については、簡単のためにアセットから取得する形(AssetImage)で作成しています。

Container(
  height: 320
  width: 200,
  decoration: BoxDecoration(
    image: DecorationImage(
      fit: BoxFit.none,
      image: AssetImage(/* 画像のパス */),
    ),
  ),
),

これで、長方形のアイテムが作成できます。

ここから、平行四辺形のアイテムを作成していきます。
作成方針は、上記で作成した長方形のアイテムを、平行四辺形に「切り抜く」で進めることにします。切り抜きには「ClipPath」Widgetを利用します。

ClipPath(
  clipper: ParallelogramClipper(50), // ここで切り抜く形を指定する。
  child: Container( // 長方形のアイテム
    height: 320
    width: 200,
    decoration: BoxDecoration(
      image: DecorationImage(
        fit: BoxFit.none,
        image: AssetImage(/* 画像のパス */),
      ),
    ),
  ),
),

平行四辺形の切り抜きは、下図のイメージ内で示したパラメータを利用して実装を進めます。

この図に示した「offset」、「width」、「height」を利用し、getClipメソッド内で
① (offset, 0)
② (width, 0)
③ (width – offset, height)
④ (0, height)
⑤ (offset, 0.0)

の順でpath指定を行うことで、平行四辺形の切り抜きを実現します。

実装は、以下の通りとなります。

class ParallelogramClipper extends CustomClipper {
  final double offset;

  ParallelogramClipper(this.offset); // 引数でオフセット値を取得

  @override
  Path getClip(Size size) {
    final path = Path();
    path.moveTo(offset, 0.0);
    path.lineTo(200, 0.0);
    path.lineTo(200 – offset, 320);
    path.lineTo(0.0, 320);
    path.lineTo(offset, 0.0);
    return path;
  }

  @override
  bool shouldReclip(covariant CustomClipper oldClipper) {
    return true;
  }
}

これで、「アイテムの形は平行四辺形」を実現することができました!

一旦ここまで作成した内容を、StatelessWidgetとして整理します。
(アニメーションを扱う必要があるため、後ほどStatefulWidgetに変更します)

また汎用性を持たせるために、アイテムの「高さ」「幅」「オフセット」「画像のパス」は、引数で指定できるようにしておきます。

class SliderItem extends StatelessWidget {
  final double height; // 高さ
  final double width; // 幅
  final double offset; // オフセット
  final String imagePath; // 画像のパス

  const SliderItem(
    {Key key, this.height, this.width, this.offset, this.imagePath})
    : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ClipPath(
      clipper: ParallelogramClipper(offset),
        child: Container(
          height: height,
          width: width,
          decoration: BoxDecoration(
          image: DecorationImage(
            fit: BoxFit.none,
            image: AssetImage(imagePath),
          ),
        ),
      ),
    );
  }
}

class ParallelogramClipper extends CustomClipper {
  final double offset;

  ParallelogramClipper(this.offset);

  @override
  Path getClip(Size size) {
    final path = Path();
    path.moveTo(offset, 0.0);
    path.lineTo(size.width, 0.0);
    path.lineTo(size.width – offset, size.height);
    path.lineTo(0.0, size.height);
    path.lineTo(offset, 0.0);
    return path;
  }

  @override
  bool shouldReclip(covariant CustomClipper oldClipper) {
    return true;
  }
}

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

切りが良いので今回はここまでとします。1つしか進んでいない。。。

次回は、「アイテムは水平方向に、一定間隔で配列されている」「アイテムは一定時間間隔で自動的にスライドされる」「アイテムのスライドは左方向」あたりについて、紹介していこうと思います。

今日のまとめ
先は長いが、1つずつ丁寧に。。

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

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

RECOMMEND

おすすめ記事