Criando Diretivas de Rolagem

Exemplo Base

Há certos momentos em que podemos querer adicionar algum comportamento, especialmente animações, em um evento de rolagem em uma página. Há várias formas de fazer isso mas, possivelmente, o caminho com menos quantidade de código e dependências é usar uma diretiva personalizada para criar um gatilho para qualquer coisa que disparar um determinado evento de rolagem.

// Código da diretiva
Vue.directive('scroll', {
inserted: function (el, binding) {
let f = function (evt) {
if (binding.value(evt, el)) {
window.removeEventListener('scroll', f)
}
}
window.addEventListener('scroll', f)
}
})

// Código principal
new Vue({
el: '#app',
methods: {
handleScroll: function (evt, el) {
if (window.scrollY > 50) {
el.setAttribute(
'style',
'opacity: 1; transform: translate3d(0, -10px, 0)'
)
}
return window.scrollY > 100
}
}
})
<div id="app">
<h1 class="centered">Scroll me</h1>
<div
v-scroll="handleScroll"
class="box">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. A atque amet harum aut ab veritatis earum porro praesentium ut corporis. Quasi provident dolorem officia iure fugiat, eius mollitia sequi quisquam.</p>
</div>
</div>

Lembre-se! A diretiva deve ser registrada antes de instância Vue.

Precisamos também de uma propriedade de estilo que faça a transição dos valores intermediários aqui, neste caso:

.box {
transition: 1.5s all cubic-bezier(0.39, 0.575, 0.565, 1);
}

Veja o Pen Custom Scroll Directive- CSS Transition por Sarah Drasner (@sdras) no CodePen.

Ou, usando GreenSock (GSAP) ou qualquer outra biblioteca JavaScript de animação, o código se torna ainda mais simples:

Vue.directive('scroll', {
inserted: function (el, binding) {
let f = function (evt) {
if (binding.value(evt, el)) {
window.removeEventListener('scroll', f)
}
}
window.addEventListener('scroll', f)
}
})

new Vue({
el: '#app',
methods: {
handleScroll: function (evt, el) {
if (window.scrollY > 50) {
TweenMax.to(el, 1.5, {
y: -10,
opacity: 1,
ease: Sine.easeOut
})
}
return window.scrollY > 100
}
}
})

No entanto, removeremos a transição CSS da implementação anterior, pois agora será tratada com JavaScript.

O Benefício das Diretivas Personalizadas

O Vue é rico em opções para diretivas, a maioria das quais abrange casos de uso muito comuns, o que pode criar uma experiência muito produtiva para o desenvolvedor. Mas, mesmo se você tiver uma exceção à uma regra não coberta pelo framework, você também estará coberto neste caso, porque é fácil criar uma diretiva personalizada para atender às suas necessidades específicas.

Adicionar e remover escutas a eventos de rolagem para elementos é um bom caso de uso para essa técnica pois, assim como outras diretivas que usamos, estão necessariamente vinculadas ao elemento e, caso contrário, precisaríamos encontrar a referência para ele no DOM. Esse padrão evita a necessidade de varrer o DOM em busca do elemento, além de manter a lógica de eventos emparelhada com o nó ao qual ela está em referência.

Exemplo do Mundo Real: Usando Diretiva de Rolagem para Animações em Cascata

No decorrer da criação de um site coeso, você pode descobrir que está reutilizando o mesmo tipo de lógica de animação em várias áreas. Parece simples, então criaríamos uma diretiva personalizada específica, certo? Bem, normalmente, se você tentar reutilizá-la, acabaria precisando alterá-la apenas um pouquinho para cada uso.

Para ajudar a manter nosso código conciso e legível, poderíamos querer passar alguns argumentos pré-definidos, como o ponto inicial e final da animação, enquanto rolamos a página para baixo.

Este exemplo é melhor visualizado na versão em tela cheia.

Veja o Pen Scrolling Example- Using Custom Directives in Vue by Sarah Drasner (@sdras) on CodePen.

Na demonstração acima, cada uma das seções tem dois tipos diferentes de animação acionados a partir da rolagem: uma animação de metamorfose e uma animação de desenho, que anima os caminhos individuais no SVG. Nós reutilizamos essas duas animações, para que possamos criar uma diretiva personalizada para cada uma delas. Os argumentos que transmitiremos ajudarão a manter tudo simples e reutilizável.

Para mostrar como fazemos isso, vamos dar uma olhada no exemplo da metamorfose, onde precisaremos declarar o início e o fim, além de passar um valor de caminho para o qual transformaremos o gráfico vetorial. Estes argumentos são definidos como binding.value.algo:

Vue.directive('clipscroll', {
inserted: function (el, binding) {
let f = function (evt) {
var hasRun = false
if (!hasRun && window.scrollY > binding.value.start) {
hasRun = true
TweenMax.to(el, 2, {
morphSVG: binding.value.toPath,
ease: Sine.easeIn
})
}
if (window.scrollY > binding.value.end) {
window.removeEventListener('scroll', f)
}
}
window.addEventListener('scroll', f)
}
})

Nós podemos, então, usar esta animação em nosso template. Neste caso, estamos anexando a diretiva ao elemento clipPath, e passamos todos os nossos argumentos para a diretiva através de um objeto.

<clipPath id="clip-path">
<path
v-clipscroll="{ start: '50', end: '100', toPath: 'M0.39 0.34H15.99V22.44H0.39z' }"
id="poly-shapemorph"
d="M12.46 20.76L7.34 22.04 3.67 18.25 5.12 13.18 10.24 11.9 13.91 15.69 12.46 20.76z"
/>
</clipPath>

Padrões Alternativos

As diretivas personalizadas são extremamente úteis, mas você pode encontrar algumas situações em que precisa de algo muito específico, e que já existe em algumas bibliotecas de rolagem, não desejando reconstruir a partir do zero.

Scrollmagic tem um ecossistema muito rico em recursos, bem como uma boa documentação e demonstrações para explorar. Isso inclui, mas não está limitado a coisas como parallax, cascading pinning, section wipes, e responsive duration.