I have two versions of an image: a desaturated version and a full color version. What I want to accomplish is a hover effect in which mousing over the desaturated image reveals a circle of the color version of the image. It would be sort of like shining a spotlight on the desaturated image to show its color. And then when you move the mouse away, it would fade back to its desaturated state.
I know I could probably use flash, but I'd like to do this with JavaScript and CSS. Ideally this would degrade to just an image if JavaScript is disabled and could be fluid in width (responsive).
border-radius
CSS3 border-radius
can be used to create a round div with a background image which serves as the image spotlight. The spotlight can be overlaid on top of the main image, and positioned based on the mouse coordinates. JSFiddle Demo
Although there's no natural way to soften the edges of the spotlight in CSS3 -- which would require support for adding an opacity gradient to arbitrary content -- it can be simulated using a staggered set of elements with increasing radius and decreasing opacity. Updated demo with softened edges
In the updated demo, the size and softness of the the spotlight can be adjusted using the following variables:
var spotlightDiameter = 150; // Base size (not including the soft edge)
var numSpotlightLayers = 6; // More layers = softer edges
var spotlightLayerThickness = 2; // Thinner = the softening is more subtle
Here's a modified demo where the spotlight has noticeable ripples. The thickness of the layers was increased to show more clearly how it works.
Below is a simplified version of the code for the initial version with sharp edges.
HTML
<div class="content">
<div class="spotlight"></div>
</div>
CSS
.content {
position: relative;
width: 640px;
height: 480px;
background: url(desaturated.jpg) no-repeat 0 0;
overflow: hidden;
}
.spotlight {
display: none;
position: absolute;
background: url(overly_saturated.jpg) no-repeat 0 0;
}
jQuery
var spotlightDiameter = 150;
// Update the spotlight position on mousemove
$('.content').on('mousemove', function(e){
var center = {x: e.pageX - this.offsetLeft,
y: e.pageY - this.offsetTop};
var x = center.x - (spotlightDiameter >> 1);
var y = center.y - (spotlightDiameter >> 1);
$('.spotlight').css({left: x + 'px', top: y + 'px',
backgroundPosition: -x + 'px ' + -y + 'px'}).show();
});
// Hide the spotlight on mouseout
$('.content').on('mouseout', function(e){
$('.spotlight').hide();
});
// Initialize the spotlight
$(document).ready(function(){
$('.spotlight').width(spotlightDiameter + 'px')
.height(spotlightDiameter + 'px')
.css({borderRadius: (spotlightDiameter >> 1) + 'px'});
});
Alternative implementations
This could also be implemented using HTML5 Canvas or SVG. Below is a browser-support comparison of the different approaches:
border-radius
: Not supported by IE8 or earlier.
- HTML5 Canvas: Not supported by IE8 or earlier.
- SVG: Not supported by IE8 or earlier, or Android 2.3 or earlier (which is still most of the Android marketshare).
In short, IE8 and earlier is not an option for any of these approaches, and if Android support is needed, that limits the choices to border-radius
and HTML5 Canvas. Of course, since this is mouse-based, Android support may not be a factor anyhow.
Use two SVG <image>
elements overlaid exactly on top of one another. The bottom is the greyscale image. The top is the color image. Apply a clipPath
to the color image, and then adjust the transform on the clipping path to reveal different areas of the upper image.
Simple Demo: http://jsfiddle.net/hZgkz/
SVG:
<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="250px">
<defs>
<image id="im" width="500" height="500"
xlink:href="http://www.nyweamet.org/wp-content/uploads/2011/09/0942a__grandCentralStationExterior.jpg"/>
<clipPath id="clip">
<path id="path" transform="translate(40,60)"
d="M60,0 A30,30 1,0,0 60,120 30,30 1,0,0, 60,0"/>
</clipPath>
</defs>
<use id="clippedImage" xlink:href="#im" clip-path="url(#clip)"/>
</svg>
and the JavaScript that moves the circle around:
var tx = document.querySelector('#path').transform.baseVal.getItem(0);
setInterval(function(){
var ms = (new Date)*1;
tx.matrix.e = Math.sin(ms/812)*150 + 160;
tx.matrix.f = Math.cos(ms/437)*60 + 70;
},50);
All you have to do is track the mouse movement and set the translation to the right spot.
You can use some CSS to achieve the result, if I have understood correctly your request. I've prepared a small fiddle as a demo here: http://jsfiddle.net/sandro_paganotti/k3AmZ/
Here's the code involved:
HTML
<figure data-desaturated></figure>
<figure data-original></figure>
CSS
figure{ width: 550px; height: 360px;
position: absolute; top: 0; left: 0;
margin: 0; padding: 0;
background-position: 50% 50%;
background-repeat: no-repeat;
background-image: url('yourimage.png');
}
figure[data-desaturated]{
/* I've used CSS filters tu simulate desaturation, you can use your already desaturated image */
-webkit-filter: grayscale(0.9);
}
figure[data-original]{
width: 360px;
left: 95px;
border-radius: 180px;
opacity: 0;
transition: opacity 0.4s;
}
figure[data-desaturated]:hover + figure[data-original],
figure[data-original]:hover{
opacity: 1;
}
I've also added a transition
to enhance the experience.
Update
Version that follows mouse movements: http://jsfiddle.net/sandro_paganotti/k3AmZ/3/