32 votes

animation javascript/css de dom node à dom node

ng-animate-ref permet de créer une transition d'un nœud de dom à un autre .

ng-animate utilise tous les styles css tels que position , font-size , font-color et plus du premier élément dom et du deuxième élément dom et crée une animation css 3 pour déplacer l'élément de l'état a d'affirmer b .

C'est exactement ce dont j'ai besoin mais malheureusement je ne peux pas utiliser Angular 1 dans le projet actuel.

Existe-t-il un moyen réutilisable de réaliser la même animation css3 sans déplacer tous les styles de mes fichiers css vers javascript ?

Pour illustrer le problème, veuillez consulter l'exemple suivant. Comme vous pouvez le constater, l'exemple a pas de code d'animation javascript personnalisé mais seulement du code javascript qui gère la logique d'état en changeant les éléments de la liste. a à b .

La définition de l'animation est écrite en css pur .

Démonstration :
https://codepen.io/jonespen/pen/avBZpO/

Avant-première :
Preview of the demo

18voto

TTCC Points 845

Bien sûr, jQuery animate peut le faire sans aucun plugin.

Il n'y a peut-être pas beaucoup de lignes de code, mais elles présentent une certaine complexité.

Ici est ce que vous voulez ( ps : jquery-ui à utiliser uniquement pour changer de couleur).

$(document).ready(function() {
  var animating = false,
    durtion = 300;
  $('.items').on("click", ".items-link", function() {
    if (animating) return;
    animating = true;
    var $this = $(this),
      dir = $this.parent().hasClass("items-l") ? "r" : "l",
      color = dir == "l" ? "#0000FF" : "#F00000",
      index = $this.attr("data-index");

    var toItems = $('.items-' + dir),
      itemsLinks = toItems.find(".items-link"),
      newEle = $this.clone(true),
      nextEle = $this.next(),
      toEle;

    if (itemsLinks.length == 0) {
      toItems.append(newEle)
    } else {
      itemsLinks.each(function() {
        if ($(this).attr("data-index") > index) {
          toEle = $(this);
          return false;
        }
      });
      if (toEle) {
        toEle.before(newEle).animate({
          "marginTop": $this.outerHeight()
        }, durtion, function() {
          toEle.css("marginTop", 0);
        });
      } else {
        toEle = itemsLinks.last();
        toEle.after(newEle)
      }
    }

    nextEle && nextEle.css("marginTop", $this.outerHeight())
      .animate({
        "marginTop": 0
      }, durtion);

    var animate = newEle.position();
    animate["background-color"] = color;
    newEle.hide() && $this.css('position', 'absolute')
      .animate(animate, durtion, function() {
        newEle.show();
        $this.remove();
        animating = false;
      });
  });
});

.items {
  padding: 0;
  -webkit-transition: 300ms linear all;
  transition: 300ms linear all;
}
.items.items-l {
  float: left
}
.items.items-r {
  float: right
}
.items.items-l a {
  background: #0000FF
}
.items.items-r a {
  background: #F00000
}
.items a,
.items-link {
  color: #fff;
  padding: 10px;
  display: block;
}
.main {
  width: 100%;
}

<script type="text/javascript" src="//code.jquery.com/jquery-1.9.1.js">
</script>
<script type="text/javascript" src="//code.jquery.com/ui/1.9.2/jquery-ui.js">
</script>
<link rel="stylesheet" type="text/css" href="http://stackoverflow.com//code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css">
<div class="main">
  <div class="items items-l">
    <a class="items-link" data-index="1" href="#">Item 1</a>
    <a class="items-link" data-index="2" href="#">Item 2</a>
    <a class="items-link" data-index="3" href="#">Item 3</a> 
    <a class="items-link" data-index="4" href="#">Item 4</a>
    </div>
    <div class="items items-r">
      <a href="#" class="items-link" data-index="5">Item 5</a>
      <a href="#" class="items-link" data-index="6">Item 6</a>
      <a href="#" class="items-link" data-index="7">Item 7</a>
      <a href="#" class="items-link" data-index="8">Item 8</a>
  </div>

8voto

user3297291 Points 14888

Une solution javascript simple qui utilise :

  • HTMLElement.getBoundingClientRect pour trouver des différences entre les anciennes et les nouvelles positions de l'élément
  • css transition d'animer
  • css transform pour traduire

Explication de l'approche :

L'idée de base est de faire en sorte que le navigateur calcule/remplit uniquement le DOM une fois . Nous nous occuperons nous-mêmes de la transition entre l'état initial et ce nouvel état.

En n'effectuant que la transition (a), le GPU accéléré transform de la propriété, sur (b) une petite sélection d'éléments (tous <li> éléments), nous essaierons d'assurer une fréquence d'images élevée.

// Store references to DOM elements we'll need:
var lists = [
  document.querySelector(".js-list0"),
  document.querySelector(".js-list1")
];
var items = Array.prototype.slice.call(document.querySelectorAll("li"));

// The function that triggers the css transitions:
var transition = (function() { 
  var keyIndex = 0,
      bboxesBefore = {},
      bboxesAfter = {},
      storeBbox = function(obj, element) {
        var key = element.getAttribute("data-key");
        if (!key) {
          element.setAttribute("data-key", "KEY_" + keyIndex++);
          return storeBbox(obj, element);
        }

        obj[key] = element.getBoundingClientRect();
      },
      storeBboxes = function(obj, elements) {
        return elements.forEach(storeBbox.bind(null, obj));
      };

  // `action` is a function that modifies the DOM from state *before* to state *after*
  // `elements` is an array of HTMLElements which we want to monitor and transition
  return function(action, elements) {
    if (!elements || !elements.length) {
      return action();
    }

    // Store old position
    storeBboxes(bboxesBefore, elements);

    // Turn off animation
    document.body.classList.toggle("animated", false);

    // Call action that moves stuff around
    action();

    // Store new position
    storeBboxes(bboxesAfter, elements);

    // Transform each element from its new position to its old one
    elements.forEach(function(el) {
      var key = el.getAttribute("data-key");
      var bbox = {
        before: bboxesBefore[key],
        after: bboxesAfter[key]
      };

      var dx = bbox.before.left - bbox.after.left;
      var dy = bbox.before.top - bbox.after.top;

      el.style.transform = "translate3d(" + dx + "px," + dy + "px, 0)";
    });

    // Force repaint
    elements[0].parentElement.offsetHeight;

    // Turn on CSS animations
    document.body.classList.toggle("animated", true);

    // Remove translation to animate to natural position
    elements.forEach(function(el) {
      el.style.transform = "";
    });
  };
}());

// Event handler & sorting/moving logic
document.querySelector("div").addEventListener("click", function(e) {
  var currentList = e.target.getAttribute("data-list");
  if (currentList) {
    var targetIndex = e.target.getAttribute("data-index");
    var nextIndex = 0;

    // Get the next list from the lists array
    var newListIndex = (+currentList + 1) % lists.length;
    var newList = lists[newListIndex];

    for (nextIndex; nextIndex < newList.children.length; nextIndex++) {
      if (newList.children[nextIndex].getAttribute("data-index") > targetIndex) {
        break;
      }
    }

    // Call the transition
    transition(function() {
      newList.insertBefore(e.target, newList.children[nextIndex]);
      e.target.setAttribute("data-list", newListIndex);
    }, items);
  }
});

div { display: flex; justify-content: space-between; }

.animated li {
  transition: transform .5s ease-in-out;
}

<h2>Example</h2>
<div>
  <ul class="js-list0">
    <li data-index="0" data-list="0">Item 1</li>
    <li data-index="3" data-list="0">Item 2</li>
    <li data-index="5" data-list="0">Item 4</li>
    <li data-index="7" data-list="0">Item 6</li>
  </ul>

  <ul class="js-list1">
    <li data-index="4" data-list="1">Item 3</li>
    <li data-index="6" data-list="1">Item 5</li>
  </ul>
</div>

Edit :

Pour ajouter la prise en charge d'autres propriétés que vous souhaitez animer, suivez cette approche en 4 étapes :

  1. Ajoutez la règle css à l'élément .animated transition propriété :

    transition: transform .5s ease-in-out,
                background-color .5s ease-in-out;
  2. Stockez le style calculé des propriétés avant de modifier le DOM :

    obj[key].bgColor = window
      .getComputedStyle(element, null)
      .getPropertyValue("background-color");
  3. Après l'avoir modifiée, définissez rapidement une surcharge temporaire pour la propriété, comme nous l'avons déjà fait pour la propriété transform prop.

    el.style.backgroundColor = bbox.before.bgColor;
  4. Après avoir activé les animations css, supprimez la dérogation temporaire permettant de déclencher la transition css :

    el.style.backgroundColor = "";

En action : http://codepen.io/anon/pen/pELzdr

Veuillez noter que les transitions css fonctionnent très bien sur certaines propriétés, telles que transform et opacity alors qu'ils peuvent être moins performants pour d'autres (comme le height qui déclenche généralement des repeints). Veillez à surveiller vos fréquences d'images pour éviter les problèmes de performances !

4voto

Gust van de Wal Points 1213

Comme vous utilisez déjà jQuery, ma réponse a été assez facile à faire

$(function(){
  var move = function(){
    var data = [0,0]
    $('.items > li').each(function(){
      var $this = $(this)
      var height = $this.outerHeight(true)
      var side = ($this.hasClass('left') ? 0 : 1)
      $this.css('top', data[side])
      data[side]+=height
    })
  }
  $(window).on('resize', function(){
    move()
  })
  $(document).on('click', '.items > li', function(){
    $(this).toggleClass('left').toggleClass('right')
    move()
  })
  move()
  $('.items').removeClass('wait')
})

.items{
  margin: 0;
  padding: 0;
  list-style: none;
}

.items > li{
  display: table;
  position: absolute;
  padding: 10px;
  color: #fff;
  cursor: pointer;
  -webkit-user-select: none;
          user-select: none;
  transition: .3s ease;
}

.items.wait > li{
  visibility: hidden;
}

.items .left{
  left: 0;
  background-color: #1ABC9C;
}

.items .right{
  left: 100%;
  transform: translateX(-100%);
  background-color: #E74C3C;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<ul class="items wait">
  <li class="left">Item 1<br />With some additional text</li>
  <li class="left">Item 2</li>
  <li class="left">Item 3</li>
  <li class="left">Item 4</li>
  <li class="right">Item 5</li>
  <li class="right">Item 6</li>
  <li class="right">Item 7</li>
  <li class="right">Item 8</li>
</ul>

Le CSS s'assure que les éléments avec la classe left sont à gauche, et ceux qui ont la classe right sont à droite, mais à cause des deux lignes suivantes

left: 100%;
transform: translateX(-100%);

la valeur de gauche et la valeur de transformation seront transitionnées, mais il semblera que right est fixé à 0.

Le script recalcule tout à 3 occasions

  • document prêt
  • redimensionnement de la fenêtre
  • lorsque l'un des éléments est cliqué

Lorsque vous cliquez sur l'un des éléments, sa classe basculera simplement de l'une à l'autre. left à right . Après cela, le recalcul est effectué. Il conserve une variable data qui garde la trace de la hauteur de chaque colonne avec chaque élément qui s'y trouve, et déplace chaque colonne suivante d'autant vers le haut.

Ce script peut tenir compte des éléments avec marge, padding, lignes multiples et images, si vous le souhaitez.

De plus, la liste a une classe wait qui masque tous les éléments jusqu'à ce qu'ils soient définis pour la première fois. Cela empêche l'utilisateur de voir les éléments lorsqu'ils ne sont pas encore placés.

J'espère que cela vous aidera

1voto

Jishnu V S Points 6151

Il suffit de copier-coller ceci dans votre HTML page, c'est exactement ce dont vous avez besoin

/* CSS */

#sortable {
    list-style-type: none;
    margin: 0;
    padding: 0;
    width: 500px;
}
#sortable li {
    margin: 0 125px 0 3px;
    padding: 0.4em;
    font-size: 1.4em;
    height: 18px;
    float: left;
    color: #fff;
    cursor: pointer;
}
#sortable li:nth-child(odd) {
    background: #01BC9C;
}
#sortable li:nth-child(even) {
    background: #E54A2D;
}
#sortable li span {
    position: absolute;
    margin-left: -1.3em;
}

<!-- HTML -->

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Try with this</title>
<link rel="stylesheet" href="http://stackoverflow.com//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="http://stackoverflow.com/resources/demos/style.css">

<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
  $( function() {
    $( "#sortable" ).sortable();
    $( "#sortable" ).disableSelection();
  } );
  </script>
</head>
<body>
<ul id="sortable">
  <li class="ui-state-default">Item 1</li>
  <li class="ui-state-default">Item 2</li>
  <li class="ui-state-default">Item 3</li>
  <li class="ui-state-default">Item 4</li>
  <li class="ui-state-default">Item 5</li>
  <li class="ui-state-default">Item 6</li>
  <li class="ui-state-default">Item 7</li>
</ul>
</body>
</html>

1voto

Claudio Baumgartner Points 134

Je me suis inspiré de tous les messages précédents et j'en ai fait une bibliothèque qui permet d'utiliser ng-animate sans angular.

La bibliothèque s'appelle Animorph

J'ai résolu l'exemple décrit avec presque aucun code javascript personnalisé (car les parties lourdes sont toutes dans la bibliothèque).

Veuillez noter que pour l'instant, il ne trie pas les listes mais se concentre uniquement sur la partie animation.

Codepen : http://codepen.io/claudiobmgrtnr/pen/NRrYaQ

Javascript :

  $(".left").on("click", "li.element", function() {
    $(this).amAppendTo('.right', {
      addClasses: ['element--green'],
      removeClasses: ['element--golden']
    });
  });
  $(".right").on("click", "li.element", function() {
    $(this).amPrependTo('.left', {
      addClasses: ['element--golden'],
      removeClasses: ['element--green']
    });
  });

SCSS :

body {
  margin: 0;
  width: 100%;
  &:after {
    content: '';
    display: table;
    width: 100%;
    clear: both;
  }
}

ul {
  list-style-type: none;
  padding: 0;
}

.element {
  width: 100px;
  height: 30px;
  line-height: 30px;
  padding: 8px;
  list-style: none;
  &--golden {
    background: goldenrod;
  }
  &--green {
    background: #bada55;
  }
  &.am-leave {
    visibility: hidden;
  }
  &.am-leave-prepare {
    visibility: hidden;
  }
  &.am-leave-active {
    height: 0;
    padding-top: 0;
    padding-bottom: 0;
  }
  &.am-enter {
    visibility: hidden;
  }
  &.am-enter-prepare {
    height: 0;
    padding-top: 0;
    padding-bottom: 0;
  }
  &.am-enter-active {
    height: 30px;
    padding-top: 8px;
    padding-bottom: 8px;
  }
  &.am-enter,
  &.am-move,
  &.am-leave {
    transition: all 300ms;
  }
}

.left {
  float: left;
}

.right {
  float: right;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X