<div class="container">
	<div class="shutter js-shutter">
    <svg id="shutters_svg" data-name="shutters_svg" xmlns="http://www.w3.org/2000/svg" width="1001.5" height="996.5" viewBox="0 0 1001.5 996.5">
			<g id="shutters">
				<path id="shutter1" d="M495.6,509C466.2,673.8,390.8,839,296,1000H707C704,784.9,608.2,584.6,495.6,509Z" transform="translate(0 -3.5)" fill="#28292e" stroke-width="1" stroke="#000"/>
				<path id="shutter2" d="M296,1000c90.9-137,167-319,203-499C368,593,201.3,655.6,0,707Z" transform="translate(0 -3.5)" fill="#28292e" stroke-width="1" stroke="#000"/>
				<path id="shutter3" d="M0,707V295c142.6,85.4,302.9,158.8,499,208C363.2,592.2,198.6,661.4,0,707Z" transform="translate(0 -3.5)" fill="#28292e" stroke-width="1" stroke="#000"/>
				<path id="shutter4" d="M290,4,0,295.8C180.2,401.6,334.3,464.8,498,501,396,354.6,310,60.8,290,4Z" transform="translate(0 -3.5)" fill="#28292e" stroke-width="1" stroke="#000"/>
				<path id="shutter5" d="M290,4H709C622.7,137.4,560.5,310.4,498,499,400.1,346.2,335.5,170.8,290,4Z" transform="translate(0 -3.5)" fill="#28292e" stroke-width="1" stroke="#000"/>
				<path id="shutter6" d="M1001.5,292.5,705.8,3.5C619.9,146.2,563.7,301.6,498,500,646.3,398.3,944,312.5,1001.5,292.5Z" transform="translate(0 -3.5)" fill="#28292e" stroke-width="1" stroke="#000"/>
				<path id="shutter7" d="M999,294.9l.2,422.9C854.5,630.2,691.9,554.9,492.9,504.5,593.7,433,779.3,358.7,999,294.9Z" transform="translate(0 -3.5)" fill="#28292e" stroke-width="1" stroke="#000"/>
				<path id="shutter8" d="M499,509c169.3,38.9,335.9,109.1,500,209L707,1000c-6-259-117-423-208-491" transform="translate(0 -3.5)" fill="#28292e" stroke-width="1" stroke="#000"/>
			</g>
		</svg>
	</div>
  <section>
      <h2>Camera Shutter Animation</h2>
      <p>Using SVG & GSAP<br/>Can be used as Page or Slider Transition</p>
      <span class="button js-play-btn">PLAY</span>
  </section>
</div>
@import url(https://fonts.googleapis.com/css?family=Open+Sans);

body {
  background: #2a282a;
}

.shutter {
	position: absolute;
	width: 100%;
	height: 100%;
  z-index: 1;

  &.is--active {
		display: block;
    z-index: 10;
  }

  svg {
    position: absolute;
		left: 50%;
		top: 50%;
		transform: translate(-50%, -50%);
		width: 360%;
		height: 300%; 
  }
}

.container {
  position: absolute;
  overflow: hidden;
  background: #D3D3D3;
  border: 1px solid #000;
  width: 100%;
  max-width: 640px;
  height: 420px;
  margin: auto;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
}

section {
  position: relative;
  display: flex;
  width: 400px;
  height: 400px;
  text-align: center;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  margin: 0 auto;
  z-index: 5;
  font-family: 'Open Sans';
}

.button {
  cursor: pointer;
  background: rgb(40, 40, 40);
  color: #ffffff;
  font-size: 14px;
  padding: 10px 40px;
  border: 1px solid rgb(40, 40, 40);
  
  &:hover {
    background: #D3D3D3;
    color: rgb(40, 40, 40);
  }
}

h2 {
  margin: 0;
  padding: 0;
  text-transform: uppercase;
}

p {
  margin: 20px 0;
  padding: 0;
  font-size: 14px;
}
const SPEED_NUM = 0.35; // slow the speed of the shutter by increasing this number
const ROTATION_DEG = 60;
const CSS_IS_ACTIVE = "is--active";

class ShutterGSAP {
	constructor() {
		this.shutterContainer = document.querySelector(".js-shutter");
		this.shutterSVG = this.shutterContainer.querySelector("svg#shutters_svg");
		this.shutters = this.shutterSVG.querySelectorAll("path");

		this.shutterTimeline = new TimelineMax({paused: true, force3D:true, yoyo:true, repeat: 1});
		this.stopAnimate = this.stopAnimate.bind(this);
		this.shutterTimeline.eventCallback("onComplete", this.stopAnimate);

		this.init();
	}

	init() {
		this.shutterTimeline
		.from(this.shutters[0], SPEED_NUM,{rotation:ROTATION_DEG, transformOrigin:"39% 87%", ease:Expo.easeIn}, 1)
		.from(this.shutters[1], SPEED_NUM,{rotation:ROTATION_DEG, transformOrigin:"14% 78%", ease:Expo.easeIn}, 1)
		.from(this.shutters[2], SPEED_NUM,{rotation:ROTATION_DEG, transformOrigin:"2% 44%", ease:Expo.easeIn}, 1)
		.from(this.shutters[3], SPEED_NUM,{rotation:ROTATION_DEG, transformOrigin:"21% 17%", ease:Expo.easeIn}, 1)
		.from(this.shutters[4], SPEED_NUM,{rotation:ROTATION_DEG, transformOrigin:"60% 15%", ease:Expo.easeIn}, 1)
		.from(this.shutters[5], SPEED_NUM,{rotation:ROTATION_DEG, transformOrigin:"82% 34%", ease:Expo.easeIn}, 1)
		.from(this.shutters[6], SPEED_NUM,{rotation:ROTATION_DEG, transformOrigin:"88% 61%", ease:Expo.easeIn}, 1)
		.from(this.shutters[7], SPEED_NUM,{rotation:ROTATION_DEG, transformOrigin:"72% 86%", ease:Expo.easeIn}, 1)

		.to(this.shutters[0], SPEED_NUM,{rotation:0, ease:Expo.easeOut},1)
		.to(this.shutters[1], SPEED_NUM,{rotation:0, ease:Expo.easeOut},1)
		.to(this.shutters[2], SPEED_NUM,{rotation:0, ease:Expo.easeOut},1)
		.to(this.shutters[3], SPEED_NUM,{rotation:0, ease:Expo.easeOut},1)
		.to(this.shutters[4], SPEED_NUM,{rotation:0, ease:Expo.easeOut},1)
		.to(this.shutters[5], SPEED_NUM,{rotation:0, ease:Expo.easeOut},1)
		.to(this.shutters[6], SPEED_NUM,{rotation:0, ease:Expo.easeOut},1)
		.to(this.shutters[7], SPEED_NUM,{rotation:0, ease:Expo.easeOut},1)
	}

	onAnimate() {
		if(!this.shutterContainer.classList.contains(CSS_IS_ACTIVE)) {
			this.shutterContainer.classList.add(CSS_IS_ACTIVE);
		}
		this.shutterTimeline.play();
	}

	stopAnimate() {
		if(this.shutterContainer.classList.contains(CSS_IS_ACTIVE)) {
			this.shutterContainer.classList.remove(CSS_IS_ACTIVE);
			this.shutterTimeline.pause(0, false);
		}
	}
}

class App {
	constructor() {
		this.shutter = new ShutterGSAP();
		const btn = document.querySelector(".js-play-btn");

		this.startAnimation = this.startAnimation.bind(this);
		btn.addEventListener("click", this.startAnimation);
	}

	startAnimation() {
		this.shutter.onAnimate();
	}
}

const app = new App();
app.startAnimation();
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js