# Como usar scroll-driven animations em direções opostas

> Como fazer colunas se moverem em direções opostas durante a rolagem com scroll-driven animations, animation-timeline e a função view() do CSS.

URL: https://dpw.dev/css/scroll-driven-animations-direcoes-opostas/  
Publicado em: 2026-08-20  
Categoria: css  
Tags: animacao-css, motion-ui  
Autor: Tárcio Zemel

Colunas de itens que deslizam em direções opostas conforme a página é rolada, desaparecendo suavemente ao cruzar as bordas do container. Parece trabalho para uma biblioteca JavaScript, mas as **scroll-driven animations** entregam o efeito inteiro com CSS puro e bem menos código do que se imagina.

> Artigo baseado em [Using Scroll-Driven Animations for Opposing Scroll Directions](https://css-tricks.com/scroll-driven-animations-opposing-scroll-directions/).

[Exemplo no CodePen](https://codepen.io/anon/pen/019dcdfc-1e41-77c8-afdf-810ebc6f2480)

Às vezes, designers têm ideias meio bobas que acabam conquistando quem precisa implementá-las. Não é tão difícil quanto parece, graças a recursos modernos do CSS, especificamente as <a href="https://css-tricks.com/scroll-driven-scroll-triggered-scroll-states-and-view-transitions/" target="_blank" rel="noopener noreferrer">scroll-driven animations</a>.

Se os conceitos básicos de `@keyframes` e `animation` ainda forem novidade, vale começar pela <a href="/css/animacoes-css-introducao/" target="_blank">introdução a animações CSS</a>.

## O HTML

O HTML é formado por um elemento pai (`.opposing-columns`), seus filhos (`.opposing-column`) e os filhos desses filhos (`.opposing-item`):

```html
<div class="opposing-columns">
  <!-- Coluna 1 -->
  <div class="opposing-column">
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
  </div>
  <!-- Coluna 2 -->
  <div class="opposing-column">
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
  </div>
  <!-- Coluna 3 -->
  <div class="opposing-column">
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
    <div class="opposing-item">...</div>
  </div>
</div>
```

É tudo o que a marcação precisa. O CSS faz o resto!

## Estilizando o container pai

Para começar, o efeito será aplicado apenas em telas maiores. Não faz muito sentido dar suporte a algo assim em telas pequenas, já que o espaço extra é justamente o que torna o efeito possível.

```css
/* Somente em telas maiores */
@media screen and (width >= 50rem) {
  .opposing-columns {
    display: flex;
    gap: 2rem;
    max-inline-size: min(90dvi, 50rem);
    margin-inline: auto;
  }
}
```

## Montando o efeito de "máscara"

Ainda faltam alguns ajustes no container pai para criar a ilusão de que os itens de cada `.opposing-column` desaparecem ao passar por ele. Os itens das colunas externas sobem durante a rolagem e os da coluna central descem. Ao cruzar os limites do elemento pai, eles precisam sumir gradualmente.

O primeiro passo é definir uma variável de cor de fundo no documento inteiro:

```css
@media screen and (width >= 50rem) {
  :root {
    --opposing-bg: lightcyan;
    background-color: var(--opposing-bg);
  }

  .opposing-columns 
}
```

O segundo passo é aplicar essa mesma cor de fundo nos pseudo-elementos `:before` e `:after` do elemento pai:

```css
@media screen and (width >= 50rem) {
  :root {
    --opposing-bg: lightcyan;
    background-color: var(--opposing-bg);
  }

  .opposing-columns 

  .opposing-columns 

      &:before {
        background-image: linear-gradient(
          to bottom,
          var(--opposing-bg) var(--opposing-mask),
          transparent
        );
        inset-block-start: calc(var(--opposing-mask) * -1);
      }

      &:after {
        background-image: linear-gradient(
          to top,
          var(--opposing-bg) var(--opposing-mask),
          transparent
        );
        inset-block-end: calc(var(--opposing-mask) * -1);
      }
    }
  }
}
```

## O layout das colunas

Antes da mágica, é preciso organizar os itens de cada coluna. Cada coluna é um flex item dentro do elemento pai, que é um flex container. Elas podem encolher (`flex-shrink: 1`) e crescer (`flex-grow: 1`), com o tamanho limitado a um certo ponto (`flex-basis: 10rem`).

Tudo isso cabe na propriedade abreviada `flex`:

```css
@media screen and (width >= 50rem) 
}
```

…e aplicá-las a cada coluna:

```css
@media screen and (width >= 50rem) 

  :where(.opposing-column:nth-of-type(1)) {
    animation-name: var(--animation-1);
  }

  :where(.opposing-column:nth-of-type(2)) {
    animation-name: var(--animation-2);
  }

  :where(.opposing-column:nth-of-type(3)) {
    animation-name: var(--animation-3);
  }
}
```

Já que se está mexendo nisso, vale desabilitar as animações para <a href="https://css-tricks.com/almanac/rules/m/media/prefers-reduced-motion/" target="_blank" rel="noopener noreferrer">respeitar as configurações de redução de movimento</a> (e remover a máscara, que sem o movimento pode ficar estranha):

```css
@media (prefers-reduced-motion: reduce) {
  .opposing-column {
    animation: unset;

    &:before,
    &:after {
      content: unset;
    }
  }
}
```

## Conclusão

As **scroll-driven animations** são realmente muito interessantes. O suporte no Firefox ainda estava pendente quando o artigo original foi escrito, mas dá para contornar isso com `@supports`.

Assim, é possível oferecer a experiência padrão com as anotações de scroll e um fallback para navegadores sem suporte, como rodar em uma timeline de animação normal:

```css
@supports (animation-timeline: view()) 
```

E isso é só a ponta do iceberg do que as scroll-driven animations podem fazer. Para outros efeitos ligados à rolagem sem uma linha de JavaScript, vale ver também <a href="/css/navegacao-ancora-smooth-scroll-css-puro/" target="_blank">navegação âncora e smooth scroll com CSS puro</a> e as <a href="/css/7-receitas-de-view-transitions/" target="_blank">receitas de View Transitions</a>.

Que tipo de coisa você já criou ou testou com scroll-driven animations? Ou abordaria esse caso de outro jeito?

---

Fonte: [dpw - desenvolvimento para web](https://dpw.dev/) (pt-BR).
Guia para agentes: https://dpw.dev/llms.txt
Índice completo: https://dpw.dev/sitemap-index.xml
Feed: https://dpw.dev/rss.xml
