Categories
Quasar

Developing Vue Apps with the Quasar Library — Intersection Observer

Spread the love

Quasar is a popular Vue UI library for developing good looking Vue apps.

In this article, we’ll take a look at how to create Vue apps with the Quasar UI library.

Intersection Directive

We can watch for element visibility with Quasar’s wrapper on the Intersection Observer API.

For instance, we can write:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <style>
      .state {
        background: #ccc;
        font-size: 20px;
        color: gray;
        padding: 10px;
        opacity: 0.8;
      }

      .observed {
        width: 100%;
        font-size: 20px;
        color: #ccc;
        background: #282a37;
        padding: 10px;
      }

      .area {
        height: 300px;
      }

      .filler {
        height: 500px;
      }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <div class="relative-position">
        <div class="area q-pa-lg scroll">
          <div class="filler"></div>

          <div
            v-intersection="onIntersection"
            class="observed text-center rounded-borders"
          >
            Observed Element
          </div>

          <div class="filler"></div>
        </div>

        <div
          class="state rounded-borders text-center absolute-top q-mt-md q-ml-md q-mr-lg text-white"
          :class="visibleClass"
        >
          {{ visible === true ? 'Visible' : 'Hidden' }}
        </div>
      </div>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          visible: false
        },
        computed: {
          visibleClass() {
            return `bg-${this.visible ? "positive" : "negative"}`;
          }
        },
        methods: {
          onIntersection(entry) {
            this.visible = entry.isIntersecting;
          }
        }
      });
    </script>
  </body>
</html>

We add the v-intersection directive to run the onIntersection method went the intersection status changes.

We get the intersection status with the entry.isIntersecting property.

The handler will run when the div with the directive applied intersections the edge of the scroll container.

We add the visibleClass computed property to return the class to apply when the visible reactive property changes.

We can make the onIntersection method trigger only once with the once modifier.

Also, we can watch for the intersection percentage by referencing the entry.isIntersection property:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <style>
      .state {
        background: #ccc;
        font-size: 20px;
        color: gray;
        padding: 10px;
        opacity: 0.8;
      }

      .observed {
        width: 100%;
        font-size: 20px;
        color: #ccc;
        background: #282a37;
        padding: 10px;
      }

      .area {
        height: 300px;
      }

      .filler {
        height: 500px;
      }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <div class="relative-position">
        <div class="area q-pa-lg scroll">
          <div class="filler"></div>

<div
            v-intersection="onIntersection"
            class="observed text-center rounded-borders"
          >
            Observed Element
          </div>

          <div class="filler"></div>
        </div>

        <div
          class="state rounded-borders text-center absolute-top q-mt-md q-ml-md q-mr-lg text-white"
          :class="visibleClass"
        >
          Percent: {{ percent }}%
        </div>
      </div>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          visible: false,
          percent: 0
        },
        computed: {
          visibleClass() {
            return `bg-${this.visible ? "positive" : "negative"}`;
          }
        },
        methods: {
          onIntersection(entry) {
            this.visible = entry.isIntersecting;
            const percent = (entry.intersectionRatio * 100).toFixed(0);
            if (this.percent !== percent) {
              this.percent = percent;
            }
          }
        }
      });
    </script>
  </body>
</html>

Also, we can use it to render items that are displayed on the screen:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <style>
      .item {
        height: 200px;
        width: 200px;
      }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <div class="q-pa-md">
        <div class="row justify-center q-gutter-sm">
          <div
            v-for="index in inView.length"
            :key="index"
            :data-id="index - 1"
            class="item q-pa-sm flex flex-center relative-position"
            v-intersection="onIntersection"
          >
            <transition name="q-transition--scale">
              <q-card v-if="inView[index - 1]">
                <img src="https://cdn.quasar.dev/img/mountains.jpg" />

                <q-card-section>
                  <div class="text-h6">Card #{{ index }}</div>
                  <div class="text-subtitle2">by John Doe</div>
                </q-card-section>
              </q-card>
            </transition>
          </div>
        </div>
      </div>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          inView: Array(50)
            .fill()
            .map(() => false)
        },
        methods: {
          onIntersection(entry) {
            const index = +entry.target.dataset.id;
            setTimeout(() => {
              this.inView.splice(index, 1, entry.isIntersecting);
            }, 50);
          }
        }
      });
    </script>
  </body>
</html>

All we have to do is call splice to set the items that are displayed on the screen.

Conclusion

We can watch for intersections with Quasar’s wrapper for the Intersection Observer API.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *