Elementos úteis
click Digital

ctrl + c ➥ ctrl + v

Footer créditos Click
(Captação do ano Automática)

Tutorial

  1. Copiar o elemento

    • Clique com o botão direito do mouse sobre o container créditos e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  •  

© Todos os direitos reservados | Desenvolvido por

Logo Click Digital

Card com efeito de glow no hover

LOREM IMPSUM

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.

Tutorial

  1. Copiar o card

    • Clique com o botão direito do mouse sobre ele e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do card
    O card é composto por 4 containers principais:

    • Card: controla o plano de fundo externo. A cor aqui representa a cor da borda.

    • Conteúdo: armazena todo o conteúdo interno. O plano de fundo define a cor geral do card.

      • Sempre utilize cores com opacidade de 0.8.

      • Para o estado hover (ao passar o mouse), defina a cor com opacidade de 0.6.

    • Blob: responsável pelo efeito de glow no hover. Você pode definir a cor livremente.

    • Fake Blob: responsável pelo efeito de glow no hover no código JavaScript. Não alterar!

  • Ativando o efeito Glow
    Para que o efeito funcione corretamente:

    • Adicione um bloco de código HTML no final da página.

    • Copie e cole o código ao lado.

				
					<script type = "text/javascript" >
  
    const cards = document.querySelectorAll(".card");

window.addEventListener("mousemove", (ev) => {

    cards.forEach((e) => {
        const blob = e.querySelector(".blob");
        const fblob = e.querySelector(".fakeblob");
        const rec = fblob.getBoundingClientRect();

        blob.animate(
            [{
                transform: `translate(${
            (ev.clientX - rec.left) - (rec.width / 2)
          }px,${(ev.clientY - rec.top) - (rec.height / 2)}px)`
            }], {
                duration: 300,
                fill: "forwards"
            }
        );

        blob.style.opacity = "1";

    });

});

</script>
				
			

Card com efeito de movimento no hover

LOREM IMPSUM

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.

Tutorial

  1. Copiar o card

    • Clique com o botão direito do mouse sobre ele e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do card
    O card pode ser customizado da forma que preferir internamente.

  3. Ativando o efeito de movimento
    Para que o efeito funcione corretamente:

    • Adicione um bloco de código HTML no final da página.

    • Copie e cole o código ao lado.

    • Todos os elementos que tiverem a classe tilt-card irão assumir o efeito
				
					<script src = "https://cdn.jsdelivr.net/npm/vanilla-tilt@1.8.1/dist/vanilla-tilt.min.js" defer>
</script> 
<script >
   document.addEventListener('DOMContentLoaded', function () {
      // Ativa tilt em tudo que tiver .tilt-card
      VanillaTilt.init(document.querySelectorAll(".tilt-card"), {
         max: 12,
         speed: 700,
         scale: 1.04,
         glare: true,
         "max-glare": 0.2
      });
   }); 
</script>
				
			

Card com efeito de borda animada

Tutorial

  1. Copiar o card

    • Clique com o botão direito do mouse sobre ele e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do card

    O card é composto por 2 containers principais:

    • Card: controla o plano de fundo geral e a borda.

      • A largura deve ser sempre Encaixotada em 100%.

      • A altura mínima precisa ser definida em um número par para que o efeito visual fique simétrico.

    • Conteúdo: armazena o conteúdo interno do card (texto, imagem, botão etc.). Você pode editar livremente.

    💡 Importante: Este card depende do CSS personalizado que está no container Card para gerar a borda animada. Para demais edições, manipular código que está devidamente comentado.

  •  

LOREM IMPSUM​

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.

BARRA ANIMADA

  • Click Digital
  • Click Digital
  • Click Digital
  • Click Digital

Tutorial

  1. Copiar o elemento

    • Clique com o botão direito do mouse sobre ele e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do card
    Faça a edição do elemento via código html que está devidamente comentado com variáveis para fácil personalização. OBS: Os ícones devem ser inseridos como códigos SVG.

Texto curvado com movimento

Tutorial

  1. Copiar o elemento

    • Clique com o botão direito do mouse sobre ele e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do elemento

    Faça a edição do texto no código html que está devidamente comentado. Para alteração de cores e tamanhos, clique no elemento html e vá em CSS personalizado, que também está devidamente comentado.

  3. Ativando o efeito de movimento

    Para que o efeito funcione corretamente:
    • Adicione um bloco de código HTML no final da página.

    • Copie e cole o código ao lado.

				
					<script>
document.addEventListener('DOMContentLoaded', function() {
  // Container raiz do componente (SVG + textos)
  const container = document.getElementById('curved-loop-container');
  if (!container) return;

  // ======== PROPRIEDADES (lidas dos atributos data-*) ========
  // Texto que será repetido ao longo do caminho
  const marqueeText = container.dataset.text || '';

  // Velocidade base do movimento (px por "tick" normalizado) — EDITA AQUI via data-speed
  let speed = parseFloat(container.dataset.speed) || 2; // Pixels por segundo (ajustado com um fator mais abaixo)

  // Intensidade da curvatura (altera o ponto de controle da curva Bézier)
  const curveAmount = parseFloat(container.dataset.curve) || 400;

  // Direção inicial do movimento ('left' | 'right')
  const direction = container.dataset.direction || 'left';

  // Habilita interação de arrastar para mudar offset/direção
  const interactive = container.dataset.interactive !== 'false';

  // Classe opcional para estilizar o <text> visível (usa data-classname)
  const className = container.dataset.classname || '';

  if (!marqueeText) return;

  // ======== PREPARO DO TEXTO ========
  // Garante um espaço não separável no fim para evitar "colagens" ao duplicar
  const hasTrailing = /\s|\u00A0$/.test(marqueeText);
  const text = (hasTrailing ? marqueeText.replace(/\s+$/, '') : marqueeText) + '\u00A0';

  // ======== ELEMENTOS DO SVG ========
  const measureText = container.querySelector('#measure-text'); // usado só para medir largura
  const curvedText = container.querySelector('#curved-text');   // <text> visível
  const textPath = container.querySelector('#text-path');       // <textPath> ligado ao caminho
  const path = container.querySelector('#curve-path');          // <path> da curva

  // ======== VARIÁVEIS DE ESTADO ========
  let spacing = 0;           // comprimento (px) do bloco "text" medido
  let offset = 0;            // deslocamento atual do startOffset
  let isReady = false;       // flag de “pronto para animar”
  let animationFrame = null; // id do requestAnimationFrame
  let isDragging = false;    // se o usuário está arrastando no momento
  let lastX = 0;             // posição X anterior no arraste
  let currentDirection = direction; // direção dinâmica (muda após arraste)
  let velocity = 0;          // velocidade do arraste (usada só para inferir direção final)
  let lastTime = performance.now(); // referência de tempo para animação baseada em tempo

  // Aplica classe custom ao <text> principal (para estilização via CSS)
  if (className) {
    curvedText.classList.add(className);
  }

  // ======== ATUALIZA A CURVA COM BASE EM curveAmount ========
  // O ponto de controle (Q) sobe/desce conforme curveAmount
  // EDITA AQUI: Se quiser mudar o caminho base, altere estas coordenadas
  const pathD = `M-100,40 Q500,${40 + curveAmount} 1540,40`;
  path.setAttribute('d', pathD);

  // ======== COMPRIMENTO APROXIMADO DO CAMINHO ========
  // Usado para decidir quantas repetições do texto precisamos colocar
  // EDITA AQUI: Se mudar o viewBox/path, este número pode mudar
  const PATH_LENGTH = 1800;

  // Mede o comprimento “em pixels” do texto (para saber quando repetir)
  function measureTextLength() {
    measureText.textContent = text;
    if (measureText.getComputedTextLength) {
      spacing = measureText.getComputedTextLength();
    } else {
      // Fallback aproximado (caso raro): multiplica por um fator médio de largura por char
      spacing = text.length * 18; // EDITA AQUI: ajuste este fator conforme a fonte usada
    }
    return spacing > 0 ? spacing : 100; // evita zero (que quebraria o loop)
  }

  // Duplica o texto o suficiente para cobrir o caminho e permitir looping sem “buracos”
  function getTotalText() {
    if (spacing <= 0) return text;
    const repeats = Math.ceil(PATH_LENGTH / spacing) + 3; // +3 para sobreposição/segurança
    return Array(repeats).fill(text).join('');
  }

  // ======== INICIALIZAÇÃO ========
  function init() {
    spacing = measureTextLength();
    if (spacing <= 0) {
      // Se a fonte ainda não carregou, tenta de novo (Elementor/webfonts podem atrasar)
      setTimeout(init, 50);
      return;
    }

    // Offset inicial: começa “um bloco” à esquerda para entrar deslizando
    offset = -spacing;
    textPath.setAttribute('startOffset', offset + 'px');
    textPath.textContent = getTotalText();
    isReady = true;
    container.style.visibility = 'visible';

    // Inicia a animação
    startAnimation();
  }

  // ======== LOOP DE ANIMAÇÃO (baseado em tempo, independente de FPS) ========
  function animate(currentTime) {
    if (!isDragging && isReady && textPath) {
      const elapsed = (currentTime - lastTime) / 1000; // segundos desde o último frame

      // Converte "speed" em deslocamento por frame usando elapsed
      // O multiplicador 60 dá uma “sensação base” de 60fps — EDITA AQUI (fator de sensibilidade)
      const delta = (currentDirection === 'right' ? speed : -speed) * elapsed * 60;

      let newOffset = offset + delta;

      // ======== WRAP CONTÍNUO ========
      // Quando o offset passa de um bloco de texto, reaplicamos o excedente
      const wrapPoint = spacing;
      if (newOffset <= -wrapPoint) {
        newOffset += wrapPoint;
      } else if (newOffset > 0) {
        newOffset -= wrapPoint;
      }

      textPath.setAttribute('startOffset', newOffset + 'px');
      offset = newOffset;
    }
    lastTime = currentTime;
    animationFrame = requestAnimationFrame(animate);
  }

  function startAnimation() {
    if (animationFrame) cancelAnimationFrame(animationFrame);
    lastTime = performance.now();
    animationFrame = requestAnimationFrame(animate);
  }

  // ======== INTERAÇÃO: ARRASTAR PARA MUDAR OFFSET/DIREÇÃO ========
  function onPointerDown(e) {
    if (!interactive) return;
    isDragging = true;
    lastX = e.clientX;
    velocity = 0;
    container.setPointerCapture(e.pointerId);
    container.classList.add('grabbing');
    container.classList.remove('grab');
    // Pausa a animação automática durante o arraste
    if (animationFrame) cancelAnimationFrame(animationFrame);
  }

  function onPointerMove(e) {
    if (!interactive || !isDragging || !textPath) return;
    const dx = e.clientX - lastX;
    lastX = e.clientX;
    velocity = dx; // usado apenas para inferir direção ao soltar

    let newOffset = offset + dx;

    // Wrap durante o arraste (mesma lógica do auto)
    const wrapPoint = spacing;
    if (newOffset <= -wrapPoint) {
      newOffset += wrapPoint;
    } else if (newOffset > 0) {
      newOffset -= wrapPoint;
    }

    textPath.setAttribute('startOffset', newOffset + 'px');
    offset = newOffset;
  }

  function endDrag() {
    if (!interactive) return;
    isDragging = false;

    // Direção pós-arraste baseada no “sinal” da última velocidade
    currentDirection = velocity > 0 ? 'right' : 'left';

    container.classList.remove('grabbing');
    if (interactive) container.classList.add('grab');

    // Retoma a animação automática com a nova direção
    startAnimation();
  }

  // ======== EVENTOS PRINCIPAIS (Pointer Events) ========
  if (interactive) {
    container.addEventListener('pointerdown', onPointerDown);
    container.addEventListener('pointermove', onPointerMove);
    container.addEventListener('pointerup', endDrag);
    container.addEventListener('pointerleave', endDrag);
    container.classList.add('grab'); // Cursor “mão” quando interativo
  }

  // ======== FALLBACK Mouse/Touch (para navegadores antigos) ========
  // Observação: tocamos nas mesmas funções, só adaptando o clientX
  const fallbackDown = (e) => {
    if (e.type === 'touchstart') e = { clientX: e.touches[0].clientX, pointerId: 0 };
    onPointerDown(e);
  };
  const fallbackUp = (e) => { if (e.type === 'touchend') velocity = 0; endDrag(); };

  container.addEventListener('mousedown', fallbackDown);
  container.addEventListener('mousemove', onPointerMove);
  container.addEventListener('mouseup', fallbackUp);
  container.addEventListener('mouseleave', endDrag);
  container.addEventListener('touchstart', fallbackDown, { passive: false });
  container.addEventListener('touchmove', onPointerMove, { passive: false });
  container.addEventListener('touchend', fallbackUp);

  // ======== CLEANUP quando o container for removido do DOM ========
  const observer = new MutationObserver(() => {
    if (!container.parentNode) {
      if (animationFrame) cancelAnimationFrame(animationFrame);
    }
  });
  observer.observe(container, { childList: true, subtree: true });

  // ======== DELAY DE INIT ========
  // Pequeno atraso ajuda quando fontes Web/Elementor ainda estão carregando
  // EDITA AQUI: se o texto aparecer tarde ou cedo, ajuste este delay
  setTimeout(init, 100);
});
</script>

				
			

TExto gradiente animado

Tutorial

  1. Copiar o elemento

    • Clique com o botão direito do mouse sobre ele e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do elemento
    Faça a edição do elemento via custom code css que está devidamente comentado com variáveis para fácil personalização.

Click DIgital

Círculo animado

Tutorial

  1. Copiar o elemento

    • Clique com o botão direito do mouse sobre ele e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do elemento

    Faça a edição do elemento normalmente no Elementor via código html, que está devidamente comentado.

  •  
Logo Click Digital

Botão com brilho e sombra animada

Tutorial

  1. Copiar o botão

    • Clique com o botão direito do mouse sobre ele e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do botão

    Faça a edição do botão normalmente no Elementor. Para controlar a cor da sombra e a largura da borda, acesse o CSS personalizado que está devidamente comentado.

  •  

Botão com sombra Pulsante

Tutorial

  1. Copiar o botão

    • Clique com o botão direito do mouse sobre ele e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do botão

    Faça a edição do botão normalmente no Elementor. Para controlar a cor da sombra e a largura da borda, acesse o CSS personalizado que está devidamente comentado.

  •  

Botão WhatsApp Customizado

Tutorial

  1. Copiar o botão

    • Clique com o botão direito do mouse sobre ele e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do botão

    Faça a edição do botão via código html que está devidamente comentado com variáveis para fácil personalização.

  •  
Entre em contato

Botão Customizado Com quadrado interno

Tutorial

  1. Copiar o botão

    • Clique com o botão direito do mouse sobre ele e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do botão

    Faça a edição do botão via elementor e código html.

  •  

EFEITO CARDS SOBREPOSTOS

Click Digital 1

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

Click Digital 2

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

Click Digital 3

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

Tutorial

  1. Copiar o container

    • Clique com o botão direito do mouse sobre o container e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do elemento

    Faça a edição dos cards normalmente dentro do elementor. Atente-se para não alterar as classes gerais dos containers que coordenam as animações.

  3. Ativando o efeito de movimento

    Para que o efeito funcione corretamente:

    • Adicione um bloco de código HTML no final da página.

    • Copie e cole o código ao lado.

				
					<!-- GSAP -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>

<script>
(() => {
  const cardsLeft = document.querySelectorAll(".card-left");
  const cardsRight = document.querySelectorAll(".card-right");
  const cardSup = document.querySelector(".card-sup");

  // Função para aplicar/remover blur no card-sup
  const applySupBlur = (apply) => {
    gsap.to(cardSup, {
      filter: apply ? "blur(6px)" : "blur(0px)",
      opacity: apply ? 0.7 : 1,
      duration: 0.8,
      ease: "power2.inOut"
    });
  };
  
  

  const setupCardMotion = (cards, direction) => {
    cards.forEach((card) => {
      gsap.set(card, { filter: "blur(6px)", opacity: 0.7 });

      const motion = gsap.timeline({ paused: true })
        .to(card, { x: direction * 250, duration: 1.5, ease: "power2.inOut" })
        .to(card, { x: direction * -50, duration: 1.5, ease: "power2.inOut" });

      const setZ = gsap.quickSetter(card, "zIndex");
      motion.eventCallback("onUpdate", () => {
        const prog = motion.progress();
        setZ(prog >= 0.5 ? 2 : 0);
      });

      const toEnd = () => {
        gsap.to(card, { filter: "blur(0px)", opacity: 1, duration: 0.8, ease: "power2.out" });
        gsap.to(motion, { progress: 1, duration: 1.5, ease: "power2.out", overwrite: "auto" });
        applySupBlur(true); // Aplica blur ao card-sup
      };

      const toStart = () => {
        gsap.to(card, { filter: "blur(6px)", opacity: 0.7, duration: 0.8, ease: "power2.inOut" });
        gsap.to(motion, { progress: 0, duration: 1.5, ease: "power2.inOut", overwrite: "auto" });
        applySupBlur(false); // Remove blur do card-sup
      };

      card.addEventListener("mouseenter", toEnd);
      card.addEventListener("mouseleave", toStart);
    });
  };

  // Configura os dois lados
  setupCardMotion(cardsLeft, -1);
  setupCardMotion(cardsRight, 1);
})();
</script>

<script>
(() => {
  const cards = document.querySelectorAll(".card-right");

  cards.forEach((card) => {
    // Define o blur inicial
    gsap.set(card, { filter: "blur(6px)", opacity: 0.7 });

    const motion = gsap.timeline({ paused: true })
      .to(card, { x: 250, duration: 1.5, ease: "power2.inOut" })
      .to(card, { x: -50, duration: 1.5, ease: "power2.inOut" });

    const setZ = gsap.quickSetter(card, "zIndex");
    motion.eventCallback("onUpdate", () => {
      const prog = motion.progress();
      setZ(prog >= 0.5 ? 2 : 0);
    });

    const toEnd = () => {
      gsap.to(card, { filter: "blur(0px)", opacity: 1, duration: 0.8, ease: "power2.out" });
      gsap.to(motion, { progress: 1, duration: 1.5, ease: "power2.out", overwrite: "auto" });
    };

    const toStart = () => {
      gsap.to(card, { filter: "blur(6px)", opacity: 0.7, duration: 0.8, ease: "power2.inOut" });
      gsap.to(motion, { progress: 0, duration: 1.5, ease: "power2.inOut", overwrite: "auto" });
    };

    card.addEventListener("mouseenter", toEnd);
    card.addEventListener("mouseleave", toStart);
  });
})();
</script>
				
			

EFEITO CARDS EM MOVIMENTO

Click Digital
Click Digital
Click Digital
Click Digital
Click Digital
Click Digital
Click Digital
Click Digital

Tutorial

  1. Copiar o container

    • Clique com o botão direito do mouse sobre o container e selecione Copiar.

    • Em sua página no Elementor, clique com o botão direito em qualquer área e escolha Colar de outro site.

  2. Estrutura do elemento

    Faça a edição dos cards dentro do código html. Nele você pode alterar os textos e os ícones. Para os ícones, deve ser sempre adicionado no formato de código svg.

  3. Ativando o efeito de movimento

    Para que o efeito funcione corretamente:

    • Adicione um bloco de código HTML no final da página.

    • Copie e cole o código ao lado.

				
					<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/Draggable.min.js"></script>

<script>
  // Duplica filhos até garantir loop perfeito
  function fillForLoop(row){
    const minWidth = row.clientWidth * 2;
    const original = Array.from(row.children);
    let total = row.scrollWidth;
    while (total < minWidth) {
      original.forEach(n => row.appendChild(n.cloneNode(true)));
      total = row.scrollWidth;
    }
  }

  function makeMarquee(row, { direction = 1, pxPerSec = 40 } = {}) {
    fillForLoop(row);

    // metade do conteúdo = 1 ciclo
    let cycle = row.scrollWidth / 2;
    let wrapX = gsap.utils.wrap(-cycle, 0);

    // estado independente do DOM (sem pausar o tween)
    const state = { pos: 0, offset: 0 }; // pos = animação base; offset = arraste
    const setX = gsap.quickSetter(row, "x", "px");

    // Tween contínuo: move "state.pos" e aplicamos wrap no onUpdate
    let tween = gsap.to(state, {
      pos: direction > 0 ? "+=" + cycle : "-=" + cycle,
      duration: cycle / pxPerSec,
      ease: "none",
      repeat: -1,
      onUpdate: () => setX( wrapX(state.pos + state.offset) )
    });

    // Draggable em um proxy (evita briga com transform do tween)
    const proxy = document.createElement("div");
    let startOffset = 0;

    let drag = Draggable.create(proxy, {
      type: "x",
      trigger: row,        // arraste diretamente na linha
      inertia: false,      // sem inércia (pode ligar se tiver plugin)
      onPress() {
        startOffset = state.offset;
      },
      onDrag() {
        state.offset = startOffset + this.x;
        // onUpdate do tween já atualiza o x via quickSetter
        setX( wrapX(state.pos + state.offset) );
      },
      onRelease() {
        // nada de pausar/retomar — o tween já está rodando
      }
    })[0];

    // Recalcula em resize sem “soluços”
    const refresh = () => {
      fillForLoop(row);
      cycle = row.scrollWidth / 2;
      wrapX = gsap.utils.wrap(-cycle, 0);
      // mantém visual: re-aplica x atual baseado no novo wrap
      setX( wrapX(state.pos + state.offset) );
      tween.duration(cycle / pxPerSec);
    };
    window.addEventListener("resize", gsap.delayedCall.bind(null, 0.2, refresh));

    return { tween, drag, refresh };
  }

  const rows = document.querySelectorAll('.container-topics');
  if (rows[0]) makeMarquee(rows[0], { direction: +1, pxPerSec: 40 });
  if (rows[1]) makeMarquee(rows[1], { direction: -1, pxPerSec: 40 });
</script>

				
			

EFEITO SCROLL LENTO

Tutorial

  1. Copiar o código

    • Copie o código ao lado.

    • Em sua página no Elementor, acesse as configurações, vá à aba Avançado e depois em CSS personalizado.Por fim, cole o código.

				
					<link rel="stylesheet" href="https://unpkg.com/lenis@1.1.18/dist/lenis.css">
<script defer src="https://unpkg.com/lenis@1.1.18/dist/lenis.min.js"></script>

<script>
    document.addEventListener('DOMContentLoaded', function () {
        const lenis = new Lenis();

        function raf(time) {
            lenis.raf(time);
            requestAnimationFrame(raf);
        }

        requestAnimationFrame(raf);
    });
</script>
				
			

Corretor de Bug de responsividade

Tutorial

  1. Copiar o código

    • Copie o código ao lado.

    • Em sua página no Elementor, acesse as configurações, vá à aba Avançado e depois em CSS personalizado.Por fim, cole o código.

				
					/* 1) Bloqueia apenas o scroll horizontal no nível da página */
html, body, .site, #page {
  overflow-x: hidden !important;
  overflow-y: visible !important; /* sem rolagem vertical interna estranha */
}

/* 5) Evita 100vw estourando com padding/border */
*:where([style*="100vw"]) {
  box-sizing: border-box;
  max-width: 100% !important;
}

				
			

Corretor de Tela Cheia automatica de vídeos + Autoplay apenas na dobra do vídeo

Tutorial

  1. Adicione um bloco de código HTML no final da página.

  2. Copie e cole o código ao lado.

				
					<script>
    (function() {
  // Helpers
  function qsa(root, sel) { return Array.prototype.slice.call(root.querySelectorAll(sel)); }
  function findPlayable(el) {
    if (!el) return null;
    if (el.tagName === 'VIDEO' || el.tagName === 'IFRAME') return el;
    return el.querySelector('video, iframe');
  }
  function getType(el) {
    if (!el) return null;
    if (el.tagName === 'VIDEO') return 'html5';
    const src = (el.getAttribute('src') || '').toLowerCase();
    if (src.includes('youtube.com/embed')) return 'youtube';
    if (src.includes('player.vimeo.com')) return 'vimeo';
    return null;
  }

  // API Loaders
  let ytReady = false, ytWaiting = [];
  function loadYT(cb) {
    if (ytReady) { cb(); return; }
    ytWaiting.push(cb);
    if (!window._ytLoading) {
      window._ytLoading = true;
      const s = document.createElement('script');
      s.src = 'https://www.youtube.com/iframe_api';
      document.head.appendChild(s);
      window.onYouTubeIframeAPIReady = () => {
        ytReady = true;
        ytWaiting.forEach(f => f()); ytWaiting = [];
      };
    }
  }
  function loadVimeo(cb) {
    if (window.Vimeo && window.Vimeo.Player) { cb(); return; }
    const s = document.createElement('script');
    s.src = 'https://player.vimeo.com/api/player.js';
    s.onload = cb;
    document.head.appendChild(s);
  }

  // Store and ready marker
  const store = new WeakMap();
  function markReady(el) { const o = store.get(el); if (o) o.ready = true; }

  function ensureAutoplayAllow(iframe) {
    const allow = (iframe.getAttribute('allow') || '').trim();
    if (!/autoplay/.test(allow)) {
      iframe.setAttribute('allow', (allow ? allow + '; ' : '') + 'autoplay');
    }
  }

  function addParam(src, param) {
    if (!src.includes(param)) {
      src += (src.includes('?') ? '&' : '?') + param;
    }
    return src;
  }

  function setup(elOrContainer) {
    const el = findPlayable(elOrContainer);
    if (!el) return;
    const type = getType(el);
    if (!type) return;

    if (type === 'html5') {
      el.setAttribute('playsinline', '');
      el.setAttribute('webkit-playsinline', '');
      el.muted = true;
      store.set(el, { ready: true, play: () => el.play().catch(() => {}), pause: () => el.pause() });
    } else if (type === 'youtube') {
      ensureAutoplayAllow(el);
      let src = el.getAttribute('src') || '';
      src = addParam(src, 'enablejsapi=1');
      src = addParam(src, 'playsinline=1');
      el.setAttribute('src', src);

      loadYT(() => {
        const id = el.id || ('yt_' + Math.random().toString(36).slice(2));
        el.id = id;
        const player = new YT.Player(id, {
          playerVars: { playsinline: 1 },
          events: { onReady: () => { try { player.mute(); } catch (e) {} markReady(el); } }
        });
        store.set(el, {
          ready: false,
          play: () => { try { player.playVideo(); } catch (e) {} },
          pause: () => { try { player.pauseVideo(); } catch (e) {} }
        });
      });
    } else if (type === 'vimeo') {
      ensureAutoplayAllow(el);
      let src = el.getAttribute('src') || '';
      src = addParam(src, 'muted=1');
      src = addParam(src, 'playsinline=1');
      el.setAttribute('src', src);

      loadVimeo(() => {
        const player = new Vimeo.Player(el);
        player.setMuted(true).catch(() => {});
        player.ready().then(() => markReady(el));
        store.set(el, {
          ready: false,
          play: () => player.play().catch(() => {}),
          pause: () => player.pause().catch(() => {})
        });
      });
    }

    observe(el);
  }

  // IntersectionObserver
  let io;
  function getObserver() {
    if (io) return io;
    io = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        const el = entry.target;
        const api = store.get(el);
        if (!api) return;
        const visible = entry.isIntersecting && entry.intersectionRatio >= 0.35;
        if (visible) {
          if (api.ready) api.play();
          else {
            const int = setInterval(() => {
              if (api.ready) { api.play(); clearInterval(int); }
            }, 120);
            setTimeout(() => clearInterval(int), 4000);
          }
        } else {
          api.pause();
        }
      });
    }, {
      threshold: [0, 0.2, 0.35, 0.6, 1],
      rootMargin: '-10% 0px -10% 0px'
    });
    return io;
  }
  function observe(el) { getObserver().observe(el); }

  function init() {
    qsa(document, '.js-autoplay-on-view').forEach(node => {
      const playable = findPlayable(node);
      if (playable) setup(node);
    });
    document.addEventListener('DOMContentLoaded', rescan, { once: true });
    setTimeout(rescan, 1200);
  }

  function rescan() {
    qsa(document, '.js-autoplay-on-view').forEach(node => {
      const playable = findPlayable(node);
      if (playable && !store.get(playable)) setup(node);
    });
  }

  if (document.readyState !== 'loading') init();
  else document.addEventListener('DOMContentLoaded', init);

  // Block programmatic fullscreen
  document.addEventListener('fullscreenchange', () => {}, true);
})();

</script>