STORES Tech Blog

こだわりを持ったお商売を支えるプラットフォーム「STORES」の開発チームによる技術ブログです。

こだわりを持ったお商売を支えるプラットフォーム「STORES」の開発チームによる技術ブログです。

アイテム画像件数上限アッププロジェクトの裏側

はじめに

hey でECのフロントエンドエンジニアを担当している @nkoba です。

STORES ECでは先日、アイテム機能のアップデートをリリースしました。そのアップデートのうちの一つが、アイテム画像件数上限アップです。

これは今までアイテムごとに画像を15枚までしか登録できなかった制限を拡大し、30枚まで登録できるようにしたアップデートです(スタンダードプランのみ)。

このアップデートのプロジェクトに担当の一人として開発に参画した中で、フロントエンドに求められる仕事について多くの気づきがあったので、この記事で紹介したいと思います。

アイテム画像件数上限アップとユーザー体験

プロジェクトの当初はフロントエンドとバックエンドの画像枚数のバリデーションを修正すれば作業は終わりという認識でした。

そのため、開発期間もかなり短く見積もって計画を立てていました。実際にバリデーション周りのコードも調査してみましたが、時間をかけずに修正できる見通しでした。

しかし、プロジェクトメンバーで要件を洗い出すミーティングをしたところ、デザイナーから「画像枚数を単純に増やすだけでは管理画面の操作性が落ちてしまう」という懸念が挙がりました。

主に受けた指摘点としては、以下でした。

  • 画像が画面を占有してしまい画面の移動が大変になる
  • 画像の並び替えはD&D(ドラッグ&ドロップ)に頼っているので、難しい移動が出てくる
  • スマートフォンの場合、スクロール可能領域が狭いので、意図しない並び替えが発生する可能性が高い
  • 誤操作によるコンテキストメニューの開閉が起こりやすくなる
  • 画像枚数とは直接は関係ないが、並び替えのインタラクションが分かりづらいため、これも誤操作の原因になっている

デザイナーはFigmaで画像枚数が増えた場合のデザインを提示してくださり、それを見ると体験が悪化が明らかであり、その時点でアイテム画像件数上限アップでやるべきことを間違って理解していたことに気づきました。

そこで全員の認識を合わせ、ユーザーの体験を損なわずにアイテム画像件数上限をアップすることを目指して、改めてプロジェクトを進めることにしました。

以下では指摘された課題に対して技術的にどう取り組んだかについて、書いていきます。行ったことをできる限り簡潔に書くつもりですが、実際にはデザイナーと協力しながら、試行錯誤しながら進めました。

課題と対策

画像が画面を占有してしまい画面の移動が大変になる

画像枚数が増えると画面に占める画像の割合が大きくなるため、最初の入力フォームまでの移動の手間が大きくなると考えられました。

そうなると、アイテムを編集する手間が大きくなり、作業の妨げになります。

f:id:daitasu:20210804153848p:plain

折り畳み機能の実装

アイテム画像が画面を占有してしまうという問題については、シンプルに折り畳み機能を実装することで解決しました。

最初に画面を開いたときに、折り畳んだ状態で表示しておき、画像を追加して一定枚数を超えた時、もしくは「すべて表示」ボタンを押したときに展開するようにしました。

最初の表示枚数はPCだと7枚、SPだと5枚で、最大の枚数まで登録済みの場合はもう1枚ずつ増えます。この辺りもデバイスごと、状況ごとに操作しやすくというデザインの意図が含まれています。

f:id:daitasu:20210804153913p:plain f:id:daitasu:20210804153928p:plain

全ては書きませんが、実装方法はかなり泥臭く書きました。Vue.jsをプロダクトで使っているのですが、computedという算出プロパティを用いて、状況に応じて表示する画像枚数を変えるという実装です。

computed: {
  shownItemImages(): Image[] {
    if (this.isOpen) {
      return this.innerImages;
    }
    if (this.innerImages.length === this.maxItemImage) {
      return this.innerImages.slice(0, imageLimitWhenClosed);
    }
    // 最大枚数に達さない場合、ImageUploaderの分の領域を空ける
    return this.innerImages.slice(0, imageLimitWhenClosed - 1);
  },
},

折りたたみ機能を使うと、折り畳んだ状態の画像配列と全ての画像配列の2つのデータが存在することになるので、それを意識しながら更新しなくてはいけないため、実装、テストともに注意深く行う必要があります。

methods: {
  onInput(images: Image[]): void {
    // 表示されている画像を全ての画像にマージする
    this.innerImages = [
      ...images,
      ...this.innerImages.slice(images.length),
    ];
  },
},

画像の並び替えはD&D(ドラッグ&ドロップ)に頼っているので、難しい移動が出てくる

アイテム編集ページで、画像を並べ替える手段はD&D以外にありません。D&Dは操作する距離が長くなればなるほど操作が難しくなるため、画像枚数が増えれば並び替えが難しくなるという問題がより顕著になると考えられました。

コンテキストメニューに「先頭に移動」を追加

画像枚数が増えた場合に、移動が難しいという課題については、シンプルに「先頭に移動」というアクションを追加しました。

シンプルですが、これにより大幅な移動というユーザーの動作は減らせたと思います。

f:id:daitasu:20210804153953p:plain

実装はシンプルです。実装の都合上で、Vuexのmutationに書いてあります。

  MOVE_TOP(state, image) {
    const index = state.images.indexOf(image);
    state.images.splice(index, 1);
    state.images.unshift(image);
  },

スマートフォンの場合、スクロール可能領域が狭いので、意図しない並び替えが発生する可能性が高い

修正前はスマートフォンで操作する場合、下記の図の通り、左右の細いラインに沿ってスクロールしなければなりませんでした。画像に重なってスクロールした際に、D&Dの操作が反応し、意図しない並び替えが起きる可能性があったからです。

こちらも画像枚数が増えれば、スクロール量が増えるため、この操作をユーザーに要求するのは厳しいと考えられました。

f:id:daitasu:20210804154018p:plain

D&D(ドラッグ&ドロップ)の時間の調整

スマートフォンの場合、スクロール可能領域が狭いという問題があり、その原因の一つはD&Dが敏感に反応しすぎるという問題がありました。

他社アプリケーションの似たような機能を参考に、「タッチから並べ替え状態になるまでに若干の遅延があればいいのではないか」という仮説を立て、エンジニアがその実現方法を調査しました。

幸いなことに自分たちはD&Dをライブラリで実現していて、そのオプションを渡すことで簡単に実現できました。

具体的には直接はVue.Draggableを使っていて、その内部実装はSortable に依存しています。Sortableにはdelayというオプションがあるので、そのオプションに200を渡すことで時間は調整できました。

これにより、スクロールで意図せずにD&Dしてしまうという現象は減りました。

ちなみに、デザイナーがもっと詳細に検討してみたいということで、150ms、250msも試してみましたが、誰も違いを理解できませんでしたが、それ以上だと引っ掛かりを感じたということがありました。こうした実験を通した気づきはとても興味深かったです。

誤操作によるコンテキストメニューの開閉が起こりやすくなる

こちらはスクロール可能領域が狭い話と同じ原因で、画像の上をスクロールしていると、コンテキストメニューが意図せずに開いてしまうという現象が起きることがありました。

これが起きるのはコンテキストメニューの感度が良すぎる(=スクロールで反応する)ためでした。

タップイベントをクリックイベントに変更

当初、コンテキストメニューの開閉はtouchstartとtouchendのイベントで実装されていました。最初はイベントの時差を計算して処理の発火するように調整しましたが、あまり体験がよくないというフィードバックをもらいました。

デザイナーが他の箇所はclickイベントで実装しているので、そちらに合わせれるのではと指摘してくれて、それを試してみると、スクロールで反応するということがなくなりました。

一方でエンジニアがあえてtouchstartで実装することはないはずという懸念もあって、その歴史的な背景も念の為、調査しました。

結果、このボタンのようなアイコンはdivで実装されていて、特定のスマートフォンではdivにclickイベントをつけても発火せず、そのためtouchstartも代替でつけていたらしいことがわかりました。

ここは素直にbuttonタグを使って再実装するとイベントが無事発火しました。divよりもbuttonの方がアクセシブルということもあり、この改修で直せてよかったです。

画像枚数とは直接は関係ないが、並び替えのインタラクションが分かりづらいため、これも誤操作の原因になっている

これまでD&D(ドラッグ&ドロップ)のインタラクションのスタイルは特に調整しておらず、ライブラリのデフォルト設定に任せていました。

その結果、ドラッグした画像が反応しているか、ドラッグ先がどこかが分かりづらく、ユーザーが操作を迷う一因になっていました。

D&D(ドラッグ&ドロップ)のインタラクションの改善

この点も直すことで、D&Dが増えてきた時の体験がよくなるということで調整しました。

ドラッグした画像を僅かに薄くして、ドロップ先を灰色で調整するという細かいデザインの変更です。

f:id:daitasu:20210804154054p:plain

こちらもライブラリに頼ることで解決がすぐにできました。ドラッグした画像には sortable-drag 、ドロップ先には sortable-ghost というclassが付加されるのでそれを利用して、スタイルを変更します。

  .sortable-ghost {
    background-color: rgba(#235094, 0.05);
  }
  .sortable-drag {
    filter: opacity(0.7);
  }

学んだこと

これらの改修を通して得た気づきについて、以下にまとめてみました。フロントエンドで業務を遂行する上で、気をつけながら働いていきたいと思います。

  • フロントエンドは仕様通りに動くかだけでなく、ユーザーの体験が損なわれないかに注意する必要があり、起きた問題を技術で対応することが仕事の一つである
  • 体験を良くする改修を行うためには、ブラウザのAPI(HTML、CSSJavaScript)、ライブラリのAPIについて知っておく必要があり、なぜそのAPIが存在するかを考えておくと、役に立つ
    • 例えば、CSSfilteroverflow-anchor(今回の記事では取り上げていない)あたりをいつ使えばいいかを常に考えていたい
    • すでにあるAPIは先人が工夫してきた痕跡であることが多いため
  • アクセシビリティについて考えることは体験について考えるきっかけになる
    • 現状ではD&Dでしか順序を操作できないことはアクセシブルではなく、それが問題の一因になっている面もある
    • 機械的アクセシビリティだけでなく、様々なユーザーを考慮してデザイナーに提案したい

おわりに

今回は取り上げませんでしたが、管理画面だけでなくストア側も細かいデザインを修正して、アイテム画像件数上限がアップしても体験が損なわないように改修を何点か行っています。

今回のプロジェクトでは簡単に見える変更であっても、ユーザーの体験について深く考えなくてはいけないということを改めて、実感しました。体験の担保はフロントエンドの大きな責務の一つであると思うので、思慮不足を反省しています。

特に今回のような小さな体験の改修はユーザーから直接指摘されることは少なく、自分たちが責任を持ってこだわるべき箇所だと思います。今後は、その意識を強く持って、体験について考え、学んでいきたいと思います。