JSFiddle - React, Tailwind, and code Playground

Vue + intersection observers, mixin method

by Travis Almand

HTML

<div id="app">
  <section-a></section-a>
  <section-b></section-b>
  <section-b></section-b>
</div>

CSS

.full-height {
  background-color: gainsboro;
  min-height: 100vh;
}
.half-height {
  background-color: gray;
  border: 1px solid black;
  min-height: 50vh;
}


.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}

.fade-left-enter-active,
.fade-left-leave-active {
  position: absolute;
  transition: 0.5s;
}
.fade-left-enter,
.fade-left-leave-to {
  opacity: 0;
  transform: translate3d(100px, 0, 0);
}

Vue

console.clear();

const intersectionObserver = {
	props: {
    io_root: {
      type: String,
      default: null
    },
    io_rootMargin: {
      type: String,
      default: '0px'
    },
    io_threshold: {
      type: Array,
      default: function () {
       return [0, 0.25, 0.5, 0.75, 1];
      }
    }
  },
	data: function () {
  	return {
    	io_options: {
        root: this.io_root,
        rootMargin: this.io_rootMargin,
        threshold: this.io_threshold
      },
    	io_entries: null,
      io_observer: null,
      io_ratio_0: false,
      io_ratio_25: false,
      io_ratio_50: false,
      io_ratio_75: false,
      io_ratio_100: false
    }
  },
  methods: {
  	io_callback: function (entries, observer) {
    	this.io_entries = entries;
      this.io_observer = observer;
      this.io_ratio_0 = entries[0].intersectionRatio > 0;
      this.io_ratio_25 = entries[0].intersectionRatio > 0.25;
      this.io_ratio_50 = entries[0].intersectionRatio > 0.5;
      this.io_ratio_75 = entries[0].intersectionRatio > 0.75;
      this.io_ratio_100 = entries[0].intersectionRatio === 1;
    }
  },
  mounted: function () {
  	const observer = new IntersectionObserver(this.io_callback, this.io_options);
    observer.observe(this.$el);
  }
};

Vue.component('section-a', {
	mixins: [intersectionObserver],
  template: `
  	<div class="full-height">
    	section a
    </div>`
});

Vue.component('section-b', {
	mixins: [intersectionObserver],
  template: `
  	<div class="full-height">
      <transition name="fade">
      	<div v-if="io_ratio_0">greater than 0</div>
      </transition>
      <transition name="fade">
      	<div v-if="io_ratio_25">greater than 25</div>
      </transition>
      <transition name="fade-left">
      	<div v-if="io_ratio_50">greater than 50</div>
      </transition>
      <transition name="fade">
      	<div v-if="io_ratio_75">greater than 75</div>
      </transition>
      <transition name="fade">
      	<div v-if="io_ratio_100">at 100</div>
 ...