Help us understand the problem. What is going on with this article?

Vue.jsでギャラリーを実装してみる

こんにちは、どらです。
Advent Calendar、大遅刻してしまって本当にすみません…😥

今回は、以前制作した中で実装したiOS風のギャラリーを紹介したいと思います。

この記事で紹介するもの

今回実装するのは、下に横スクロールの画像リストがあり、上に大きく画像が表示されるiOS風(?)のギャラリーです。
サンプルページ
完成したギャラリー

なお、全体のソースコードは以下のリポジトリにて公開しています。

実装する上でのポイント

画像が絡むAPIを使いたいサンプルにはpixabayが便利

今回ギャラリーを作るにあたっては、pixabayのAPIを使用させていただいています。
Googleアカウントなどで新規登録をすれば、すぐにAPIキーがドキュメントに表示されるので大変ラクチンです。

複雑な認証等も必要ないので、すぐに使い始めることができます。

overflowさせるには、min-widthを使う

下の横スクロールする画像は、flexboxを用いて並べられています。
この時、普通にwidthflex-basisを使ってしまうと、overflowしないように縮められてしまい、うまくいきません。

これは、min-widthで幅を指定してあげれば回避することができます。

参考)横スクロールの実装でflexboxを使ったのでそのメモ。

選択されている画像が中央に来るようにする

http://localhost:3000/?id=3といったように、現在選択している画像をアドレス内に含め、そのアドレスを直接開いた時に該当画像を選択した状態になるように実装しています。

それにあたり、下部に表示する横スクロールリストも選択画像が中央に来るようにしています。これには、DOM操作が必要なため、ref属性を用いる必要があります。

<template>
  <div ref="container" :class="`${$options.name}__root`">
    <div
      v-for="(image, i) in images"
      :ref="`img-${i}`"
      ...
    >
      <v-lazy-image :src="image.previewURL" />
    </div>
  </div>
</template>

このようにrefを指定することで、

const imgRefs = this.$refs[`img-${idx}`][0]

とすると、DOMを直接取ることができます。

最終的には、以下のように実装することで所望の画像を中央に持ってくることができました。

getImagePos(idx: number) {
  const imgRefs = this.$refs[`img-${idx}`]
  if (Array.isArray(imgRefs) && imgRefs.length > 0) {
    const imgRef = imgRefs[0] as Element
    return imgRef.getBoundingClientRect()
  } else {
    return null
  }
},
moveToSelectedImg(idx: number) {
  // 画像のdivの位置を取得
  const selectedPos = this.getImagePos(idx)
  if (selectedPos === null) return

  // 横スクロールリストの箱自体の位置を取得
  const containerRef = this.$refs.container as Element
  const containerRect = containerRef.getBoundingClientRect()

  // リスト内での画像中央位置を計算
  // * leftはページ全体での座標なので、コンテナ内での相対座標にするためにコンテナの左端座標を引く
  const centerPos =
    selectedPos.left + selectedPos.width / 2 - containerRect.left
  // セットすべきscrollLeftを計算
  const scrollLeftPos = centerPos - containerRect.width / 2

  containerRef.scrollLeft = scrollLeftPos
}

// 移動したいタイミングで下を呼び出す
this.moveToSelectedImg(this.selected)

ただ、$refsで操作ができるのはDOMが構築された後なので、呼び出すタイミングを考えないとnullになりがちです。
今回は、imagesが渡された後に若干setTimeoutで待ちを入れて操作しています(本当は使わずになんとかしたい)

APIキーを環境変数に入れる

PixabyのAPIを利用するには、ログイン後に取得するAPIキーが必要です。ただ、あまりこのようなキーをリポジトリに含めて管理するのはあまり好ましくないので、環境変数を参照するようにしました。

Nuxt.jsには@nuxtjs/dotenvというパッケージがあり、こちらを利用することで、サーバーサイドでよくするように.envファイルから読み込むことができます。

導入は、特に特別な設定をしなければ、以下の設定を行うのみでOKです。

$ yarn add --dev @nuxtjs/dotenv # or npm install --save-dev @nuxtjs/dotenv
nuxt.config.js
export default {
  buildModules: [
    '@nuxtjs/dotenv',
  ]
}

.envファイルにはこのように記述します。

.env
PIXABAY_APIKEY=XXXXXX

Netlifyなど本番環境で環境変数として注入する場合は、nuxt.config.jsにさらに以下のように書く必要があります。

nuxt.config.js
export default {
  env: {
    PIXABAY_APIKEY: process.env.PIXABAY_APIKEY
  }
}

まとめ

以前、とあるプロジェクトの中でこのようなギャラリーを実装して個人的に満足していたので今回記事にしてみました。
データバインディングを超えて、より複雑なことをしたい場合にはrefsをうまく活用してみると良いですね。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away