Vuejs v-viewer/Viewer.js Dynamic Load More

Dec 3, 2019

Behaviour

  • The page will show 4 images (out of 6 images)
  • When click on image to launch the image viewer, the navbar/gallery will show all 6 images.

v-viewer/Viewer.js doesn't support dynamic image loading via API methods, thus we need to manipulate the img DOM by loading extra images as hidden img.

The reason I don't preload the extra images as hidden img is to avoid browser loading the hidden images (unless user click to view image).

Solution 1: using v-viewer

<template>  <div>    <div class="viewer" v-viewer="viewerOptions">      <img v-for="image in visibleImages" :src="image.src" :key="image.src" :data-href="image.href" :alt="image.alt">      <img v-for="image in hiddenImages" :src="image.src" :key="image.src" :data-href="image.href" :alt="image.alt" style="display: none;">    </div>  </div></template><script>export default {  data() {    return {      renderHiddenMore: false,      viewerOptions: {        movable: false,        rotatable: false,        scalable: false,        url: 'data-href',        title(image, imageData) {          // if want to show blank title, must use alt=" "          return image.alt        },        show: () => {          if (!this.renderHiddenMore) {            console.log('load more')            this.renderHiddenMore = true            // delay hack to get the new viewer object is being generated            setTimeout(() => {              console.log('launch viewer')              const viewer = this.$el.querySelector('.viewer').$viewer              viewer.show()            }, 500);          }        }      },      images: [        { src: 'https://picsum.photos/id/1/150/150', href: 'https://picsum.photos/id/1/800/800', alt: ' ' },        { src: 'https://picsum.photos/id/2/150/150', href: 'https://picsum.photos/id/2/800/800', alt: ' ' },        { src: 'https://picsum.photos/id/3/150/150', href: 'https://picsum.photos/id/3/800/800', alt: ' ' },        { src: 'https://picsum.photos/id/4/150/150', href: 'https://picsum.photos/id/4/800/800', alt: ' ' },        { src: 'https://picsum.photos/id/15/150/150', href: 'https://picsum.photos/id/15/800/800', alt: ' ' },        { src: 'https://picsum.photos/id/16/150/150', href: 'https://picsum.photos/id/16/800/800', alt: ' ' }      ]    }  },  computed: {    visibleImages() {      return this.images.slice(0, 4)    },    hiddenImages() {      return this.renderHiddenMore ? this.images.slice(4) : []    }  }}</script>

Disadvantage

  • Slightly slower because v-viewer is initially loaded with 4 images, then itercepted using show event to be reinitialized with additional images before being shown
  • The delay hack to get the new viewer object might be unreliable

Solution 2: using Viewer.js directly

<template>  <div>    <div class="viewer">      <img v-for="(image, index) in visibleImages" :src="image.src" :key="image.src" :data-href="image.href" :alt="image.alt" @click="showImage(index)">      <img v-for="image in hiddenImages" :src="image.src" :key="image.src" :data-href="image.href" :alt="image.alt" style="display: none;">    </div>  </div></template><script>import Viewer from 'viewerjs'export default {  data() {    return {      renderHiddenMore: false,      viewerOptions: {        movable: false,        rotatable: false,        scalable: false,        url: 'data-href',        title(image, imageData) {          // if want to show blank title, must use alt=" "          return image.alt        },        $viewer: null      },      images: [        { src: 'https://picsum.photos/id/1/150/150', href: 'https://picsum.photos/id/1/800/800', alt: ' ' },        { src: 'https://picsum.photos/id/2/150/150', href: 'https://picsum.photos/id/2/800/800', alt: ' ' },        { src: 'https://picsum.photos/id/3/150/150', href: 'https://picsum.photos/id/3/800/800', alt: ' ' },        { src: 'https://picsum.photos/id/4/150/150', href: 'https://picsum.photos/id/4/800/800', alt: ' ' },        { src: 'https://picsum.photos/id/15/150/150', href: 'https://picsum.photos/id/15/800/800', alt: ' ' },        { src: 'https://picsum.photos/id/16/150/150', href: 'https://picsum.photos/id/16/800/800', alt: ' ' }      ]    }  },  computed: {    visibleImages() {      return this.images.slice(0, 4)    },    hiddenImages() {      return this.renderHiddenMore ? this.images.slice(4) : []    }  },  methods: {    showImage(index) {      if (!this.renderHiddenMore) {        this.renderHiddenMore = true        this.$nextTick(() => {          console.log('create viewer')          this.$viewer = new Viewer(this.$el.querySelector('.viewer'), this.viewerOptions)          // this.$viewer.show()          this.$viewer.view(index)        })      }      else {        this.$viewer.view(index)      }    }  },}</script>

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.