GSAP

by Kenneth Luplau-Brøgger

HTML

<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/splitting.js"></script>
<div class="text">
  <p>Ordene er sendt til volapyk-laboratoriet til undersøgelse. Er der tale om volapyk, udrydder vi den med det samme. </p>
  <p>Tak for hjælpen. Og fortsat god dag.</p>
</div>

<svg version="1.1" x="0px" y="0px" width="24" height="27" viewBox="0 0 24 27" class="trashcan">

<g class="can">
<style type="text/css">
	.st0{fill:#F5F5F5;}
</style>
	<g>
		<path class="st0" d="M4.3,26c-0.2,0-0.5-0.2-0.5-0.4l-2.5-16c0-0.2,0.1-0.3,0.1-0.4C1.4,9.1,1.6,9,1.8,9h20.5
			c0.2,0,0.3,0.1,0.4,0.2c0.1,0.1,0.1,0.2,0.1,0.4l-2.5,16c0,0.2-0.2,0.4-0.5,0.4H4.3z"/>
		<path d="M21.7,10l-2.4,15H4.7L2.3,10H21.7 M22.2,8H1.8C0.8,8,0.1,8.8,0.3,9.7l2.5,16C2.9,26.5,3.5,27,4.3,27h15.4
			c0.7,0,1.4-0.5,1.5-1.3l2.5-16C23.9,8.8,23.2,8,22.2,8L22.2,8z"/>
	</g>
	<g>
		<path d="M7,19L7,19c-0.6,0-1-0.4-1-1v-5c0-0.6,0.4-1,1-1h0c0.6,0,1,0.4,1,1v5C8,18.6,7.6,19,7,19z"/>
	</g>
	<g>
		<path d="M12,21L12,21c-0.6,0-1-0.4-1-1v-7c0-0.6,0.4-1,1-1h0c0.6,0,1,0.4,1,1v7C13,20.6,12.6,21,12,21z"/>
	</g>
	<g>
		<path d="M17,19L17,19c-0.6,0-1-0.4-1-1v-5c0-0.6,0.4-1,1-1h0c0.6,0,1,0.4,1,1v5C18,18.6,17.6,19,17,19z"/>
	</g>
</g>

<g class="lit">
	<path d="M23,3H13V1c0-0.6-0.4-1-1-1s-1,0.4-1,1v2H1C0.4,3,0,3.4,0,4c0,0.6,0.4,1,1,1h22c0.6,0,1-0.4,1-1C24,3.4,23.6,3,23,3z"/>
</g>

</svg>

SCSS

body {
  padding: 20px;
}

svg {
  overflow: visible;
  position: absolute;
  top: 300px;
  left: 500px;
}

.text {
  p {
    span {
      display: inline-block
    }
  }
}

.trashcan {
  cursor: pointer;
}

JavaScript 1.7

let trashPosition = null;
let words = null;

function map (value, start1, stop1, start2, stop2) {
  return ((value - start1) / (stop1 - start1)) * (stop2 - start2) + start2
}

const closeLit = () => {
  TweenMax.to(".lit", 1, {
    rotation: 0,
    y: 0,
    transformOrigin: "100% 50%",
    ease: Expo.easeInOut
  })
}

const openLit = (callback) => {
  TweenMax.to(".lit", 1, {
    rotation: 35,
    y: -5,
    transformOrigin: "100% 50%",
    ease: Expo.easeInOut,
    onComplete() {
      callback();
    }
  });
}

const animateWords = (callback) => {
	let maxWordPosLeft = 0;
	let minWordPosLeft = 999999;
	
  Splitting({
    target: "p",
    by: "words",
    key: null
  });

  words = document.querySelectorAll(".text p > span");

	words.forEach(word => {
		if (word.innerHTML === " ") {
      word.innerHTML = "&nbsp;"
    }
		
		const left = word.getBoundingClientRect();
		
		if (left < minWordPosLeft) {
			minWordPosLeft = left;
		}
		
		if (left > maxWordPosLeft) {
			maxWordPosLeft = left;
		}
	})
	
	setTimeout(() => {
		Array.from(words).reverse().forEach((word, i) => {
			const wordPosition = word.getBoundingClientRect();

			TweenMax.to(word, 0.5, {
				x: trashPosition.left - wordPosition.left + 15,
				y: trashPosition.top - wordPosition.top - 10,
				scale: 0.35,
				rotation: map(wordPosition.left, minWordPosLeft, maxWordPosLeft, 15, 90),
				transformOrigin: "0 0 0",
				ease: Expo.easeIn,
				delay: 0.01 * i,
				onComplete() {
					TweenMax.set(word, {
						opacity: 0
					})
          
          if (i + 1 === words.length) {
          	closeLit();
          }
				}
			})
		})
	}, 1000/60);
}

const trash = () => {
  trashPosition = document.querySelector(".trashcan").getBoundingClientRect();
	
  openLit(() => {
    animateWords(closeLit);
  })
}

document.querySelector(".trashcan").addEventListener("click", () => {
  trash();
})