2011-03-19 1 views
9

Ma première question ici. :)JavaScript rétrécissement/croissance cercle de transition

Je cherche une transition entre deux images où l'image se rétrécit d'abord en forme de cercle puis le cercle se développe à nouveau contenant l'autre image. C'est difficile à expliquer, et je peux utiliser les mauvais mots, parce que je ne peux rien trouver à ce sujet sur Interwebz. Je parle d'un effet comme la fin de Loony Toons. http://www.youtube.com/watch?v=ZuYIq-J5l9I

Ce rétrécissement-à-noir, peut-il être fait dans JavaScript/JQuery?

Répondre

7

TL: DR

Comment puis-je commencer même à décrire celui-ci? Ce serait beaucoup plus facile si la norme CSS 2 supportait autre chose qu'une valeur "rect", à savoir un "cercle" ou une "ellipse" mais ... comme ça n'existe pas, j'ai fait de mon mieux pour arranger quelque chose ensemble, cela fera ce que vous demandez. Les mises en garde sont nombreuses. Le premier est que cela va seulement travailler sur quelque chose avec un fond de couleur unie dans le cas où vous vouliez que l'image se clipse en arrière-plan. Un autre est que, bien que j'aie essayé de tenir compte de la synchronisation des mises à jour CSS entre les navigateurs, le rendu n'est toujours pas "parfait". Mon approche initiale consistait simplement à animer le clip sur l'image qui était remplacée, mais cela n'a pas fonctionné en raison de la façon dont les mises à jour ont été faites à l'écrêtage via la fonction d'accélération du plugin que j'ai localisé. L'approche finale est ci-dessous.

L'approche

Le concept est de définir l'image en tant que propriété background-image d'un récipient comme un <div> avec un background-position de center center, et le position du récipient à relative, ou toute non-statique. La prochaine consiste à générer les éléments de découpage en tant qu'enfants du conteneur. Le premier est une image de cercle de découpage position: absolute de la couleur de votre fond, soit PNG transparent ou GIF (je préfère l'ancien), et les quatre suivants sont divs, également avec absolute positions qui ont left, right, top, et bottom ensemble d'attributs à 0 pour chacun des côtés respectifs qu'ils vont couper. L'idée est d'animer les top, left, width et height de l'image du cercle de détourage et de synchroniser la largeur et la hauteur des divs de découpage en utilisant l'option de rappel de l'appel .animate() en les faisant correspondre au left actuel et top valeurs. Entre les animations, vous modifiez le background-image du conteneur à la nouvelle image, puis démarrez l'animation dans la direction opposée.

Cela nécessitait un peu de finesse dans les navigateurs IE7, 8 et Webkit, car l'animation était beaucoup plus nette dans Firefox et IE9. Ce sera la variable adjust que vous verrez dans la démo de travail.

L'exemple de code est ci-dessous:

Le balisage

<div class="imageContainer image1"> 
    <img class="clip" src="http://www.mysite.com/images/clipCircle.png" /> 
    <div class="top fill"></div> 
    <div class="left fill"></div> 
    <div class="right fill"></div> 
    <div class="bottom fill"></div> 
</div> 

Le CSS

div.imageContainer 
{ 
    background-position: center; 
    width: 300px; 
    height: 300px; 
    position: relative; 
} 

img.clip 
{ 
    width: 100%; 
    height: 100%; 
    position: absolute; 
} 

div.fill 
{ 
    position: absolute; 
    background-color: White; 
} 

div.left, div.right 
{ 
    height: 100%; 
    top: 0; 
    width: 0; 
} 

div.left 
{ 
    left: 0; 
} 

div.right 
{ 
    right: 0; 
} 

div.top, div.bottom 
{ 
    width: 100%; 
    left: 0; 
    height: 0; 
} 

div.top 
{ 
    top: 0; 
} 

div.bottom 
{ 
    bottom: 0; 
} 

Le script

var speed = 1000; 

$clip = $("img.clip"); 

$clip.animate({ 
    top: $clip.parent().height()/2, 
    left: $clip.parent().width()/2, 
    width: 0, 
    height: 0 
}, { 
    duration: speed, 
    step: function(now, fx) { 
     switch (fx.prop) { 
     case "top": 
      $("div.top").css("height", now); 
      $("div.bottom").css("height", now + adjust);  
      break; 
     case "left": 
      $("div.left").css("width", now); 
      $("div.right").css("width", now + adjust); 
     } 
    }, 
    complete: function() { 
     $(this).parent().addClass("image2"); 

     $(this).animate({ 
      top: 0, 
      left: 0, 
      width: $clip.parent().width(), 
      height: $clip.parent().height() 
     }, { 
      duration: speed, 
      step: function(now, fx) { 
       switch (fx.prop) { 
       case "top": 
        $("div.top").css("height", now); 
        $("div.bottom").css("height", now + adjust);  
        break; 
       case "left": 
        $("div.left").css("width", now); 
        $("div.right").css("width", now + adjust); 
       } 
      }, 
      complete: function() { 
       $("div.imageContainer > *").removeAttr("style"); 
      } 
     }); 
    } 
}); 

EDIT:

La solution CSS3

Lorsque la compatibilité multi-navigateur est moins préoccupante, CSS3 est une option (même si je vous suggère probablement de voir ce qui peut être fait avec le nouveau HTML5 Canvas pour ce genre d'animation). Il y a un certain nombre de choses à noter:

  • L'image doit être à l'intérieur d'un conteneur afin de nous permettre de nous rapprocher de son centre plutôt que de son coin supérieur gauche.
  • L'attribut border-radius ne coupera pas les images enfants dans un conteneur. Pour cette raison, l'image doit devenir l'attribut background-image du conteneur.
  • jQuery n'anime pas correctement le border-radius. Vous pouvez soit remplacer la fonctionnalité d'animation jQuery actuelle pour cet attribut, soit créer un objet d'animation border-radius personnalisé pour rendre jQuery plus sages. J'ai opté pour le dernier. Le rayon de bordure de chaque coin doit être animé séparément.
  • L'animation d'entrée ou de sortie se compose de deux segments distincts, et par conséquent, la fonction d'accélération "linéaire" est probablement mieux utilisée pour obtenir des résultats plus nets.

La méthode est commentée en ligne ci-dessous:

Le balisage

<div class="imageContainer image1"> 
</div> 

Le CSS

div.imageContainer 
{ 
    background-position: 0px 0px; 
    background-repeat: no-repeat; 
    width: 300px; 
    height: 300px; 
    position: absolute; 
    top: 0; 
    left: 0; 
} 

div.image1 
{ 
    background-image: url(http://www.mysite.com/images/myFirstImage.png); 
} 

div.image2 
{ 
    background-image: url(http://www.mysite.com/images/mySecondImage.png); 
} 

Le script

// Total animation speed in or out will be speed * 1.5 
var speed = 600; 

// Store a reference to the object to be clipped 
var $clip = $("div") 

// A function to build a mapping object for border radius parameters 
var buildRadiusObj = function(value) { 

    // Dimension an option object 
    var opts = {}; 

    // Use specialized Mozilla CSS attributes when needed 
    var attributes = $.browser.mozilla ? 
     ["-moz-border-radius-topleft", 
     "-moz-border-radius-bottomleft", 
     "-moz-border-radius-topright", 
     "-moz-border-radius-bottomright"] : 
     ["border-top-left-radius", 
     "border-bottom-left-radius", 
     "border-top-right-radius", 
     "border-bottom-right-radius"]; 

    // Build the option object 
    $.each(attributes, function(i, key) { 
     opts[key] = value; 
    }); 

    // Return the result 
    return opts; 
} 

$clip.animate(buildRadiusObj($clip.width() * 0.5), { // Animate the border radius until circular 
    duration: speed * 0.5, 
    easing: "linear" 
}).animate({           // Resize and reposition the container 
    width: 0, 
    left: $clip.width()/2, 
    height: 0, 
    top: $clip.height()/2 
}, { 
    duration: speed, 
    easing: "linear", 
    step: function(now, fx) {       // Synch up the background-position 
     if (fx.prop == "top") { 
      $(this).css("background-position", "-" + $(this).css("top") + " -" + $(this).css("left")); 
     } 
    }, 
    complete: function() {        // Swap the image 
     $(this).addClass("image2"); 
    } 
}).animate({           // Restore position and size 
    width: $clip.width(), 
    left: 0, 
    height: $clip.height(), 
    top: 0 
}, { 
    duration: speed, 
    easing: "linear", 
    step: function(now, fx) {       // Synch the background-position 
     if (fx.prop == "top") { 
      $(this).css("background-position", "-" + $(this).css("top") + " -" + $(this).css("left")); 
     } 
    }, 
    complete: function() {        // Remove inline styles but reapply border-radius 
     $(this).removeAttr("style").css(buildRadiusObj($clip.width() * 0.5)); 
    } 
}).animate(buildRadiusObj(0), {       // Restore border-radius to block 
    duration: speed * 0.5, 
    easing: "linear", 
    complete: function() { 
     $(this).removeAttr("style");     // Remove inline styles 
    } 
}); 

Again, the demo is located here.

+0

Ça a l'air très bien, même si j'en aurai probablement besoin pour travailler sur un fond aussi. J'aurais dû le mentionner, mais je ne le savais pas au moment où j'ai posté la question. : -/ – GolezTrol

+0

Mais j'ai pensé que je pourrais peut-être mettre l'image dans un div avec (css3) bords arrondis et rétrécir ce div. L'image d'arrière-plan sera tronquée à la frontière même si elle est arrondie, non? Ensuite, j'ai seulement besoin d'animer la taille et la position de ce div et la position de l'arrière-plan. Dans IE et (les autres) anciens navigateurs, il s'anime en carré, mais je peux vivre avec ça si cela me donne la possibilité d'utiliser cette animation sur un fond. Je vais essayer de comprendre comment cette animation fonctionne et comment je peux l'adapter à mes besoins. Merci déjà pour la grande quantité d'efforts. – GolezTrol

+0

@GolezTrol Eh bien, cela devient probablement beaucoup plus facile si vous ne vous souciez pas des solutions multi-navigateur, pour ce que ça vaut. Cependant, dans le monde de IE7/8 ... oy ... Ouais ce sera un bordel sans Flash ou similaire. – lsuarez

1

Je suis venu à travers cela, j'espère que c'est intéressant: http://www.netzgesta.de/transm/. La transition circles_out avec un cercle pourrait faire le travail je pense.

+0

Cela semble bien, même si je n'ai pas encore réussi à le faire fonctionner. J'essaierai encore plus cet après-midi. – GolezTrol

0

Voilà, c'est parti. http://jquery.malsup.com/cycle/ Découvrez le zoom. Quelque chose peut être élaboré avec la partie du cercle.

+0

Oui, j'ai trouvé celui-là, mais ce n'est pas ce dont j'ai besoin. Je ne veux pas réduire l'image elle-même, mais seulement l'espace qui l'entoure, de sorte qu'une partie de plus en plus petite de l'image devient visible. – GolezTrol

0

J'ai essayé un peu plus et est venu avec l'idée d'utiliser un élément <canvas>.

Veuillez voir le résultat à: http://jsfiddle.net/3MG8e/2/.

var cv = $('canvas')[0]; 
var ctx = cv.getContext('2d'); 
ctx.fillStyle = 'black'; 

var int = null; 
var t = -1; 
var amount = 50; 
var time = 1000; 
var size = 0; 

var im = new Image(); 
im.src = "http://burzak.com/proj/fxcanvas/docs/images/mario2.png"; 
im.onload = function() { 
    size = im.width; 
    int = setInterval(update, time/amount); 
} 

function update() { 
    if(++t >= amount) { 
     clearInterval(int); 
    } 
    ctx.fillRect(0, 0, cv.width, cv.height); 
    ctx.beginPath(); 
    ctx.arc(size/2, size/2, 
      size/2 - t * (size/2)/amount, 
      0, Math.PI*2, 
      false); 
    ctx.clip(); 
    ctx.drawImage(im, 0, 0, size, size); 
} 
Questions connexes