When you create a product page, there are some effective UX solutions that can be used to make the user 'feel' the product. A comparison image slider is one of those. If you look at the Sony Ultra HD TV product page, they use this approach to emphasize the difference between their display resolution and a standard one. Google uses it to show how cool is a Google+ Photos filter.
Image credits: Unsplash.
Let's dive into the code ;)
👋 A new version of this component is available. Download now →.
Creating the structure
We used a figure element to wrap our original image, the modified image and the slider handle:
<figure class="cd-image-container">
<img src="img/img-original.jpg" alt="Original Image">
<span class="cd-image-label" data-type="original">Original</span>
<div class="cd-resize-img"> <!-- the resizable image on top -->
<img src="img/img-modified.jpg" alt="Modified Image">
<span class="cd-image-label" data-type="modified">Modified</span>
</div>
<span class="cd-handle"></span> <!-- slider handle -->
</figure> <!-- cd-image-container -->
Adding style
The original image is used to give the correct dimentions to its container .cd-image-container;
the modified image is inserted inside the div.cd-resize-image
. This element is set in position: absolute
over the .cd-image-container
and its width is changed while dragging the .cd-handle
element (using jQuery) in order to reveal/hide the modified image.
The .cd-resize-image
width is originally set to 0, and then changed to 50% when it enters the viewport, using the .is-visible
class which is added to the .cd-image-container
(using jQuery). We also defined the cd-bounce-in
animation to create a bounce effect.
This way, the animation will only start when the .cd-image-container
is visible inside the viewport.
.cd-resize-img {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 0;
overflow: hidden;
/* Force Hardware Acceleration in WebKit */
transform: translateZ(0);
backface-visibility: hidden;
}
.is-visible .cd-resize-img {
width: 50%;
/* bounce in animation of the modified image */
animation: cd-bounce-in 0.7s;
}
@keyframes cd-bounce-in {
0% {
width: 0;
}
60% {
width: 55%;
}
100% {
width: 50%;
}
}
Events handling
To implement the slider functionality, we defined the drags()
function to make the .cd-handle
element draggable (credit to CSS-Tricks).
When the mouse is pressed over the .cd-handle
element and moved, we update the .cd-handle
left value according to the current mouse position (we have added a constrain to limit the movement inside its parent .cd-image-container
) and change the div.cd-image-size
width accordingly.
In order to add mobile support, we have been using jQuery mobile (specifically, the vmouse
events which simulate the mouse
events on a touch device).
As last effect, when the .cd-image-container
element enters the viewport, we add the .is-visible
class to animate its children.
jQuery(document).ready(function($){
//function to check if the .cd-image-container is in the viewport here
// ...
//make the .cd-handle element draggable and modify .cd-resize-img width according to its position
$('.cd-image-container').each(function(){
var actual = $(this);
drags(actual.find('.cd-handle'), actual.find('.cd-resize-img'), actual);
});
//function to upadate images label visibility here
// ...
});
//draggable funtionality - credits to http://css-tricks.com/snippets/jquery/draggable-without-jquery-ui/
function drags(dragElement, resizeElement, container) {
dragElement.on("mousedown vmousedown", function(e) {
dragElement.addClass('draggable');
resizeElement.addClass('resizable');
var dragWidth = dragElement.outerWidth(),
xPosition = dragElement.offset().left + dragWidth - e.pageX,
containerOffset = container.offset().left,
containerWidth = container.outerWidth(),
minLeft = containerOffset + 10,
maxLeft = containerOffset + containerWidth - dragWidth - 10;
dragElement.parents().on("mousemove vmousemove", function(e) {
leftValue = e.pageX + xPosition - dragWidth;
//constrain the draggable element to move inside its container
if(leftValue < minLeft ) {
leftValue = minLeft;
} else if ( leftValue > maxLeft) {
leftValue = maxLeft;
}
widthValue = (leftValue + dragWidth/2 - containerOffset)*100/containerWidth+'%';
$('.draggable').css('left', widthValue).on("mouseup vmouseup", function() {
$(this).removeClass('draggable');
resizeElement.removeClass('resizable');
});
$('.resizable').css('width', widthValue);
//function to upadate images label visibility here
// ...
}).on("mouseup vmouseup", function(e){
dragElement.removeClass('draggable');
resizeElement.removeClass('resizable');
});
e.preventDefault();
}).on("mouseup vmouseup", function(e) {
dragElement.removeClass('draggable');
resizeElement.removeClass('resizable');
});
})