CodyHouse » Experiments https://codyhouse.co A free library of HTML/CSS/Javascript resources to boost your web projects and learn new tricks. Tue, 04 Apr 2017 16:36:48 +0000 en-US hourly 1 http://wordpress.org/?v=4.2.12 Image Mask Effect https://codyhouse.co/gem/image-mask-effect/ https://codyhouse.co/gem/image-mask-effect/#comments Wed, 15 Feb 2017 12:13:59 +0000 https://codyhouse.co/?post_type=gem&p=19101 image-mask-effect An immersive transition effect powered by image masks and CSS transforms. We’ve been publishing in our library some nice mask effects powered by SVG properties. This time we took advantage of the PNG transparencies to zoom through the mask layer, into a project background image. If you want to change the color of the .pgn masks, you can easily do it in Photoshop (or any other graphic tool) by applying a color overlay to the whole image layer. If you plan to create your own masks, please note that this effect works only if there’s an empty space in the very center of the mask. Inspiration: Offsite Homepage Animation by Hrvoje Grubisic.

Creating the structure

The HTML structure is composed of a list of <section>s wrapped in a .cd-image-mask-effect element. Each <section> contains a div.featured-image (project image), a div.mask (image mask) and a div.cd-project-info for the project content.
<section class="project-1 cd-project-mask">
	<h1>Project Name</h1>
	<div class="featured-image"></div>
	<div class="mask">
		<img src="img/mask-01.png" alt="mask">
		<span class="mask-border mask-border-top"></span>
		<span class="mask-border mask-border-bottom"></span>
		<span class="mask-border mask-border-left"></span>
		<span class="mask-border mask-border-right"></span>
	</div>

	<a href="#0" class="project-trigger">Explore Project</a>

	<a href="#0" class="cd-scroll cd-img-replace">Scroll down</a>

	<div class="cd-project-info" data-url="project-1">
		<!-- content loaded using js -->
	</div>

	<a href="#0" class="project-close cd-img-replace">Close Project</a>
</section> <!-- .cd-project-mask -->

<section class="project-2 cd-project-mask">
	<!-- content here -->
</section>

<!-- other sections here -->
The project content is not included in the HTML but is loaded using JavaScript.

Adding style

Each .cd-project-mask has a height of 100vh (viewport height) and a width of 100%; the project image is set as background-image of the .featured-image element, while the mask image is wrapped inside the .mask element. Four .mask-border elements have been used to create a frame around the image mask to make sure the project featured image is not visible outside the mask (we used <span> elements rather than pseudo elements because their behaviour was buggy on Safari 9).
.cd-project-mask {
  position: relative;
  height: 100vh;
  width: 100%;
  overflow: hidden;
}

.cd-project-mask .featured-image {
    /* project intro image */
    position: absolute;
    left: 50%;
    top: 50%;
    bottom: auto;
    right: auto;
    transform: translateX(-50%) translateY(-50%);
  	height: 100%;
  	width: 100%;
  	background: url(../img/img-01.jpg) no-repeat center center;
  	background-size: cover;
}

.cd-project-mask .mask {
  position: absolute;
  left: 50%;
  top: 50%;
  bottom: auto;
  right: auto;
  transform: translateX(-50%) translateY(-50%);
  width: 300px;
  height: 300px;
}

.cd-project-mask .mask .mask-border {
  /* this is used to create a frame around the mask */
  position: absolute;
}

.cd-project-mask .mask .mask-border-top,
.cd-project-mask .mask .mask-border-bottom {
    /* this is used to create a frame around the mask */
    height: calc(50vh - 150px + 10px);
    width: 100vw;
    left: 50%;
    right: auto;
    transform: translateX(-50%);
}

.cd-project-mask .mask .mask-border-top {
    bottom: calc(100% - 10px);
}

.cd-project-mask .mask .mask-border-bottom {
    top: calc(100% - 10px);
}

.cd-project-mask .mask .mask-border-left,
.cd-project-mask .mask .mask-border-right {
  	/* this is used to create a frame around the mask */
	height: 100vh;
	width: calc(50vw - 150px + 10px);
	top: 50%;
	bottom: auto;
    transform: translateY(-50%);
}

.cd-project-mask .mask .mask-border-left {
  left: calc(100% - 10px);
}

.cd-project-mask .mask .mask-border-right {
  right: calc(100% - 10px);
}
When the user selects a project, the class .project-view (added to the wrapper .cd-image-mask-effect) is used to hide all the other projects. The .mask element is then scaled up to reveal the project featured image and the project content is loaded (more in the Events handling section).
.project-view .cd-project-mask:not(.project-selected) {
   /* the project-view class is added to the .cd-image-mask-effect element when a project is selected - hide all not selected projects */
   position: absolute;
   top: 0;
   left: 0;
   opacity: 0;
   visibility: hidden;
}

Events handling

To implement this image mask effect, we created a ProjectMask object and used the initProject method to attach the proper event handlers.
function ProjectMask( element ) {
	this.element = element;
	this.projectTrigger = this.element.find('.project-trigger');
	this.projectClose = this.element.find('.project-close'); 
	this.projectTitle = this.element.find('h1');
	this.projectMask = this.element.find('.mask');
	//...
	this.initProject();
}

var revealingProjects = $('.cd-project-mask');
var objProjectMasks = [];

if( revealingProjects.length > 0 ) {
	revealingProjects.each(function(){
		//create ProjectMask objects
		objProjectMasks.push(new ProjectMask($(this)));
	});
}
When the user selects a project, the revealProject method is used to scale up the mask image while the uploadContent method takes care of loading the project content (using the load() function) and adding the new page to the window.history (using the pushState() method).
ProjectMask.prototype.initProject = function() {
	var self = this;

	//open the project
	this.projectTrigger.on('click', function(event){
		event.preventDefault();
		if( !self.animating ) {
			self.animating = true;
			//upload project content
			self.uploadContent();
			//show project content and scale up mask
			self.revealProject();
		}
	});

	//...
};

ProjectMask.prototype.revealProject = function() {
	var self = this;
	//get mask scale value
	self.updateMaskScale();
	//scale up mask and animate project title
	self.projectTitle.attr('style', 'opacity: 0;');
	self.projectMask.css('transform', 'translateX(-50%) translateY(-50%) scale('+self.maskScaleValue+')').one(transitionEnd, function(){
		self.element.addClass('center-title');
		self.projectTitle.attr('style', '');
		self.animating = false;
	});

	//hide the other sections
	self.element.addClass('project-selected content-visible').parent('.cd-image-mask-effect').addClass('project-view');
}

ProjectMask.prototype.uploadContent = function(){
	var self = this;
	//if content has not been loaded -> load it
	if( self.projectContent.find('.content-wrapper').length == 0 ) self.projectContent.load(self.projectContentUrl+'.html .cd-project-info > *');
	
	if( self.projectContentUrl+'.html'!=window.location ){
        //add the new page to the window.history
        window.history.pushState({path: self.projectContentUrl+'.html'},'',self.projectContentUrl+'.html');
    }
}
 ]]>
https://codyhouse.co/gem/image-mask-effect/feed/ 11
Immersive Video Template https://codyhouse.co/gem/immersive-video-template/ https://codyhouse.co/gem/immersive-video-template/#comments Tue, 20 Dec 2016 11:53:39 +0000 https://codyhouse.co/?post_type=gem&p=18351 Immersive Video Template A full-screen video presentation, that is resized and animated to become the content of a mobile device. Lately, we’ve come across a couple of websites using full-screen videos as a way to “dramatically” introduce a product or a feature of an app. We tried to create something similar, in the form of a simple application template. With the help of some CSS tricks (mostly CSS transforms and animations), we’ve created a video introduction that turns into content for a mobile device. Some of the tricky parts were about picking the right assets (we’ve created this video that introduces some features of a photo editing app) and handling the loading animation. We’re aware that showing a loader right away is not a great experience. Therefore, before using this template you should consider 1) whether the video would be a great selling point or not, and 2) whether you expect many returning visitors, or the goal is just to let the user download the app and (almost) never come back. If the answers to these questions are 1) YES and 2) No, then this template will come in handy for you ;) Video footage: pexels.com Inspiration: Landing Page by Cuberto Icons: Nucleo

Creating the structure

The HTML structure is composed of two main elements: a div.product-intro (product title, action button, ..) and a div.product-preview for the image/video preview of the product.
<div class="cd-immersive-video">
	<div class="intro-wrapper">
		<div class="product-intro">
			<div>
				<h1 class="item item-1">Immersive Video Template</h1>
				
				<p class="item item-2">
					<!-- product description here -->
				</p>

				<a href="#0" class="cd-btn item item-3">Download &amp; Article</a>
			</div>
		</div> <!-- .product-intro -->

		<div class="product-preview no-transition">
			<div class="device-frame">
				<img src="assets/img/mobile-frame.svg" alt="Device Frame">
			</div>

			<div class="product-image">
				<img src="assets/img/video-fallback.jpg" alt="Product Preview">
			</div>

			<div class="product-video" data-video="assets/video/video.mp4">
				<div class="video-wrapper">
					<!-- video will be insereted here using js -->
				</div>
			</div>
		</div> <!-- .product-preview -->
	</div> 

	<div class="cd-loader"></div>
</div> <!-- .cd-immersive-video -->
The video is not directly inserted in the HTML but it's loaded using JavaScript.

Adding style

On small devices (viewport width less than 800px), the css is pretty straightforward: both the div.product-intro and the div.product-preview are initially hidden ( opacity: 0 ) and then revealed using the cd-item-move-up animation.
@keyframes cd-item-move-up {
  	0% {
    	opacity: 0;
    	transform: translateY(50px);
  	}
  	100% {
   		opacity: 1;
    	transform: translateY(0);
  	}
}
The product preview video is not loaded on these devices while an image is used as a preview of the product (div.product-image > img). On bigger devices, the product intro and preview are initially hidden; once the preview video has been loaded (more in the Events handling section), the .video-is-loaded class is used to reveal the content and trigger the video animation.
@media only screen and (min-width: 800px) {
  .cd-immersive-video .intro-wrapper {
    /* while loading the video - hide the content */
    visibility: hidden;
    opacity: 0;
    transition: opacity 1.5s, visibility 1.5s;
  }
  .cd-immersive-video.video-is-loaded .intro-wrapper {
    /* video has been loaded - reveal content */
    visibility: visible;
    opacity: 1;
  }
}
For the video animation: the div.product-video is initially scaled-up (using JavaScript) to cover the entire viewport. Once the video has been played for around 1s, the div.product-video is scaled down and translated to the right. The div.product-intro is then animated using the .animate-content class: each item of the intro (h1, p..) is revealed using the cd-item-slide-in animation with a different animation-delay.
@media only screen and (min-width: 800px) {
  .cd-immersive-video .product-intro .item {
    opacity: 0;
  }
  .cd-immersive-video .product-intro.animate-content .item {
    animation: cd-item-slide-in .4s both;
  }
  .cd-immersive-video .product-intro.animate-content .item-1 {
    animation-delay: .1s;
  }
  .cd-immersive-video .product-intro.animate-content .item-2 {
    animation-delay: .2s;
  }
  .cd-immersive-video .product-intro.animate-content .item-3 {
    animation-delay: .3s;
  }
}

Events handling

On big devices, we use jQuery to load the product preview video and insert it into the HTML structure. We used the canplaythrough event to detect if the video is ready to be played; we also added a check to the readyState of the video  (if the video is cached the canplaythrough event may not be detected).
if( productVideo.is(':visible') ) { //productVideo = $('.product-video');
	//the video src is the data-video of productVideo
	var	video = $('<video><source src="'+productVideo.data('video')+'" type="video/mp4" />Sorry, your browser does not support HTML5 video.</video>');
	
	//check if the canplaythrough event occurs - video is ready to be played
	video.on('canplaythrough', function() {
		video.off('canplaythrough').trigger('readyToPlay');
	});
	// if video is in cache 'canplaythrough' won't be triggered 
	if (video.get(0).readyState > 3) {
		video.off('canplaythrough').trigger('readyToPlay');
	}
}
Once the video is ready, we insert it into the HTML, hide the loader and then trigger the video animation.
video.on('readyToPlay', function(){
	//video id ready to play
	video.appendTo(productVideo.find('.video-wrapper'));

	//wait for the end of an animation iteraction and reveal the video
	loader.one(animationIteration, function() {
        loader.addClass('no-animation').off(animationIteration);
		//makes sure the transition is applied (when using the scale-down class)
        //https://css-tricks.com/restart-css-animation/
        void loader.get(0).offsetWidth; 
        loader.addClass('scale-down');
        loader.on(transitionEnd, function(){
        	loader.off(transitionEnd);
        	immersiveVideoWrapper.trigger('startAnimation'); //animate div.product-video
        });
    });
});
]]>
https://codyhouse.co/gem/immersive-video-template/feed/ 4
3D Portfolio Template https://codyhouse.co/gem/3d-portfolio-template/ https://codyhouse.co/gem/3d-portfolio-template/#comments Tue, 20 Sep 2016 11:24:04 +0000 https://codyhouse.co/?post_type=gem&p=16675 3D Portfolio Template A portfolio template, with a filter that triggers the rotation of 3D sections. CSS 3D Transforms can be used in plenty of creative ways, particularly if combined with CSS Transitions! Today’s nugget is a good example of how to use CSS to create a parallelepiped, whose faces are different projects. A filter on top of the page triggers the 3D rotations that reveal new projects. Images: Unsplash

Creating the structure

The HTML structure is composed of two main elements: a nav.cd-3d-portfolio-navigation for the top projects navigation and a div.projects wrapping the portfolio projects. Inside the div.projects, three unordered lists (ul.row) are used to create the three rotating parallelepipeds.
<div class="cd-3d-portfolio">
	<nav class="cd-3d-portfolio-navigation">
		<div class="cd-wrapper">
			<h1>3D Portfolio Template</h1>
				
			<ul>
				<li><a href="#0" class="selected">Filter 1</a></li>
				<li><a href="#0">Filter 2</a></li>
				<li><a href="#0">Filter 3</a></li>
			</ul>
		</div>
	</nav> <!-- .cd-3d-portfolio-navigation -->
	
	<div class="projects">
		<ul class="row">
			<li class="front-face selected project-1">
				<div class="project-wrapper">
					<div class="project-image">
						<div class="project-title">
							<h2>Project 1</h2>
						</div>
					</div> <!-- .project-image -->

					<div class="project-content">
						<!-- project content here -->
					</div> <!-- .project-content -->

					<a href="#0" class="close-project">Close</a>
				</div> <!-- .project-wrapper -->
			</li>

			<li class="right-face project-2">
				<div class="project-wrapper">
					<div class="project-image">
						<div class="project-title">
							<h2>Project 2</h2>
						</div>
					</div> <!-- .project-image -->

					<div class="project-content">
						<!-- project content here -->
					</div> <!-- .project-content -->

					<a href="#0" class="close-project">Close</a>
				</div> <!-- .project-wrapper -->
			</li>

			<li class="right-face project-3">
				<div class="project-wrapper">
					<div class="project-image">
						<div class="project-title">
							<h2>Project 3</h2>
						</div>
					</div> <!-- .project-image -->

					<div class="project-content">
						<!-- project content here -->
					</div> <!-- .project-content -->

					<a href="#0" class="close-project">Close</a>
				</div> <!-- .project-wrapper -->
			</li>
		</ul> <!-- .row -->
	
		<ul class="row">
			<!-- projects here -->
		</ul> <!-- .row -->
	
		<ul class="row">
			<!-- projects here -->
		</ul> <!-- .row -->
	</div><!-- .projects -->
</div>

Adding style

Each ul.row has a height equal to one-fourth of the viewport height and is translated along the Z-axis of half the viewport width. This way, we move the rotation center of the element away from the user of a quantity equal to half the element width. Its list items (portfolio projects) are then used to create the different faces of the parallelepiped and are translated/rotated according to the face they are on. For example, the front-face just needs to be translated back along the Z-axis, while the right face needs to be rotated along the Y-axis and translated back. Here's a simple animation explaining this concept (created using Adobe After Effects): parallelepiped-animation
.cd-3d-portfolio .projects .row {
  height: 25vh;
  position: relative;
  z-index: 1;
  /* position its children in a 3d space */
  transform-style: preserve-3d;
  transform: translateZ(-50vw);
  transition: transform 0.6s cubic-bezier(0.5, 0, 0.1, 1);
}

.cd-3d-portfolio .projects .row > li {
  /* this is the single project */
  position: absolute;
  z-index: 1;
  height: 100%;
  width: 100%;
  overflow: hidden;
}
.cd-3d-portfolio .projects .row > li.front-face {
  transform: translateZ(50vw);
}
.cd-3d-portfolio .projects .row > li.right-face {
  transform: rotateY(90deg) translateZ(50vw);
}
.cd-3d-portfolio .projects .row > li.left-face {
  transform: rotateY(-90deg) translateZ(50vw);
}
.cd-3d-portfolio .projects .row > li.back-face {
  transform: rotateY(180deg) translateZ(50vw);
}
When the user selects one of the filters in the top navigation, each ul.row is rotated to reveal the selected face (more in the Event handling section). As for the single projects, the project preview image is set as background-image of the .project-image::before element; it has an absolute position and a height of 240% the project height (which means, 60% of the viewport height); this way only a portion of the preview image is visible due to the overflow property of its .row > li ancestor. The project content is wrapped inside the .project-content element which is placed right below the project preview image. When a project is open, the overflow property of the .row > li element is changed to reveal its content.
.cd-3d-portfolio .projects .project-image {
  position: relative;
  width: 100%;
  height: 25%;
  transition: transform 0.6s;
}
.cd-3d-portfolio .projects .project-image::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 1;
  height: 240%;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
}

.cd-3d-portfolio .projects .project-content {
  position: absolute;
  /* place the content right below the project image */
  top: 60%;
  width: 100%;
  background: white;
}
For the 3D rotation to work, the transform-style property of the .row elements is set to preserve-3d so that its children are placed in a 3D space. If the browser does not support this property, we replace the 3D effect with a fade-in/fade-out effect (we use Modernizr to check browser support).
.no-preserve3d .cd-3d-portfolio .projects .row {
  	/* fallback for browsers that don't support the preser3d property */
    transform: translateZ(0);
}
.no-preserve3d .cd-3d-portfolio .projects .row > li {
  	opacity: 0;
  	transform: translateX(0);
}
.no-preserve3d .cd-3d-portfolio .projects .row > li.front-face, 
.no-preserve3d .cd-3d-portfolio .projects .row > li.right-face, 
.no-preserve3d .cd-3d-portfolio .projects .row > li.left-face, 
.no-preserve3d .cd-3d-portfolio .projects .row > li.back-face {
    transform: translateX(0);
}
.no-preserve3d .cd-3d-portfolio .projects .row > li.selected {
  opacity: 1;
}

Events handling

To implement this 3D portfolio, we created a Portfolio3D object and used the bindEvents function to attach event handlers to the proper elements.
function Portfolio3D( element ) {
	//define a Portfolio3D object
	this.element = element;
	this.navigation = this.element.children('.cd-3d-portfolio-navigation');
	this.rowsWrapper = this.element.children('.projects');
	this.rows = this.rowsWrapper.children('.row');
	this.visibleFace = 'front';
	this.visibleRowIndex = 0;
	this.rotationValue = 0;
	//animating variables
	this.animating = false;
	this.scrolling = false;
	// bind portfolio events
	this.bindEvents();
}

if( $('.cd-3d-portfolio').length > 0 ) {
	var portfolios3D = [];
	$('.cd-3d-portfolio').each(function(){
		//create a Portfolio3D object for each .cd-3d-portfolio
		portfolios3D.push(new Portfolio3D($(this)));
	});
}
The visibleFace property is used to store the parallelepiped face visible at the moment (if the ul.row has not been rotated, the visible face is the front face while, if it has been rotated of 90deg, the visible face is the left one and so on). When the user selects a filter in the top navigation, the showNewContent() method is used to move the selected faces in the right position and to rotate the ul.row elements.
Portfolio3D.prototype.bindEvents = function() {
	var self = this;

	this.navigation.on('click', 'a:not(.selected)', function(event){
		//update visible projects when clicking on the filter
		event.preventDefault();
		if( !self.animating ) {
			self.animating = true;
			var index = $(this).parent('li').index();
			
			//show new projects
			self.showNewContent(index);

			//update filter selected element
			//..
		}
	});

	//...
};
According to whether the selected filter precedes or follows the one already selected, the ul.row are rotated clockwise ('rightToLeft') or anticlockwise ('leftToRight'). The getRotationPrameters method uses this direction value plus the visibleFace property value to determine the new rotation value of the ul.row; additionally, it takes care of determining which face is going to be visible to give it the proper classes. For example, if the face visible at the moment is the front face and we need to rotate the parallelepiped 'rightToLeft', then the new visible face will be the right one.
Portfolio3D.prototype.showNewContent = function(index) {
	var self = this,
		direction = ( index > self.visibleRowIndex ) ? 'rightToLeft' : 'leftToRight',
		rotationParams = this.getRotationPrameters( direction ),
		newVisibleFace = rotationParams[0],
		rotationY = rotationParams[1],
		translateZ = $(window).width()/2;
	
	//rotate the parallelepiped
	this.setTransform(rotationY, translateZ);
	
	//update .row > li classes
	//...

	//update Portfolio3D properties
	//..
};
]]>
https://codyhouse.co/gem/3d-portfolio-template/feed/ 10
Clipped SVG Slider https://codyhouse.co/gem/clipped-svg-slider/ https://codyhouse.co/gem/clipped-svg-slider/#comments Wed, 27 Jul 2016 10:03:07 +0000 https://codyhouse.co/?post_type=gem&p=15333 SVG Clipped Slider A simple slider, with morphing preview images animated using SVG properties. Here on CodyHouse we’ve published a lot of SVG experiments! What’s really powerful with SVG is the possibility to combine path animations with the clipPath element. Add a touch of CSS transformations and you got a spicy recipe! Inspiration: Music player animation by Veronika Lykova. Tool used: Snap.svg Images: Unsplash

Creating the structure

The HTML structure is composed of three unordered lists: a ul.gallery  and a ul.navigation for the slider images and navigation, and a ul.caption for the image captions. Each list item inside the ul.gallery is composed of a .svg-wrapper element wrapping a <svg> containing a  <clipPath> element (used to change the clipping area of the slide image), an <image> element (whose clip-path url attribute is the<clipPath> id), and a <use> element (whose xlink:href attribute is the<clipPath> id) used to create the layer covering the slide images not in the center.
<div class="cd-svg-clipped-slider" data-selected="M780,0H20C8.954,0,0,8.954,0,20v760c0,11.046,8.954,20,20,20h760c11.046,0,20-8.954,20-20V20 C800,8.954,791.046,0,780,0z" data-lateral="M795.796,389.851L410.149,4.204c-5.605-5.605-14.692-5.605-20.297,0L4.204,389.851 c-5.605,5.605-5.605,14.692,0,20.297l385.648,385.648c5.605,5.605,14.692,5.605,20.297,0l385.648-385.648 C801.401,404.544,801.401,395.456,795.796,389.851z">
	<div class="gallery-wrapper">
		<ul class="gallery">
			<li class="left">
				<div class="svg-wrapper">
					<svg viewBox="0 0 800 800">
						<title>Animated SVG</title>
						<defs>
							<clipPath id="cd-image-1">
								<path id="cd-morphing-path-1" d="M795.796,389.851L410.149,4.204c-5.605-5.605-14.692-5.605-20.297,0L4.204,389.851 c-5.605,5.605-5.605,14.692,0,20.297l385.648,385.648c5.605,5.605,14.692,5.605,20.297,0l385.648-385.648 C801.401,404.544,801.401,395.456,795.796,389.851z"/>
							</clipPath>
						</defs>
						
						<image height='800px' width="800px" clip-path="url(#cd-image-1)" xlink:href="img/img-01.jpg"></image>
						<use xlink:href="#cd-morphing-path-1" class="cover-layer" />
					</svg>
				</div> <!-- .svg-wrapper -->
			</li>

			<li class="selected">
				<div class="svg-wrapper">
					<svg viewBox="0 0 800 800">
						<title>Animated SVG</title>
						<defs>
							<clipPath id="cd-image-2">
								<path id="cd-morphing-path-2" d="M780,0H20C8.954,0,0,8.954,0,20v760c0,11.046,8.954,20,20,20h760c11.046,0,20-8.954,20-20V20 C800,8.954,791.046,0,780,0z"/>
							</clipPath>
						</defs>
						
						<image height='800px' width="800px" clip-path="url(#cd-image-2)" xlink:href="img/img-02.jpg"></image>
						<use xlink:href="#cd-morphing-path-2" class="cover-layer" />
					</svg>
				</div> <!-- .svg-wrapper -->
			</li>

			<!-- other slides here -->
		</ul>
		
		<nav>
			<ul class="navigation">
				<li><a href="#0" class="prev">Prev</a></li>
				<li><a href="#0" class="next">Next</a></li>
			</ul>
		</nav>
	</div>

	<ul class="caption">
		<li class="left">Lorem ipsum dolor</li>
		<li class="selected">Consectetur adipisicing elit</li>
		<!-- other captions here -->
	</ul>
</div> <!-- .cd-svg-clipped-slider -->

Adding style

By default, all the list items inside the ul.gallery have a position absolute, an opacity of zero and are moved to the right and scaled down.
.cd-svg-clipped-slider .gallery li {
  /* slider images */
  position: absolute;
  z-index: 1;
  top: 0;
  left: 25%;/* (100% - width)/2 */
  width: 50%;
  height: 100%;
  opacity: 0;
  transform: translateX(75%) scale(0.4);
  transition: opacity .3s, transform .3s ease-in-out;
}
The .selected class is then used to move the selected image back to the center and to scale it up.
.cd-svg-clipped-slider .gallery li.selected {
  /* slide in the center */
  position: relative;
  z-index: 3;
  opacity: 1;
  transform: translateX(0) scale(1);
}
The .left and .right classes are used to show the preview images on both sides of the selected image; the .left class is also used to move an image preview to the left.
.cd-svg-clipped-slider .gallery li.left {
  /* slides on the left */
  transform: translateX(-75%) scale(0.4);
}
.cd-svg-clipped-slider .gallery li.left, 
.cd-svg-clipped-slider .gallery li.right {
  /* .right -> slide visible on the right */
  z-index: 2;
  opacity: 1;
}
When a new slide is selected, the <path> element used to clip the slide image is animated to reveal a different portion of the image (the entire image if the slide is the .selected one, or just a section for the .left/.right images). The same classes are also used to control the visibility/position of the image captions. By default, all captions are hidden and moved to the right; the class .selected is used to show the selected caption and move it back to the center, while the .left class is used to hide it and move it to the left.
.cd-svg-clipped-slider .caption li {
  /* slide titles */
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  text-align: center;
  width: 100%;
  transform: translateX(100px);
  opacity: 0;
  transition: opacity .3s, transform .3s ease-in-out;
}
.cd-svg-clipped-slider .caption li.selected {
  /* slide visible in the center */
  z-index: 2;
  position: relative;
  transform: translateX(0);
  opacity: 1;
}
.cd-svg-clipped-slider .caption li.left {
  /* slide hidden on the left */
  transform: translateX(-100px);
}

Events handling

To implement this slider we created a svgClippedSlider object and used the bindEvents method to attach event handlers for the click to the slider navigation.
function svgClippedSlider(element) {
	this.element = element;
	this.slidesGallery = this.element.find('.gallery').children('li');
	this.slidesCaption = this.element.find('.caption').children('li');
	this.slidesNumber = this.slidesGallery.length;
	this.selectedSlide = this.slidesGallery.filter('.selected').index();
	// ....

	this.bindEvents();
}

svgClippedSlider.prototype.bindEvents = function() {
	var self = this;
	//detect click on one of the slides
	this.slidesGallery.on('click', function(event){
		if( !$(this).hasClass('selected') ) {
			//determine new slide index and show it
			var newSlideIndex = ( $(this).hasClass('left') )
				? self.showPrevSlide(self.selectedSlide - 1)
				: self.showNextSlide(self.selectedSlide + 1);
		}
	});
}
The showPrevSlide and showNextSlide methods take care of showing the selected slide; these functions are used to add/remove the proper classes from the slide images and captions, and to animate the 'd' attribute of the <path> element inside the <clipPath> used to clip the slide image.]]>
https://codyhouse.co/gem/clipped-svg-slider/feed/ 5
Radial SVG Slider https://codyhouse.co/gem/radial-svg-slider/ https://codyhouse.co/gem/radial-svg-slider/#comments Thu, 02 Jun 2016 13:02:33 +0000 https://codyhouse.co/?post_type=gem&p=13475 Radial SVG Slider A simple, responsive slider, with a radial transition effect powered by SVG clipPath and mask elements. While browsing Dribbble in search of inspiration, we came across this interesting animation created by Tokito. The main idea is to show a little preview of the following slide, then scale it up when the user interacts with it. In order to create this effect, SVG clipping and masking elements sounded like the perfect allies. Let’s break this up in steps: first of all, we needed to show a preview of both previous and next slides. Therefore, for each slide, we needed 2 paths: a circle element to clip the part of the image visible at the beginning (the navigation CTA), and a second circle element that covers the entire slider (obtained by increasing the radius of the first circle element). Here is a preview of the 2 paths created in Illustrator on 2 separate artboards, and then exported as SVG files. You’ll find these vectors inside the source files. clip-path By using the clipPath element, you can define the visible area of an image. Then, obviously, if you animate the clipPath element, you animate the visible area of the image you’re applying the clip to. clip-animation-01 Now the tricky part: by animating the circle element the user interacts with, we cover the entire slider, including the opposite round/navigation item. To fix that, we used the SVG mask element: basically we set an area where the animated circle element won’t be visible, no matter what . This mask is equal to the size of the round/navigation element we don’t want to cover. Masks use transparency. Therefore, using a vector graphic tool, we created a white path that covers the entire SVG viewport, except the area we want to mask out. mask Here is a quick animation we put together to show you the idea behind the clipping + masking. clip-animation-02 Images: Unsplash

Creating the structure

The HTML structure is composed of two unordered lists: a ul.cd-radial-slider for the slides and a ul.cd-radial-slider-navigation for the slider navigation. Each list item inside the ul.cd-radial-slider is composed of two main elements: a .svg-wrapper containing a svg with a  <clipPath> element (used to change the clipping area of the slide image) and an <image> element (whose clip-path url attribute is the <clipPath> id), and a .cd-radial-slider-content for the slide content. An additional .cd-round-mask is used to wrap the two <mask> elements.
<div class="cd-radial-slider-wrapper">
	<ul class="cd-radial-slider" data-radius1="60" data-radius2="1364" data-centerx1="110" data-centerx2="1290">
		<li class="visible">
			<div class="svg-wrapper">
				<svg viewBox="0 0 1400 800">
					<title>Animated SVG</title>
					<defs>
						<clipPath id="cd-image-1">
							<circle id="cd-circle-1" cx="110" cy="400" r="1364"/>
						</clipPath>
					</defs>

					<image height='800px' width="1400px" clip-path="url(#cd-image-1)" xlink:href="img/img-1.jpg"></image>
				</svg>
			</div> <!-- .svg-wrapper -->

			<div class="cd-slider-content">
				<div class="wrapper">
					<div>
						<h2>Slide #1 Title</h2>
						<p>Lorem ipsum dolor sit amet, consectetur.</p>
						<a href="#0" class="cd-btn">Learn More</a>
					</div>
				</div>
			</div> <!-- .cd-slider-content -->
		</li>

		<li class="next-slide">
			<!-- ... -->
		</li>

		<!-- additional slides here -->
		
	</ul> <!-- .cd-radial-slider -->

	<ul class="cd-slider-navigation">
		<li><a href="#0" class="next">Next</a></li>
		<li><a href="#0" class="prev">Prev</a></li>
	</ul> <!-- .cd-slider-navigation -->
	
	<div class="cd-round-mask">
		<svg viewBox="0 0 1400 800">
			<defs>
				<mask id="cd-left-mask" height='800px' width="1400px" x="0" y="0" maskUnits="userSpaceOnUse">
					<path fill="white" d="M0,0v800h1400V0H0z M110,460c-33.137,0-60-26.863-60-60s26.863-60,60-60s60,26.863,60,60S143.137,460,110,460z"/>
			    </mask>

			    <mask id="cd-right-mask" height='800px' width="1400px" x="0" y="0" maskUnits="userSpaceOnUse">
					<path fill="white" d="M0,0v800h1400V0H0z M1290,460c-33.137,0-60-26.863-60-60s26.863-60,60-60s60,26.863,60,60S1323.137,460,1290,460z"/>
			    </mask>
			</defs>
		</svg>
	</div>
</div> <!-- .cd-radial-slider-wrapper -->

Adding style

The slider structure is quite basic: all slides have an opacity: 0, are in absolute position and are placed one on top of the other (top: 0 and left:0). The .visible class is added to the selected slide (at the end of the clipping animation) to change its position from absolute to relative, while the .is-animating class is added to the slide during the clipping animation to change its z-index. The .next-slide and .prev-slide classes are instead used to show a preview of both the previous and next slides. Two additional classes have been used to animate the navigation round elements: a .scale-down class to hide the slide preview when a new slide is selected (scale-down effect) and a .move-up class used to create the click effect when one of the slide previews is clicked on.
.cd-radial-slider > li {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  opacity: 0;
  transition: transform .2s;
}
.cd-radial-slider > li.visible {
  position: relative;
  opacity: 1;
}
.cd-radial-slider > li.is-animating, 
.cd-radial-slider > li.prev-slide, 
.cd-radial-slider > li.next-slide {
  opacity: 1;
}
.cd-radial-slider > li.is-animating {
  z-index: 2;
}
.cd-radial-slider > li.scale-down, 
.cd-radial-slider > li.move-up {
  z-index: 3;
}
.cd-radial-slider > li.move-up {
  /* class added to the navigation round element when clicked - used to create the click effect */
  animation: cd-clicked .2s;
}
.cd-radial-slider > li.scale-down {
  /* class added to the navigation round element to create the scale down effect  */
  transform: scale(0);
}
.cd-radial-slider > li.next-slide {
  /* for the scale-down/click effect - change the transform origin so that it is the center of the navigation round element */
  transform-origin: 92.14% 50%;
}
.cd-radial-slider > li.prev-slide {
  transform-origin: 7.86% 50%;
}

Events handling

To implement the radial SVG slider, we created a radialSlider object and used the bindEvents function to attach event handlers for the click to the slider navigation:
var radialSlider = function(element) {
	this.element = element;
	this.slider = this.element.find('.cd-radial-slider');
	this.slides = this.slider.children('li');
	//...
	this.navigation = this.element.find('.cd-radial-slider-navigation');
	//...
	this.bindEvents();
} 

radialSlider.prototype.bindEvents = function() {
	var self = this;

	//update visible slide when clicking the navigation round elements
	this.navigation.on('click', function(event){
		if( !self.animating ) {
			self.animating =  true;
			event.preventDefault();
			var direction = ( $(event.target).hasClass('next') ) ? 'next' : 'prev';
			//update radialSlider index properties
			self.updateIndexes(direction);
			//show new slide
			self.updateSlides(direction);
		}
	});
}
To animate the slide image clipping area, we animated the 'r' attribute of the <circle> element inside the <clipPath>. We added to the .cd-radial-slider element a data-radius1 and a data-radius2 attribute to easily retrieve the initial and final radius values, and a data-centerx1 and data-centerx2 for the <circle> center (two different values for the next and previous slide preview). We then used the animate() method provided by Snap.svg to animate the circle element.
clipPathVisible.animate({'r': radius2}, duration, customMinaAnimation, function(){
	//callback function here
});
The easing function is a custom cubic-bezier function; unfortunately, this is something which is not available by default in Snap.svg, but you can create a custom timing function from your custom cubic-bezier (here’s a StackOverflow post that covers it in details). To apply a mask to the visible slide, instead, we changed the style attribute of the svg <image> element. For example, to mask the slide so that the next slide preview is visible we used:
this.slides.eq(this.visibleIndex).find('image').attr('style', 'mask: url(#'+this.rightMask.attr('id')+')');
where this.slides.eq(this.visibleIndex) is the visible slide and this.rightMask.attr('id') is the id of the <mask> element.]]>
https://codyhouse.co/gem/radial-svg-slider/feed/ 21
Animated Transition Effects https://codyhouse.co/gem/animated-transition-effects/ https://codyhouse.co/gem/animated-transition-effects/#comments Wed, 11 May 2016 11:04:34 +0000 https://codyhouse.co/?post_type=gem&p=12123 Animated Transition Effects A library of animated transition effects, powered by CSS Animations. A few weeks ago we published a tutorial about how to create an Ink Transition effect using a PNG sprite and the steps() CSS timing function. That resource has since become one of the most popular here on CodyHouse, therefore we decided to team up with talented motion designer Gabriele Mellera to create a small library of transition effects! If you want to learn how to create your own transition effects, here is our thorough tutorial: Ink Transition Effect Tutorial

How to use the transition effects

We created a separate html file for each effect. Note the class applied to the <body>, that we used in CSS to target the specific effect.
<!doctype html>
<html lang="en" class="no-js">
<head>
	<!-- ... -->
  	
	<title>Cartoon Transition Effect | CodyHouse</title>
</head>
<body class="cartoon-transition">
<main class="cd-main-content">
	<!-- ... -->
</main> 

<div class="cd-modal" id="modal-1">
	<!-- ... -->
</div> 

<div class="cd-transition-layer" data-frame="25"> 
	<div class="bg-layer"></div>
</div> 

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script>
	if( !window.jQuery ) document.write('<script src="js/jquery-2.2.1-min.js"><\/script>');
</script>
<script src="js/main.js"></script> <!-- Resource jQuery -->
</body>
</html>
The CSS file is organized in different sections. For a transition to work properly you need to include the style shared by all effects, the custom effect style, and the keyframes to control the animation.
/* -------------------------------- 

Shared style

-------------------------------- */

.cd-btn {
  display: inline-block;
  padding: 1.6em 2.4em;
  font-size: 1.4rem;
  letter-spacing: .15em;
  font-weight: 700;
  text-transform: uppercase;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
  transition: box-shadow .3s;
  /*...*/
}

/* -------------------------------- 

Scrub Effect - Custom effect style

-------------------------------- */

.scrub-transition {
  font-family: "PT Sans", sans-serif;
  color: #2c1a32;
  /*...*/
}

/* -------------------------------- 

Animations - remember to check the animation name to copy the correct keyframes

-------------------------------- */

@keyframes cd-sequence {
  0% {
    /* translateX(-2%) is used to horizontally center the first frame inside the viewport */
    transform: translateY(-50%) translateX(-2%);
  }
  100% {
    /* translateX(-98%) (2% + 96) is used to horizontally center the last frame inside the viewport  */
    transform: translateY(-50%) translateX(-98%);
  }
}
If you want to change the color of the transitions, all you need to do is importing the PNG image sprites into a graphic tool, and change the color of the filled area. For example, in Photoshop you can use layer styles to apply a color overlay. Once you've edited the image, simply save it as PNG. If you're concerned about the size, TinyPNG is a great tool to compress PNG images. Change image color]]>
https://codyhouse.co/gem/animated-transition-effects/feed/ 19
Ink Transition Effect https://codyhouse.co/gem/ink-transition-effect/ https://codyhouse.co/gem/ink-transition-effect/#comments Mon, 21 Mar 2016 12:08:38 +0000 https://codyhouse.co/?post_type=gem&p=9184 ink-transition-effect An ink bleed transition effect, powered by CSS animations. I recently came across a couple of websites using ink bleeds as transition effects. A great example is the Sevenhills website. At first I thought they were using a HTML canvas powered technique (for allowing transparency), then I checked the source code and found out they weren’t using a video, but a PNG image sprite. By using a PNG sprite and the steps() timing function in CSS, we can create video effects and use them as transitions! In our resource, we used this technique to fire a modal window, but you can use it to transition between two different pages as well. The process to create these effects is simple, let me break it down for you: First, you need a video with a filling effect and a transparent area. Then you need to export this video as a PNG sequence. We used After Effects to export the sequence (make sure to export the alpha channel as well). ae-01 Since our video is composed of 25 frames, the assets exported are 25 PNG images. Just to give you more info about the composition settings, we created a 640x360px video with a duration of 1 second and a frame rate equal to 25. ae-02 Finally the tedious part: you need to create the PNG sprite, by creating a new image that includes all frames on the same row. We did this manually using Photoshop, and combined all frames into a single 16000x360 pixels image. png-sequence-preview In order to turn the sequence into a video, we just need to translate the PNG sprite, and use the steps() function to define the number of frames. Do you want to learn more about CSS transforms and animations? Check out our course ;) Now let’s jump into the code!

Creating the structure

The HTML structure is composed of 3 main elements: a main.cd-main-content for the page main content, a div.cd-modal for the modal window and a div.cd-transition-layer for the transition layer.
<main class="cd-main-content">
	<div class="center">
		<h1>Ink Transition Effect</h1>
		<a href="#0" class="cd-btn cd-modal-trigger">Start Effect</a>
	</div>
</main> <!-- .cd-main-content -->

<div class="cd-modal">
	<div class="modal-content">
		<h1>My Modal Content</h1>
		
		<p>
			Lorem ipsum dolor sit amet, consectetur adipisicing elit. 
			Ad modi repellendus, optio eveniet eligendi molestiae? 
			Fugiat, temporibus! 
		</p>
	</div> <!-- .modal-content -->

	<a href="#0" class="modal-close">Close</a>
</div> <!-- .cd-modal -->

<div class="cd-transition-layer"> 
	<div class="bg-layer"></div>
</div> <!-- .cd-transition-layer -->

Adding style

The .cd-modal window has, initially, visibility: hidden, height: 100% and width: 100% and is in fixed position. When a user clicks the a.cd-modal-trigger, the visibility of the modal window is changed to visible and its opacity to 1 (using the .visible class).
.cd-modal {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 3;
  height: 100%;
  width: 100%;
  opacity: 0;
  visibility: hidden;
}
.cd-modal.visible {
  opacity: 1;
  visibility: visible;
}
The div.cd-transition-layer element is used to create the transition ink effect: it has visibility: hidden, height: 100% and width: 100% and is in fixed position.
.cd-transition-layer {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 2;
  height: 100%;
  width: 100%;
  opacity: 0;
  visibility: hidden;
  overflow: hidden;
}
Its child element div.bg-layer has the ink.png sprite as background-image, a background-size: 100%,  height: 100% and width: 2500% (the ink.png sprite is composed of 25 frames); its left/top/translate values are set so that, initially, the first frame of the ink.png sprite is centered inside the div.cd-transition-layer:
.cd-transition-layer .bg-layer {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translateY(-50%) translateX(-2%);
  height: 100%;
  /* our sprite is composed of 25 frames */
  width: 2500%;
  background: url(../img/ink.png) no-repeat 0 0;
  background-size: 100% 100%;
}
Note: to center an element inside its parent, you would use:
position: absolute;
left: 50%;
top: 50%;
transform: translateY(-50%) translateX(-50%);
In our case, though, we want to center the first frame of the ink.png sprite, and since div.bg-layer width is 25 times the one of its parent, we use a translateX(-(50/25)%). To create the ink animation, we change the translate value of the div.bg-layer; we defined the cd-sequence keyframes rule:
@keyframes cd-sequence {
  0% {
    transform: translateY(-50%) translateX(-2%);
  }
  100% {
    transform: translateY(-50%) translateX(-98%);
  }
}
This way, at the end of the animation, the last frame of the ink.png sprite is centered inside the div.cd-transition-layer element. Note: since we have 25 frames, to show the last one you need to translate the .bg-layer of -100% * (25 - 1) = -96%; but then, to center it inside its parent, you need to add the additional -2%. When a user clicks the a.cd-modal-trigger, the .visible class is added to the .cd-transition-layer to show it, while the .opening class is used to trigger the ink animation:
.cd-transition-layer.visible {
  opacity: 1;
  visibility: visible;
}
.cd-transition-layer.opening .bg-layer {
  animation: cd-sprite 0.8s steps(24);
  animation-fill-mode: forwards;
}
Note that we used the steps() function: that's because we don't want the translate value to change continuously, but rather change through fixed steps, in order to show one frame at a time; the number of steps used is equal to our frames less one.

Events handling

We used jQuery to add/remove classes when user clicks the a.cd-modal-trigger or .modal-close to open/close the modal window. Besides, we change the .bg-layer dimensions in order not to modify the png frames aspect ratio. In the style.css file, we set .bg-layer height and width so that each frame has height and width equal to the ones of the viewport. Viewport and frames could have a different aspect ratio though and that could distort the single frame. The setLayerDimensions() function has been used to prevent this from happening:
var frameProportion = 1.78, //png frame aspect ratio
	frames = 25, //number of png frames
	resize = false;

//set transitionBackground dimentions
setLayerDimensions();
$(window).on('resize', function(){
	if( !resize ) {
		resize = true;
		(!window.requestAnimationFrame) ? setTimeout(setLayerDimensions, 300) : window.requestAnimationFrame(setLayerDimensions);
	}
});

function setLayerDimensions() {
	var windowWidth = $(window).width(),
		windowHeight = $(window).height(),
		layerHeight, layerWidth;

	if( windowWidth/windowHeight > frameProportion ) {
		layerWidth = windowWidth;
		layerHeight = layerWidth/frameProportion;
	} else {
		layerHeight = windowHeight;
		layerWidth = layerHeight*frameProportion;
	}

	transitionBackground.css({
		'width': layerWidth*frames+'px',
		'height': layerHeight+'px',
	});

	resize = false;
}
]]>
https://codyhouse.co/gem/ink-transition-effect/feed/ 47
Animated Intro Section https://codyhouse.co/gem/animated-intro-section/ https://codyhouse.co/gem/animated-intro-section/#comments Thu, 29 Oct 2015 12:23:29 +0000 http://codyhouse.co/?post_type=gem&p=1546 animated-intro-section A collection of fancy text effects, to animate the tagline and action buttons of your website intro section. Animations in web design are often used to drive the user’s focus to a specific section. One section  you want to make sure to highlight is the main tagline, with the action buttons. A good use of typography and a wise choice of colors should do the trick. However we decided to spice things up a little by creating some text effects that you can easily apply to the intro section of your web projects.

Creating the structure

The HTML structure for each effect is pretty simple: a section.cd-intro is used as a container for the div.cd-intro-content which wraps the main tagline. The structure for the main tagline slightly differs from one effect to the other: for the bouncy effect, for example, we used an <h1> for the page title, a <p> tag as tagline, and a div.action-wrapper to wrap the action buttons:
<section class="cd-intro">
	<div class="cd-intro-content bouncy">
		<h1>Animated Intro Section</h1>
		<p>A collection of text effects for the intro section of your website</p>
		<div class="action-wrapper">
			<a href="#0" class="cd-btn main-action">Get started</a>
			<a href="#0" class="cd-btn">Learn More</a>
		</div>
	</div>
</section>
The .bouncy class added to the .cd-into-content is used to trigger the animation.

Adding style

By default, we hide the intro content by setting its opacity to zero, then we animate it using CSS Animations. For the bouncy effect, for example, we created 3 different animations for the <h1>, <p> and .cd-btn buttons:
.cd-intro-content h1,
.cd-intro-content p,
.cd-intro-content .cd-btn {
  opacity: 0;
  animation-delay: 0.3s;
  animation-fill-mode: forwards;
}

.bouncy.cd-intro-content h1 {
  animation-name: cd-bounce-right;
}

.bouncy.cd-intro-content p {
  animation-name: cd-bounce-left;
}

.bouncy.cd-intro-content h1,
.bouncy.cd-intro-content p {
  animation-duration: 0.6s;
}

.bouncy.cd-intro-content .cd-btn {
  animation-name: cd-bounce-rotate;
  animation-duration: 0.5s;
}

.bouncy.cd-intro-content .cd-btn.main-action {
  animation-delay: 0.4s;
}

@keyframes cd-bounce-right {
  0% {
    opacity: .2;
    transform: translateX(-200px);
  }
  60% {
    opacity: .7;
    transform: translateX(15px);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes cd-bounce-left {
  0% {
    opacity: .2;
    transform: translateX(200px);
  }
  60% {
    opacity: .7;
    transform: translateX(-15px);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes cd-bounce-rotate {
  0% {
    opacity: .2;
    transform: perspective(800px) rotateX(-80deg);
  }
  20% {
    opacity: 1;
  }
  60% {
    transform: perspective(800px) rotateX(20deg);
  }
  100% {
    opacity: 1;
    transform: perspective(800px) rotateX(0);
  }
}
Let's take a look at the video effect now: if you open the video.html file, you can see that an additional div.cd-bg-video-wrapper has been inserted; this element is used to wrap the background video (you won't see any <video> element inside the HTML because it's loaded using Javascript - more in the 'Events Handling' section). Besides, we inserted two svg elements (.svg-mask and .svg-mask-mobile) inside the <h1>: these svgs are used to create the title transparency effect (the first one when the device width is bigger than 768px, the second for the other devices). Basically, the idea is: we create an opaque rectangular <path> element but then we add a transparent area in the shape of the page title (in our demo, 'Video Effect'). The svg is then used as a layer to cover the entire .cd-intro-content: the transparent area of the svg lets you see what's below it (the background video). You can create a svg mask simply by using a vector graphic editor. We used Illustrator, where we created a rectangle and then removed the text using the pathfinder tool.
.cd-bg-video-wrapper {
  /* background cover video */
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: url(../assets/bg-img.jpg) no-repeat center center;
  background-size: cover;
}
.cd-bg-video-wrapper video {
  /* you won't see this element in the html, but it will be injected using js */
  display: block;
  position: absolute;
  left: 50%;
  top: 50%;
  bottom: auto;
  right: auto;
  transform: translateX(-50%) translateY(-50%);
  min-height: 100%;
  min-width: 100%;
  max-width: none;
  height: auto;
  width: auto;
}

.video.cd-intro-content svg {
  position: absolute;
  z-index: 2;
  /* center the svg inside its parent */
  left: 50%;
  top: 50%;
  bottom: auto;
  right: auto;
  transform: translateX(-50%) translateY(-50%);
  opacity: 0.8;
}
.video.cd-intro-content svg.svg-mask {
  /* this is the svg mask used on desktop version */
  display: none;
}
@media only screen and (min-width: 768px) {
  .video.cd-intro-content svg.svg-mask-mobile {
    display: none;
  }
  .video.cd-intro-content svg.svg-mask {
    display: block;
  }
}

Events handling

These intro effects have been created using CSS only. We used jQuery for the video effect only to load the background video if the device width is bigger than 768px. A data-video has been added to the  div.cd-bg-video-wrapper to retrieve the video url.]]>
https://codyhouse.co/gem/animated-intro-section/feed/ 25