Découper une image avec jQuery (plugin split/er)

Manipuler le DOM ouvre vraiment des perspectives aussi multiples qu’amusantes. Aujourd’hui, nous allons faire éclater des images, tout du moins en donner l’illusion. Tout ça pour afficher un description ou bien faire un effet de transition dans une galerie d’images.

On voit de plus en plus d’effet de transition en javascript, des ‘fade’, des ’slideToggle’, des ’slide’… Un collègue m’a montré ce site avec cet effet d’explosion qui m’a inspiré ce script.
Alors l’effet est moins spectaculaire, mais assez sympa pour une transition originale je trouve.

J’ai imaginé deux applications possible pour cet effet (testé sur firefox 3.6, chrome 3 et safari 4, sur PC).

imageimage

Voyons la première démonstration, qui propose de révéler une description au survol d’une image et l’explosion de celle-ci.

Comme d’habitude l’idée est de rendre les choses faciles à l’utilisation, alors la structure HTML et le CSS sont très allégés :

une très belle image : Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. image
#container {
	width:900px;
	margin:10px auto;
	position:relative;
}
.explosionBox {
	position:relative;
	float:left;
	margin:0 50px 50px 0;
	width:361px;
	height:276px;
	background:#ddd;
}
.explosionBox p,
.explosionBox img { position:absolute; }
.explosionBox p { padding:10px; }
.hidden { display:none; }

Tout simplement dans votre div, le texte et l’image vont se superposer, et en retirant cette dernière, le texte s’affichera.

Voyons le javascript :

jQuery.fn.split = function() {
	$(this).each(function(){
		$(this).addClass("closed");
		$(this).hover(function(){
			if ($(this).hasClass("closed")) {
				$(this).removeClass("closed").addClass("opened");

				var image = $("img", this);
				var imageSrc = image.attr("src");

				var largeur = image.width();
				var hauteur = image.height();
				var semiLargeur = (image.width())/2;
				var semiHauteur = (image.height())/2;

				$(image).addClass("hidden");
				var box = "
image
" for (i = 0; i < 4; i++) { $(box).appendTo(this); } $(".box", this).css({width : largeur, height : hauteur}).wrap("
"); $(".quart", this).css({width : semiLargeur, height : semiHauteur}); $(".box:eq(0)", this).css({left : 0, top : 0}); $(".box:eq(1)", this).css({left : -semiLargeur, top : 0}); $(".box:eq(2)", this).css({left : 0, top : -semiHauteur}); $(".box:eq(3)", this).css({left : -semiLargeur, top : -semiHauteur}); $(".quart:eq(0)", this).css({left : 0, top : 0}).animate({left : -semiLargeur, top : -semiHauteur, opacity : 0},300); $(".quart:eq(1)", this).css({left : semiLargeur, top : 0}).animate({left : largeur, top : -semiHauteur, opacity : 0},300); $(".quart:eq(2)", this).css({left : 0, top : semiHauteur}).animate({left : -semiLargeur, top : hauteur, opacity : 0},300); $(".quart:eq(3)", this).css({left : semiLargeur, top : semiHauteur}).animate({left : largeur, top : hauteur, opacity : 0},300); } },function(){ if ($(this).hasClass("opened")) { $(this).removeClass("opened"); var image = $("img", this); var semiLargeur = (image.width())/2; var semiHauteur = (image.height())/2; $(".quart:eq(0)", this).animate({left : 0, top : 0, opacity : 1},300, function(){ $(this).prev("img").removeClass("hidden"); $(this).parent("div").addClass("closed"); $(this).remove(); }); $(".quart:eq(1)", this).animate({left : semiLargeur, top : 0, opacity : 1},300, function(){ $(this).remove(); }); $(".quart:eq(2)", this).animate({left : 0, top : semiHauteur, opacity : 1},300, function(){ $(this).remove(); }); $(".quart:eq(3)", this).animate({left : semiLargeur, top : semiHauteur, opacity : 1},300, function(){ $(this).remove(); }); } }); }); };

Et voici comment l’appeler :

$(document).ready(function(){
	$(".explosionBox").split();
});

Vous pouvez appliquer la class sur autant de div que vous le souhaitez.

Voyons le 2e exemple

Voici une autre variante de cet effet de transition, une galerie d’images qui défilent quand on clique sur celles-ci. Mais cette fois comme l’évènement est clique, on peut s’amuser avec la position de celui-ci pour découper l’image à un endroit précis, et renforcer l’effet de découpe!

L’objectif est toujours la simplicité d’intégration, et donc un HTML super light. Ici on appelle uniquement les images dans une div de votre choix, il suffit juste de déclarer le title pour la description.
Important à noter, l’ordre d’affichage se fait d’abord par la dernière image et remonte le DOM, et il faut indiquer les dimension (width et height) de la dernière image pour que l’affichage soit correct sur chrome et safari au chargement de la page. (Mais vous déclarez déjà les dimensions puisque c’est une recommandation du W3C, non? ^^)
Voici la structure de base :

image 1 image 2 image 3 image 4 image 5

C’en est déconcertant de simplicité. Et voici le CSS de base :

#container {
	width:900px;
	margin:10px auto;
}
.splitBox {
	position:relative;
	float:left;
	margin:0 20px 0 0;
	background:#fff;
	border:1px solid #ddd;
}
.splitBox:hover {
	/*box-shadow: 0px 0px 4px #ccc;
	-moz-box-shadow:0px 0px 10px #666;
	-webkit-box-shadow:0px 0px 10px #666;*/
}
.splitBox p,
.splitBox img {
	position:absolute;
	cursor:pointer;
}
.splitBox p {
	padding:5px 10px;
	bottom:0;
	margin:0;
	background:rgba(221, 221, 221, 0.9);

}

Vous noterez que j’ai mis en commentaire un effet de shadow-box sur le bloc lors du survol qui est du plus bel effet, mais ralenti un peu l’animation d’explosion, donc selon votre choix.

Voici le javascript qui rend vivant tout ça :

jQuery.fn.spliter = function(){
	$(this).each(function(){
		var current = 1;
		var etat = false;
		var images = $(this).children("img");
		var chiffre = images.length;
		var i = chiffre - 1;
		images.hide();
		images.last().show();
		var largeur = images.last().width();
		var hauteur = images.last().height();

		var contentP = $("img:eq(" + i + ")", this).attr("title");
		$(this).append("

"); var largeurPPadding = $("p", this).outerWidth(); $(this).css({ width: largeur, height: hauteur }); $("p", this).css({ width: largeur - largeurPPadding }).html(current + "/" + chiffre + " : " + contentP); $(this).click(function(e){ if (etat == false) { etat = true; current++; if (current == chiffre + 1) { current = 1; } var i = ($("img", this).length) - 1; var image = $("img:eq(" + i + ")", this); var imageSrc = image.attr("src"); var largeur = image.width(); var hauteur = image.height(); var posBox = $(this).position(); var posX = e.pageX - posBox.left; var posY = e.pageY - posBox.top; var resteX = largeur - posX; var resteY = hauteur - posY; var imageNext = i - 1; var largeurPrev = $("img:eq(" + imageNext + ")", this).width(); var hauteurPrev = $("img:eq(" + imageNext + ")", this).height(); var largeurP = $("img:eq(" + imageNext + ")", this).width(); $(this).animate({ width: largeurPrev, height: hauteurPrev }, 600); var box = "

image
" for (i = 0; i < 4; i++) { $(box).appendTo(this); } $(".box").css({ width: largeur, height: hauteur }).wrap("
"); $(".box:eq(0)").css({ left: 0, top: 0 }); $(".box:eq(1)").css({ left: -posX, top: 0 }); $(".box:eq(2)").css({ left: 0, top: -posY }); $(".box:eq(3)").css({ left: -posX, top: -posY }); $(".quart:eq(0)").css({ left: 0, top: 0, width: posX, height: posY }).animate({ left: -posX, top: -posY, opacity: 0 }, 600); $(".quart:eq(1)").css({ left: posX, top: 0, width: resteX, height: posY }).animate({ left: largeur, top: -posY, opacity: 0 }, 600); $(".quart:eq(2)").css({ left: 0, top: posY, width: posX, height: resteY }).animate({ left: -posX, top: hauteur, opacity: 0 }, 600); $(".quart:eq(3)").css({ left: posX, top: posY, width: resteX, height: resteY }).animate({ left: largeur, top: hauteur, opacity: 0 }, 600, function(){ etat = false; $(".quart").remove(); }); $(image).prev("img").fadeIn(800); $(image).hide().prependTo(this); var contentP = $("p", this).prev("img").attr("title"); $("p", this).animate({ width: largeurP - largeurPPadding }, 600).html(current + "/" + chiffre + " : " + contentP); } }); $(this).hover(function(){ $("p", this).fadeIn(500); }, function(){ $("p", this).stop(true, true).fadeOut(); }); }); };

J’essairai de prendre le temps de poser quelques commentaires sur le script, mais si vous avez des questions n’hésitez pas à les poser.
Et l’appel du plugin :

$(document).ready(function(){
	$(".splitBox").spliter();
});

Voilà pour cet effet qui peut être repris de nombreuses façons. On m’a lancé le défi de réaliser quelque chose dans le même genre, mais avec une transition en ondulations, comme à la surface de l’eau… Avec du CSS3, sans doute possible… Encore pas mal de casse-tête en vue.

A bientôt

10 réponses à “Découper une image avec jQuery (plugin split/er)”

  1. 1
    sebastien dit :

    demo1 : c’est dommage, l’effet ne marche pas quand on passe d’une image à lautre. Il faut aller sur une image, enlever son surseur, et une fois que l’image s’est reformé on peut le faire sur une autre image, c’est dommage. est-ce qu’on peut pas donné des id aux images pour que l’effet soit valable sur une image même si il n’est pas achevé sur la precedente ?

    • 1.1
      admin dit :

      J’ai bien tenté que ce soit possible, mais cela générait des erreurs d’affichage. C’est d’ailleurs aussi pour ça que j’ai utilisé les états, et c’est lui qui bloque pour le moment.
      Donner des id, sans doute, mais je pense qu’il doit y avoir une solution encore plus simple, en gérant mieux les sélecteurs. Je vais y réfléchir, et si tu trouves la solutions n’hésite pas à la partager. :)

    • 1.2
      admin dit :

      Et voilà, j’ai ajouté ce qu’il fallait pour que l’effet fonctionne sur plusieurs images à la fois.
      Et on peut même faire plusieurs galeries de la démo 2. :)
      Tout ça grâce à ‘.each()’ !

  2. 2
    s.patoux dit :

    Ah bin ouais celui-là il est vraiment très beau de plug-in… bien joué Seb ;o)

  3. 3
    rhobli dit :

    Bonjour et merci pour ce tuto.

    Je ne m’y connais pas trop en js mais j’ai réussis à faire marcher l’exemple 1.
    Par quoi devrait on remplacer le .hover dans le split.js pour obtenir le même résultat après 5 secondes ? sans cliquer ni passer la souris dessus.

    Merci.

    • 3.1
      admin dit :

      Bonjour, merci pour le commentaire et d’avoir essayé ce plugin.
      C’est tout à fait possible, il suffit de mettre un setTimeout à la place du hover.
      Mais dans ce cas on ne retourne pas à l’état initial, à moins de remettre un second setTimeout.

      Pour le moment voilà ce que je peux te proposer, les images éclatent au bout d’une seconde pour faire apparaître un contenu :

      jQuery.fn.split = function() {
      	$(this).each(function(){
      
      		var $that = $(this);
      
      		setTimeout(function(){
      
      				var image = $that.find('img');
      				var imageSrc = image.attr("src");
      
      				var largeur = image.width();
      				var hauteur = image.height();
      				var semiLargeur = (image.width())/2;
      				var semiHauteur = (image.height())/2;
      
      				$(image).addClass("hidden");
      				var box = "
      
      image
      " for (i = 0; i < 4; i++) { $(box).appendTo($that); } $that.find('.box').css({width : largeur, height : hauteur}).wrap("
      "); $that.find('.quart').css({width : semiLargeur, height : semiHauteur}); $that.find('.box:eq(0)').css({left : 0, top : 0}); $that.find('.box:eq(1)').css({left : -semiLargeur, top : 0}); $that.find('.box:eq(2)').css({left : 0, top : -semiHauteur}); $that.find('.box:eq(3)').css({left : -semiLargeur, top : -semiHauteur}); $that.find('.quart:eq(0)').css({left : 0, top : 0}).animate({left : -semiLargeur, top : -semiHauteur, opacity : 0},800); $that.find('.quart:eq(1)').css({left : semiLargeur, top : 0}).animate({left : largeur, top : -semiHauteur, opacity : 0},800); $that.find('.quart:eq(2)').css({left : 0, top : semiHauteur}).animate({left : -semiLargeur, top : hauteur, opacity : 0},800); $that.find('.quart:eq(3)').css({left : semiLargeur, top : semiHauteur}).animate({left : largeur, top : hauteur, opacity : 0},800); },1000); }); };

      C’est une possibilité, mais on peut varier de nombreuses façons

  4. 4
    rhobli dit :

    Au cas ou ça intéresserait quelqu’un : Remplacer le .hover par .show dans le split.js et dans l’index ajouter un setTime comme suit:
    ‘$(document).ready(function() {
    setTimeout(function() {
    $(« .explosionBox »).split();
    }, 5000);
    });’

    Avec le .show, j’ai du ajouter : ‘$(this).show(« slow »,function()’ le slow ne servant à rien … n’y aurait il pas plus simple ?

    • 4.1
      admin dit :

      Tout à fait ça marche très bien de le mettre à cet endroit aussi.
      Pour show, si tu utilises un callback, tu dois mettre une durée dans ta fonction.
      En fin de compte le script au dessus est peut-être plus simple.

  5. 5
    rhobli dit :

    Merci d’avoir pris le temps de rectifier. Effectivement ça semble plus léger comme solution avec un setTimeout à cet endroit, je n’ai pas encore eu le temps de la tester.
    Par contre j’essaie de donner l’impression d’une image qui se déchire avec une déchirure en croix puis avec des bords intérieurs qui ont l’aspect déchiré.