views:

297

answers:

6

Hello

I'm trying to simulate the effect where I hover on an image an overlayed semi-transparent image will fade in from the direction where your mouse came from. Vice versa when your mouse leaves the image (fadeout + moves away)

I've prepared a test page for this. Go ahead and check it out, it will clarify what the desired effect is.

I have defined a HTML structure for this:

    <div class="overlayLink">
        <img src="assets/work/thumbnails/kreatude.jpg" alt="Kreatude" />
        <div class="overlayLink_overlay_bg">&nbsp;</div>
        <div class="overlayLink_overlay_fg">
            <span class="overlayLink_overlay_link"><a href="#">View Case Study</a></span>
            <div class="top">&nbsp;</div>
            <div class="left">&nbsp;</div>
            <div class="right">&nbsp;</div>
            <div class="bottom">&nbsp;</div>
        </div>
     </div>

and the following JS (I'm using jQuery):

jQuery(document).ready(function () {
    ourWork();
});

function ourWork(){
    var inHandler = function(){
        var blue_bg = jQuery(this).find('.overlayLink_overlay_bg');
        var divClass = inClass;

        blue_bg.stop(true,true).fadeIn();
        var ml,mt;
        if(divClass == 'left'){
            ml = -260;
            mt = 0;
        }
        else if(divClass == 'right'){
            ml = 260;
            mt = 0;
        }
        else if(divClass == 'top'){
            ml = 0;
            mt = -140;
        }
        else if(divClass == 'bottom'){
            ml = 0;
            mt = 140;
        }       

        //positioning
        jQuery(this).find('a').css({
            'marginLeft': ml + 'px',
            'marginTop' : mt + 'px'
        });


        //animation
        jQuery(this).find('a').stop(true,true).animate({
            "marginLeft": "0px",
            "marginTop": "0px"
        }, "slow");
    }
    var outHandler = function(){
        var blue_bg = jQuery(this).find('.overlayLink_overlay_bg');
        var divClass = outClass;

        blue_bg.stop(true,true).fadeOut();
        var ml,mt;
        if(divClass == 'left'){
            ml = -260;
            mt = 0;
        }
        else if(divClass == 'right'){
            ml = 260;
            mt = 0;
        }
        else if(divClass == 'top'){
            ml = 0;
            mt = -140;
        }
        else if(divClass == 'bottom'){
            ml = 0;
            mt = 140;
        }        

        //animation
        jQuery(this).find('a').stop(true,true).animate({
            "marginLeft": ml + "px",
            "marginTop": mt + "px"
        }, "slow");

    }

    var inClass, outClass;
    jQuery('.overlayLink_overlay_fg div').hover(function(){        
        inClass = jQuery(this).attr('class');
    },function(){       
        outClass = jQuery(this).attr('class');
    });

    jQuery('.overlayLink').mouseenter(inHandler).mouseleave(outHandler);
}

explanation:

Basically I have four absolute positioned divs on top of the image to know the direction (left,top,bottom,right) when I hover on one of those 4 div's (.overlayLink_overlay_fg div) I put the class name of the hovered div in a variable (var inClass and var outclass)

Once I hover over the div who covers the area of the image (.overlayLink) I request the direction from the inClass or outClass variable and perform the animation (inHandler,outHandler)

however this all seems to work, it's a little glitchy when you move your mouse really fast, now I'm asking what the problem is (that's causing the glitches) and how it could be fixed with my current code.

Thanks in advance

A: 

Rather than surrounding the image with absolutely positioned divs, you might try doing this with an image map, attaching handlers to the defined areas. Just define the border areas to be thick enough that the mouse movement will be detected even if it's being moved rather quickly.

Tim
is this just a thought or is this considered being a good practice?
Ayrton
Note sure what you mean by "just a thought". Surrounding your images with absolutely positioned divs is a clever hack. Whereas image map and image are integral to one another and offer a cleaner design with better encapsulation and a less complicated page layout.
Tim
Also, with an image map you can more easily adjust the thickness of the invisible "hot" border areas, the better to detect mouse-over, without affecting the layout.
Tim
A: 

If you swipe your mouse rapidly from left to right so that it crosses 3 rectangles

     [ 1 ] [ 2 ] [ 3 ] 

the mouse will be entering rectangle #3 via its left border div immediately after it has exited rectangle #2 via its right border div. However, the assignment inClass="left" outClass="left" when rectangle #3 is entered via its left border div occurs BEFORE the outHandler for rectangle #2 has fired.

Tim
A: 

If I were going to implement this functionality, here's how I'd approach it:

  1. Create image map areas defining the top, bottom, left, and right borders for the image.
  2. For each image where you want this intelligence (i.e. what side was it entered on, what side was it departed from): a) track the sideEntered -- this value is assigned once and only once; whenever an image-map border area is entered its mouseover handler will consult sideEntered and assign it only if it has not yet been assigned; b) track the lastSideTouched -- whenever an image map area is entered its mouseoverhandler reassigns this variable c) when the mouse leaves the rectangle, the leave handler consults lastSideTouched and resets sideEntered to null

Each rectangle should have its own handlers so the two important values, sideEntered and lastSideTouched, are encapsulated within the scope of its handler.

Tim
See my explanation below of why rapid movement is causing your glitches.
Tim
+4  A: 

Perhaps you shoud consider not to use the divs as "hotspots" but use some Math and Javascript to find the point where the mouse enters and leaves a div. This avoids overlapping/gaps problems. The code below basicaly divides a div in 4 triangled zones. Each zone returns a number when the mouse moves over it. The code needs some finetuning, and you have to decide for yourself where to bind and unbind the events. But I think it takes away most of your flickering problems.

$(".overlayLink").bind("mouseenter mouseleave",function(e){

/** the width and height of the current div **/
var w = $(this).width();
var h = $(this).height();

/** calculate the x and y to get an angle to the center of the div from that x and y. **/
/** gets the x value relative to the center of the DIV and "normalize" it **/
var x = (e.pageX - this.offsetLeft - (w/2)) * ( w > h ? (h/w) : 1 );
var y = (e.pageY - this.offsetTop  - (h/2)) * ( h > w ? (w/h) : 1 );

/** the angle and the direction from where the mouse came in/went out clockwise (TRBL=0123);**/
/** first calculate the angle of the point, 
 add 180 deg to get rid of the negative values
 divide by 90 to get the quadrant
 add 3 and do a modulo by 4  to shift the quadrants to a proper clockwise TRBL (top/right/bottom/left) **/
var direction = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180 ) / 90 ) + 3 )  % 4;


/** do your animations here **/ 
switch(direction) {
 case 0:
  /** animations from the TOP **/
 break;
 case 1:
  /** animations from the RIGHT **/
 break;
 case 2:
  /** animations from the BOTTOM **/
 break;
 case 3:
  /** animations from the LEFT **/
 break;
}});

of course the short notation to get the direction should be:

var direction =  Math.round( Math.atan2(y, x) / 1.57079633 + 5 ) % 4

where 1.57... is Math.PI / 2 This is much more efiicient bit harder for me to explain since it skips the degrees conversion.

Caspar Kleijne
A: 
  • What the problem is (that's causing the glitches)

This code (and the use of inClass & outClass) are problematic. You are changing the state of global variables that are used in the event handlers.

var inClass, outClass; 
jQuery('.overlayLink_overlay_fg div').hover(function(){         
    inClass = jQuery(this).attr('class'); 
},function(){        
    outClass = jQuery(this).attr('class'); 
}); 

This code assumes that outClass was set by the 'local' div, but coulsd have been set by a neighboring top div

    var outHandler = function(){        
    var blue_bg = jQuery(this).find('.overlayLink_overlay_bg');        
    var divClass = outClass;   
  • how [could] it could be fixed with my current code?

Do not manage this as a single, global variable, try managing it with Isolated mannor.

Here is one apprach: I have not tested this code

    jQuery('.overlayLink_overlay_fg div').hover(function() {
        jQuery(this).parents('.overlayLink').attr('mouseEnterClass', jQuery(this).attr('class'));
    }, function() {
        jQuery(this).parents('.overlayLink').attr('mouseLeaveClass', jQuery(this).attr('class'));
    });

In your handlers, refrence this new data.

    var outHandler = function() {
        var blue_bg = jQuery(this).find('.overlayLink_overlay_bg');
        var divClass = jQuery(this).attr('mouseLeaveClass');
        ....
      }

this woulod address the global variable issue. I am not sure what will happen if the 'hover' event occurs before the animation completes.

EDIT: Fixed issues with code.

brian chandley
A: 

Fundamentally, mouse movement events happen as a sequence of dots not as a curved line as we perceive the movement on the screen. The faster you move the mouse the wider the spacing between the dots and the larger the objects the mouse can move over without touching. So ideally you need to know the direction of the last mouse position prior to landing on your object and from that calculate the direction from which it approached. i.e. You need to constantly track the mouse position on the whole page to be certain of getting your effect to work correctly every time.

soid