Image Mask Effect

Go to Source
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');
    }
}

 
Go to Source