Efecto de falla en texto / imágenes / SVG | Programar Plus

Glitch de Lucas Bebber es un efecto súper genial. Es como si estuviera viendo un texto que se muestra en un monitor de escaneo progresivo que se ha dejado caer al suelo demasiadas veces y, por lo tanto, la alineación de los píxeles está desfasada en cantidades de tiempo y espacio extrañamente desiguales.

¡Es un truco CSS genuino si alguna vez hubo uno! Me tomó un poco descubrir cómo estaba funcionando, así que pensé en explicarlo. Luego terminé haciéndolo funcionar para otros tipos de contenido, además de convertirlo en un grupo de Sass @mixins para que trabajar con él sea un poco más fácil.

Ver la pluma
Texto con fallas en CSS por Chris Coyier (@chriscoyier)
en CodePen.

Tres copias del texto

Mientras que el HTML es solo:

<div class="glitch" data-text="GLITCH">GLITCH</div> 

Se crean tres copias, a través de pseudoelementos, y se colocan uno encima del otro.

.glitch {
  position: relative;
}
.glitch::before,
.glitch::after {
  content: attr(data-text);
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

Produciendo tres copias únicas que se pueden controlar individualmente:

Alterar las copias

Cada copia es idéntica excepto que:

  • Está desplazado hacia la izquierda o hacia la derecha.
  • Tiene un resaltado, en forma de sombra de texto.

Entre el offset y el realce, le da esa sensación rota.

.glitch::before {
  /* ... anything needed to make it identical */

  /* variation */
  left: 2px;
  text-shadow: -1px 0 red;
  
  /* important: opaque background masks the original */
  background: black;
}
.glitch::after {
  /* ... anything needed to make it identical */

  /* variation */
  left: -2px;
  text-shadow: -1px 0 blue;
  
  /* important: opaque background masks the original */
  background: black;
}

Entonces ahora las tres copias son así:

Recortando las copias

Solo verá la copia superior si se deja como está. Probablemente el ::after versión, a menos que modifique con z-index. Pero no temas, solo vamos a revelar partes de la parte superior, copias alteradas con el clip propiedad. Esta propiedad aparentemente está en desuso a favor de clip-path, pero en el momento de escribir este artículo, solo clip estaba trabajando para mí. Estoy seguro de que eso cambiará con el tiempo, así que tendremos que vigilarlo y, presumiblemente, Autoprefixer se encargará de ello.

¡Actualizar! Agosto de 2019: ha pasado mucho tiempo y clip todavía se admite en general, pero en desuso a favor de la (mejor) clip-path. El clip la sintaxis puede hacer lo que el inset() el valor de la función es para clip-path, así que estoy actualizando este artículo para usarlo. No parece que Autoprefixer se ocupe de la conversación.

La sintaxis de clip es un poco raro. Para los cuatro valores, puede esperar algo como arriba / izquierda / ancho / alto, o punto-arriba-izquierda / punto-abajo-derecha. Pero en cambio, es como margen y relleno: arriba / derecha / abajo / izquierda

.glitch::before {
  clip: rect(44px, 450px, 56px, 0);
  /*
    Essentially a box from 0, 44px
    to 450px, 56px
  */

  /* clip-path: inset(); needs how far you want to push in from the edges instead */
}

Aquí hay algunos clips de ejemplo en esas capas, ahora con fondos completamente opacos aplicados (pero aún rotados y con colores adicionales para que pueda ver lo que está sucediendo):

Animar los clips

Resulta que el clip se puede animar, por lo que ese cuadro recortado se animará a una nueva posición con el tiempo si se le indica. Aquí hay un ejemplo de fotogramas clave:

@keyframes noise-anim {
  0% {
    clip-path: inset(40% 0 61% 0);
  }
  20% {
    clip-path: inset(92% 0 1% 0);
  }
  40% {
    clip-path: inset(43% 0 1% 0);
  }
  60% {
    clip-path: inset(25% 0 58% 0);
  }
  80% {
    clip-path: inset(54% 0 7% 0);
  }
  100% {
    clip-path: inset(58% 0 43% 0);
  }
}

Observe que los valores izquierdo y derecho siguen siendo los mismos, solo cambian la parte superior e inferior. Y esos valores son un poco aleatorios.

Puede generar eso con bastante facilidad con Sass, como:

@keyframes noise-anim {
  $steps: 20;
  @for $i from 0 through $steps {
    #{percentage($i*(1/$steps))} {
      $top: random(100);
      $bottom: random(101 - $top);
      clip-path: inset(#{$top}% 0 #{$bottom}% 0);
    }
  }
}

Como querría dos conjuntos de posiciones de recorte aleatorias, haría dos conjuntos de esos @keyframes y los aplicaría a las copias:

.glitch::before {
  ...

  animation: glitch-anim-1 2s infinite linear alternate-reverse;
}

.glitch::after {
  ...

  animation: glitch-anim-2 2s infinite linear alternate-reverse;
}

Ahí es donde establecemos la velocidad (el número de fotogramas clave también afecta la velocidad), además de hacer que se ejecute infinitamente hacia adelante y hacia atrás.

Es muy divertido de ver:

Aunque debería ser evidente, un poco vale mucho.

Sass @mixins

Pensé que estaría bien si la técnica fuera más reutilizable. Básicamente, llame a un @mixin con parámetros para controlar el efecto y obtener justo lo que necesita.

.example-one {
  font-size: 100px;
  @include textGlitch("example-one", 17, white, black, red, blue, 450, 115);
}

Aquí está mi opinión al respecto:

/*
  (TEXT) PARAMS
  =================
  1. Namespace
  2. Intensity
  3. Text color
  4. Background color (flat)
  5. Highlight #1 color
  6. Highlight #2 color
  7. Width (px)
  8. Height (px)
*/

@mixin textGlitch($name, $intensity, $textColor, $background, $highlightColor1, $highlightColor2, $width, $height) {
  
  color: $textColor;
  position: relative;
  $steps: $intensity;
  
  // Ensure the @keyframes are generated at the root level
  @at-root {
    // We need two different ones
    @for $i from 1 through 2 {
      @keyframes #{$name}-anim-#{$i} {
        @for $i from 0 through $steps {
          $top: random(100);
          $bottom: random(101 - $top);
          #{percentage($i*(1/$steps))} {
            clip-path: inset(#{$top}% 0 #{$bottom}% 0);
          }
        }
      }
    }
  }
  &::before,
  &::after {
    content: attr(data-text);
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    background: $background;
  }
  &::after {
    left: 2px;
    text-shadow: -1px 0 $highlightColor1;
    animation: #{$name}-anim-1 2s infinite linear alternate-reverse;
  }
  &::before {
    left: -2px;
    text-shadow: 2px 0 $highlightColor2; 
    animation: #{$name}-anim-2 3s infinite linear alternate-reverse;
  }
}

Hay un millón de formas diferentes de abordarlo, esta es solo una. Depende totalmente de cuánto desea que haga el mixin por usted, cuánta personalización desea o necesita, qué desea que quede en el HTML, etc.

También hice dos mixins más, uno para aplicar este efecto a las imágenes y otro para SVG en línea. Son diferentes porque no usan pseudo-elementos para hacer las copias, la coloración ocurre de diferentes maneras, el posicionamiento ocurre de manera diferente, etc. Aquí están los tres juntos.

Y una demostración:

Ver la pluma
Texto con fallas en CSS por Chris Coyier (@chriscoyier)
en CodePen.