
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、このところこのブログではユーザービリティを向上するための記事を公開していますが、その流れもあって図書館でいくつかのユーザビリティの本をひと通り見てみることにしました。
そして、その本にはいろいろと書かれていたのですが、中でも一番「たしかに!」と思ったことがありました。
それが、良いユーザビリティの例として表示されていた、
「Googleフォト」の選択機能
です。
↓↓↓ こんなカンジですね。
これは、ひと目みるだけで「今どの画像が選択中なのか」がとても分かりやすいデザインだと思うんです。しかも、逆の「どれが選択されていないか」も分かりやすいですよね。
今まで何気なく使っていましたが、だからこそ「考えずに使える、つまり良いユーザビリティ」ってことですね。
そこで!
今回は、Vue
を使ってこの「Googleフォト」のような選択機能を実装してみたいと思います。(ちなみに選択機能だけじゃなく、さっきの画像上部にある「選択した時だけ表示される削除ボタン」もつくってみます)
ぜひ皆さんのお役に立てると嬉しいです
開発環境: Vue 2.6
目次 [非表示]
やりたいこと
ということで、今回実装したい内容は次のとおりです。
- 画像を正方形で並べて表示する
- 画像をクリックしたら選択状態になって表示が変わる
- 選択された画像をクリックしたら、選択されていない状態にもどる
- 画像が選択された時だけ削除ボタンを表示する
では、今回も順を追って見ていきましょう!
Vueの基本形をつくる
では、これから画像を表示して選択機能をつくっていくための基本になるHTML
&JavaScript
を用意します。
<html>
<head>
</head>
<body>
<div id="app">
<!-- ここにコンテンツをつくる -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
<script>
new Vue({
el: '#app'
});
</script>
</body>
</html>
画像を並べて表示できるようにする
次に、今回テストとして表示する以下の画像5つを「きれいに正方形で」表示できるようにしていきましょう。(ちなみにこの画像を私が最近美味しいと思った食べ物・飲み物です)
では、画像のURLをdata
に追加します。
new Vue({
el: '#app',
data: {
imageUrls: [
'/images/yummy/1.jpg',
'/images/yummy/2.jpg',
'/images/yummy/3.jpg',
'/images/yummy/4.jpg',
'/images/yummy/5.jpg'
]
}
});
そして次に、それらのURLをループで表示するようにします。
<div class="selectable-box active" v-for="(imageUrl,index) in imageUrls">
<img :src="imageUrl">
</div>
画像は大きさも縦長・横長もバラバラなのでCSS
を追加しておきます。(object-fit:cover;
を使えばキレイにフィットさせてくれるんですね)
<html>
<head>
<style>
.selectable-box {
float: left;
width: 150px;
height: 150px;
margin: 5px;
background: #e8f0fd;
position: relative;
}
.active {
width: 120px;
height: 120px;
padding: 15px;
}
.selectable-box img {
width:100%;
height:100%;
object-fit:cover;
}
</style>
</head>
<!-- 以下省略 -->
実際に表示したものがこちらです。
そして、選択されたとき用のactive
をつけたバージョンはこちら。(ただし、まだアイコンはついていません)
画像をクリックしたら「選択/未選択」を切り替える
では続いて、どの画像が選択されているかが分かるようにselectedIndexes
という変数をつくって、画像クリック時に中身を変更できるようにしていきましょう。
まずはHTML側です。
画像にクリック・イベントをつけます。
<div class="selectable-box" v-for="(imageUrl,index) in imageUrls">
<img :src="imageUrl" @click="onImageSelect(index)">
</div>
そして、必要な変数とメソッドは以下のようになります。
new Vue({
el: '#app',
data: {
// 省略
selectedIndexes: []
},
methods: {
isSelected(index) {
return (this.selectedIndexes.includes(index));
},
onImageSelect(imageIndex) {
if(this.isSelected(imageIndex)) { // 選択されている
// 選択された画像「以外」だけ残す
this.selectedIndexes = this.selectedIndexes.filter((selectedIndex) => {
return (selectedIndex !== imageIndex);
});
} else {
this.selectedIndexes.push(imageIndex);
}
}
}
});
ちなみにisSelected()
は、画像が選択されているかが分かるメソッドですが、別の場所でも使うので専用メソッドとしてコードを分けました。
選択された画像だけ表示を変更する
では、selectedIndexes
の中に番号が入っている画像だけ表示を変更していきます。
画像のCSSを切り替える
まず先ほどつくったCSS
を選択状態で切り替えます。(実際には.active
をつける/つけないという動作になります)
HTMLに:class
をつけてVueで切り替えできるようにしましょう。
<div :class="imageCss(index)" v-for="(imageUrl,index) in imageUrls">
<img :src="imageUrl" @click="onImageSelect(index)">
</div>
そしてメソッドです。
methods: {
// 省略
imageCss(imageIndex) {
let classes = ['selectable-box'];
if(this.isSelected(imageIndex)) {
classes.push('active'); // 選択中なら "active" を追加
}
return classes;
},
// 省略
}
チェックアイコンをつける
続いて、より選択状態を分かりやすくするためにチェックアイコンをつけましょう。
なお、アイコンには絵文字を使おうかと思ったのですが、ブラウザ間の違いが大きかったので、今回はFont Awesome
を使います。
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.css">
<style>
// 省略
.selectable-box i {
position: absolute;
left: 10px;
top: 10px;
font-size: 25px;
color: #2B6FE7;
background-color: #fff;
border-radius: 50%;
}
</style>
</head>
<body>
<div id="app">
<h1>選択機能をつけた画像ギャラリー</h1>
<div :class="imageCss(index)" v-for="(imageUrl,index) in imageUrls">
<i class="fa fa-check-circle icon" v-if="isSelected(index)"></i>
<img :src="imageUrl" @click="onImageSelect(index)">
</div>
ここでは、isSelected()
を使って画像が選択されていればアイコンを表示し、そうでなければ非表示にしています。
そして、Font Awesome
はそのままでは透過しているので背景色を追加しているのですが、これだと背景が四角になってしまうのでborder-radius
をつけています。
では、今の状態でどのようになるか見てみましょう。
思った以上にキレイにできました
画像が選択されたら削除ボタンなどのコンテンツを表示する
では最後に画像が選択されたらページの一番下に以下のようなコンテンツを表示するようにしてみましょう。
- 削除ボタン
- 選択解除ボタン
- いまどれだけ選択しているか
まずはHTML側です。
// 省略
<body>
<div id="app">
// 省略
<div style="width:100%;position:fixed;bottom:0;left:0;background:#ddd;" v-if="selectedIndexes.length">
<div style="padding:15px;">
<button @click="clear"><i class="fa fa-times"></i> 選択解除</button>
<span style="padding-left:30px;" v-text="selectedIndexes.length"></span> 件
<button style="margin-left:30px;" @click="close"><i class="fa fa-trash-alt"></i> 削除</button>
</div>
</div>
そしてJavaScript側です。
new Vue({
// 省略
methods: {
// 省略
clear() {
this.selectedIndexes = [];
},
close() {
// ここにAjaxなどで削除
// テスト用コード
let newImageUrls = [];
for(let i = 0 ; i < this.imageUrls.length ; i++) {
if(!this.isSelected(i)) {
newImageUrls.push(this.imageUrls[i]);
}
}
this.imageUrls = newImageUrls;
this.selectedIndexes = [];
alert('削除が完了しました。');
},
// 省略
これで画像が選択されると以下のようなコンテンツが画面一番下に表示されることになります。
テストしてみる
では、実際にテストしてみた動画をご覧ください。
お疲れさまでした!
ソースコード一式をダウンロードする
今回実際に開発したソースコード一式を以下からダウンロードすることができます。
※ CDNを使っているので展開したらすぐ実行できますよ!
【Vue】Google フォトのような画像選択できる機能おわりに
ということで今回は「Google フォト」のような選択機能がある画像ギャラリーをつくってみました。
開発して実際に使ってみましたが、クリックして画像を選択するのが小気味いい気持ちになりました。さすがはGoogleさんといったところですね!
ちなみに今回CSS 3
のobject-fit
を初めて使ってみましたがすごく便利ですよね、コレ。私も今後の開発に使っていこうと思っています。
ただし、can i useで調べてみるとやはりというかIEさんはサポートしていませんでしたし、Edgeは部分サポートとなっていました。マイクロソフトさん…
ただ、最近はIEはサポート外でOKの案件が多くなってきてますし、Edge
もChromium
ベースになるので次期バージョンからはフルにサポートしてくれることになります。やっと時代が変わってきた感じがしますね。
ぜひ皆さんも同じようなコンテンツに挑戦してみてくださいね。
ではでは〜!