React function components with context

by rafaelaferro

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<div id="app"></div>

SCSS

body {
  background: #111020;
}

.app {
  display: flex;
  gap: 20px;
  padding: 50px;
}


/* 
  mixin to create a glitch effect animation
  - $nth-child: specifies the layer number (default is 1)
  - $frames: specifies the number of animation frames (default is 20)
  - $duration: specifies the duration of the animation (default is 2 seconds)
  - $delay: specifies the delay before the animation starts (default is 0 seconds)
*/

@mixin glitch-animation($nth-child: 1,
  $frames: 20,
  $duration: 2s,
  $delay: 0s) {
  // 1. HUE TONES
  // Color each layer differently based on $nth-child
  @if $nth-child==1 {
    // You can tweak the hue-rotate values in each layer, to achieve
    // a different color palette
    filter: sepia(100%) saturate(300%) brightness(120%) hue-rotate(240deg);
  }
  @else if $nth-child==2 {
    filter: sepia(100%) saturate(300%) brightness(150%) hue-rotate(520deg);
  }
  @else if $nth-child==3 {
    filter: sepia(100%) saturate(300%) brightness(120%) hue-rotate(90deg);
  }

  /* Generate a name for each layer so they animate differently */
  $name: glitch-animation-#{unique-id()};

  @keyframes #{$name} {
    @for $i from 0 through $frames {
      #{percentage($i * (1 / $frames) / 3)} {
        // 2. CROP
        $top-limit: random() * 100%;
        $bottom-limit: random() * 100%;
        
        clip-path: polygon(
          0% $top-limit,
          100% $top-limit,
          100% $bottom-limit,
          0% $bottom-limit
        );

        // 3. OFFSET        
        @for $i from 1 through 3 {
          @if $nth-child == $i {
            // Offset the layer position in proportion to image size
            // (between -3% and 3% in each direction)
            $translate-x: random() * 6% - 3%;
            $translate-y: random() * 6% - 3%;

            transform: translate(calc(-50% + #{$translate-x}), $translate-y);
          }
        }
      }
    }

    /* Stop animating the crop to pause the glitch for two-thirds of the frames */
   ...

React

function App() {
  return (
  	<div className="app">
      <Glitch src="https://www.rafaelaferro.com/assets/images/blogposts/glitch-effect/mimi.jpg" alt="The most beautiful cat ever seen" width={200} height={200} />
      <Glitch src="https://www.rafaelaferro.com/assets/images/blogposts/glitch-effect/mimi-2.jpg" alt="The most beautiful cat ever seen" width={100} height={100} />
      <Glitch src="https://www.rafaelaferro.com/assets/images/blogposts/glitch-effect/mimi-2.jpg" alt="The most beautiful cat ever seen" width={50} height={50} />
    </div>
  );
}


function Glitch({ src, alt, width, height }) {
    return (
    	<div className="glitch-wrapper">
        <img src={src} alt={alt} width={width} height={height} />

        {/*
	        Using lodash for simplicity but you can
	        manually repeat the html element
        */}
        {_.times(3, i => (
        	<img
	        	key={i}
	        	className="glitch-layer"
	        	src={src}
	        	alt=""
	        	width={width}
	        	height={height}
        	/>
        ))}
      </div>
  )
}

ReactDOM.render(<App />, document.querySelector("#app"))