Share


I found it could help some of you who fell in love with the Mashable concept of "Drag-to-Share" designed by Meebo. You can see it in action at mashable.com.

This is the killer !
Before I dive into the code, I just want to mention that this script starts from the implementation by Dan Wellman from NetTuts+. Dan's script is great, amazing and helpful. So, I am just adding what I fought was important regarding the user exprience, ... the shadow ! So, I strongly recommand you follow this tutorial before you continue with this one.

The source files are available for download here , and a demo can be found here.
Problem :
Add shadow to the hovered DOM element (here, an image).

Solution :
The solution I propose here is quite radical. What I do is create an overlay to the document which containts the image, wrapped inside a div surrounded with the shadow elements. Here is how I proceed :
On MouseEnter :
- first clone the image's DOM element,
- wraps it with the shadowing elements (see below),
- add it some 'draggability' (With jQuery Drag And Drop plugin)
- append the resulting DOM element (called 'shadowee') to the 'body' element.
On MouseLeave :
- just remove the 'shadowee' from the DOM tree !

Requirements :
jQuery + jQuery UI

You may want to pick them right from this storage-saver links :





The other aspects of the drag-and-drop have been documented in Dan's tutorial.

We get started !
We first generate the shadows by creating a {n-s-e-w}-based positionning illustrated here :
This image also illustrates how images have been cut for the backgrounds creation. N and S div's are 1 px width, and E and W, 1 px height. The images must be transparent, PNG or whatever format you want. Browse the archive given before to pick the sample I have prepared.

The html content of the shadows' wrapper is contained in the variable 'draggable_shadower'.

Here is it's content :

var draggable_shadower = '










';


We need to style all this, what is done by the CSS below, feel free to modify it to your convenience.

18px, represents here the shadow expansion, so it needs to be adapted.

#draggable_shadower{
position : absolute;
}
div.shadower_bg {
position: absolute;
display: block;
z-index: 70;
border: 0;
padding: 0;
margin: 0;
}

div#shadower_n {
top: -18px;
left: 0;
width: 100%;
height: 18px;
background: transparent url('imgs/shadower_n.png') repeat-x;
}

div#shadower_ne {
top: -18px;
right: -18px;
width: 18px;
height: 18px;
background: transparent url('imgs/shadower_ne.png') no-repeat;
}

div#shadower_e {
right: -18px;
height: 100%;
width: 18px;
background: transparent url('imgs/shadower_e.png') repeat-y;
}

div#shadower_se {
bottom: -18px;
right: -18px;
width: 18px;
height: 18px;
background: transparent url('imgs/shadower_se.png') no-repeat;
}

div#shadower_s {
bottom: -18px;
left: 0;
width: 100%;
height: 18px;
background: transparent url('imgs/shadower_s.png') repeat-x;
}

div#shadower_sw {
bottom: -18px;
left: -18px;
width: 18px;
height: 18px;
background: transparent url('imgs/shadower_sw.png') no-repeat;
}

div#shadower_w {
left: -18px;
height: 100%;
width: 18px;
background: transparent url('imgs/shadower_w.png') repeat-y;
}

div#shadower_nw {
top: -18px;
left: -18px;
width: 18px;
height: 18px;
background: transparent url('imgs/shadower_nw.png') no-repeat;
}

The sexy side of the post is over, we get all this to work by adding some interaction !

Let's be a little odd : we starts with the targets !

JS :

$("#targets li").droppable({
tolerance: "pointer",
//show info when over target
over: function() {
$(".dts_share", "#targets").remove();
$("").addClass("dts_share").text("Partager sur " + $(this).attr("id")).addClass("active").appendTo($(this)).fadeIn();
},
drop: function() {
var id = $(this).attr("id"),
baseUrl = $(this).find("a").attr("href");

if (id.indexOf("twitter") != -1) {
window.location.href = baseUrl + "/home?status=" + title + ": " + shared_url;
} else if (id.indexOf("delicious") != -1) {
window.location.href = baseUrl + "/save?url=" + shared_url+ "&title=" + title;
} else if (id.indexOf("facebook") != -1) {
window.location.href = baseUrl + "/sharer.php?u=" + shared_url+ "&t=" + title;
}
}
});

Again, if you haven't yet, jump to the Dan's tutorial to see what it looks like and how it is styled. I am giving here the CSS to handle the HTML elements generated from this function.
CSS :

.dts_share{
color : white;
font-weight:bold;
position:absolute;
font-size:14px;
font-family:Verdana;
width : 200px;
margin-top:70px;
margin-left:-40px;
}


The main function to handle the shadow adding is 'draggable_add_shadow'.
What it does first is get the offsets (horizontal and vertical) of the target image that have been hovered. The corresponding jQuery object is passed as parameter to this function.
The 'draggable_shadower' string is used to create a new DOM element, which is combo-styled (I mean creation and style are done in the same line of code, another reason to love jQuery !). We also add the 'mouseleave' event handle and append the resulting element to the document body, by the way !

function draggable_add_shadow(target){
var x_= target.offset().left,
y_ = target.offset().top,
h_ = target.height(),
w_ = target.width();
$(draggable_shadower).css({"margin-left" : x_, "margin-top" : y_, "width" : w_, "height" : h_}).mouseleave(draggable_remove_shadow).appendTo("body");
var clonee = target.clone();
make_draggable(clonee);
clonee.addClass("draggable").appendTo("#shadower_content");
}


As you can see in this method, the cloning process is done with the jQuery 'clone()' fonction.
The resulting DOM element is made 'draggable' with the 'make_draggable' method (see below).
I assume all this is clear enough, but if not you may consider looking at the Dan's tutorial on how this stuff works, I just don't want to dive into it again, it's almost useless at this point for those who want to get it working.

Here, the 'make_draggable' method; the 'target' argument is a jQuery object wrapping the image DOM element :

function make_draggable(target){
target.draggable({
helper: function() {
var img_src = target.attr("src");
return $("
").attr("id", "helper").html("" + title + "").appendTo("body");
},
cursor: "pointer",
cursorAt: { left: -10, top: 20 },
zIndex: 99999,
//show overlay and targets
start: function() {
prevent_shadow_remove = true;
$("
").attr("id", "overlay").css("opacity", 0.7).appendTo("body");
$("#tip").remove();
$(this).unbind("mouseenter");
$("#targets").css("left", ($("body").width() / 2) - $("#targets").width() / 2).slideDown();
},
//remove targets and overlay
stop: function() {
$("#targets").slideUp();
$(".dts_share", "#targets").remove();
$("#overlay").remove();
$(this).bind("mouseenter", createTip);
prevent_shadow_remove = false;
draggable_remove_shadow();
}
});
target.bind("mouseenter", createTip).mousemove(function(e) {
$("#tip").css({ left:e.pageX + 30, top:e.pageY - 16 });
}).mouseleave(function() {
$("#tip").fadeOut(300, function(){$("#tip").remove()});
draggable_remove_shadow();
});
}

This method appends to the document body '#overlay'(which needs to be styled to play it's expected role of overlay), '#tip', '#helper' :

#overlay {
background-color:#000000;
height:100%;
left:0;
position:fixed;
top:0;
width:100%;
z-index:99997;
}
#helper{
background-color:#c2c2c2; position:absolute; height:35px;
padding:15px 70px 0 20px; color:#fff;
font-family:Verdana, Arial, Helvetica, sans-serif; font-weight:bold;
font-size:18px; border-radius:8px; -moz-border-radius:8px;
-webkit-border-radius:8px; border:3px solid #7d7d7d;
}
#thumb{
width:50px; height:50px; position:absolute; right:0; top:0;
border-left:3px solid #7d7d7d;
}

The 'draggable_remove_shadow' method checks the 'prevent_shadow_remove' boolean before removing '#draggable_shadower' div.
This is a workaround to avoid a bug a found went I remove this div during the drag.

I don't understand quite well why this is happening but anyway, since it is working !

function draggable_remove_shadow(){
if(!prevent_shadow_remove)
$("#draggable_shadower").remove();
}

The 'createTip' method is Dan's. I am pasting it below ...

function createTip(e){
//create tool tip if it doesn't exist
($("#tip").length === 0) ?
$("
").html("Drag this image to share the page").attr("id", "tip").css({ left:e.pageX + 30, top:e.pageY - 16 }).appendTo("body").fadeIn(500) :
null;
}

... with some CSS to style it :

#tip {
position:absolute; display:none; height:25px; padding:9px 9px 0px;
color:#fff; font-family:Verdana, Arial, Helvetica, sans-serif;
font-size:11px; font-weight:bold; border-radius:4px;
-moz-border-radius:4px; -webkit-border-radius:4px;
background:#000; background:rgba(0,0,0,.5);
}
#tip .arrow {
width:0; height:0; line-height:0; border-right:8px solid #000;
border-right:8px solid rgba(0,0,0,.5); border-top:8px solid transparent;
border-bottom:8px solid transparent; position:absolute; left:-8px;
top:9px;
}


That's it !
I have implemented this 'drag-to-share' feature on this blog, so you can experience it right from here.
Hope this helps.