Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display and transition when scroll position reaches element position in the screen?

Suppose this is my template:

<template>
    <div id="Test">
        <transition name="fade">
            <div class="row" id="RowOne">
                <p>Lorem ipsum dolor odit qui sit?</p>
            </div>
        </transition>
        <transition name="fade">
            <div class="row" id="RowTwo">
                <p>Lorem ipsum dolor sit amet, consectetur.</p>
            </div>
        </transition>
        <transition name="fade">
            <div class="row" id="RowThree">
                <p>Lorem ipsum dolor sit amet, tenetur!</p>
            </div>
        </transition>
    </div>
</template>

I want to display and animate RowOne, RowTwo and RowThree when it's in the displayed in the viewport, respectively. Like in the Laracasts website, the elements appear and animate when scroll position reaches the elements offset. Is it possible using Vue.js and javascript?

like image 914
Kakar Avatar asked Jul 22 '17 17:07

Kakar


People also ask

How do you get the scroll position element?

To get or set the scroll position of an element, you follow these steps: First, select the element using the selecting methods such as querySelector() . Second, access the scroll position of the element via the scrollLeft and scrollTop properties.


1 Answers

Here is how you can do it with a directive.

Vue.directive('vpshow', {
  inViewport (el) {
    var rect = el.getBoundingClientRect()
    return !(rect.bottom < 0 || rect.right < 0 || 
             rect.left > window.innerWidth ||
             rect.top > window.innerHeight)
  },

  bind(el, binding) {
    el.classList.add('before-enter')
    el.$onScroll = function() {
      if (binding.def.inViewport(el)) {
        el.classList.add('enter')
        el.classList.remove('before-enter')
        binding.def.unbind(el, binding)        
      }
    }
    document.addEventListener('scroll', el.$onScroll)
  },

  inserted(el, binding) {
    el.$onScroll()  
  },

  unbind(el, binding) {    
    document.removeEventListener('scroll', el.$onScroll)
    delete el.$onScroll
  }  
})

You will need to add v-vpshow directive to the elements you want to animate when they become visible in the viewport.

For example:

<div class="row" id="RowOne" v-vpshow>...</div>

This directive uses two classes.

1) before-enter: it hides the element by default and is added automatically when the directive is bound to the element.

2) enter: this one should contain the transition you want to apply when the element becomes visible.

v-vpshow will unbind itself automatically once the element has become visible (after triggering the animation) in the viewport removing any data and events listeners that were set on bind.

Here is a working example.

Vue.directive('vpshow', {
  inViewport (el) {
    var rect = el.getBoundingClientRect()
    return !(rect.bottom < 0 || rect.right < 0 || 
             rect.left > window.innerWidth ||
             rect.top > window.innerHeight)
  },
  
  bind(el, binding) {
    el.classList.add('before-enter')
    el.$onScroll = function() {
      if (binding.def.inViewport(el)) {
        el.classList.add('enter')
        el.classList.remove('before-enter')
        binding.def.unbind(el, binding)        
      }
    }
    document.addEventListener('scroll', el.$onScroll)
  },
  
  inserted(el, binding) {
    el.$onScroll()  
  },
  
  unbind(el, binding) {    
    document.removeEventListener('scroll', el.$onScroll)
    delete el.$onScroll
  }  
})

new Vue({
  el: '#app',  
})
/* v-vpshow classes */
.before-enter {
  opacity: 0;
}

.enter {
  transition: opacity 2s ease;  
}
/* ---------------- */



.row {  
  display: flex;
  min-height: 500px;
  justify-content: center;
  font-size: 20px;
  font-family: tahoma;
}

#RowOne {
  background-color: yellow;  
}

#RowTwo {
  background-color: #5D576B;
}

#RowThree {
  background-color: #F7567C;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>

<div id="app">
    <div id="Test">
        <div class="row" id="RowOne" v-vpshow>
            <p>Lorem ipsum dolor sit amet, consectetur.</p>
        </div>
        <div class="row" id="RowTwo" v-vpshow>
            <p>Lorem ipsum dolor sit amet, consectetur.</p>
        </div>
        <div class="row" id="RowThree" v-vpshow>
            <p>Lorem ipsum dolor sit amet, tenetur!</p>
        </div>
    </div>
</div>
like image 154
Ikbel Avatar answered Sep 19 '22 06:09

Ikbel