Подтвердить что ты не робот

Javascript/css анимация из dom node в dom node

ng-animate-ref позволяет создать переход от одного dom node к другому.

ng-animate использует все стили css, такие как position, font-size, font-color и многое другое из первого элемента dom и второго элемента dom и создает анимацию css 3 для перемещения элемента из состояния a в состояние b.

Это именно то, что мне нужно, но, к сожалению, я не могу использовать Angular 1 в текущем проекте.

Есть ли способ для повторного использования одной и той же анимации css3, не перемещая все стили из моих файлов css в javascript?

Чтобы проиллюстрировать проблему, см. следующий пример. Как вы можете видеть, у примера есть не пользовательский код анимации javascript, а только код javascript, который обрабатывает элементы переключения логики состояния из списка a в b.

Определение анимации записывается в чистом css.

Демо:
https://codepen.io/jonespen/pen/avBZpO/

Предварительный просмотр:
Предварительный просмотр демо

4b9b3361

Ответ 1

Я был вдохновлен всеми замечательными предыдущими сообщениями и превратил его в библиотеку, которая позволяет использовать ng-animate без angular.

Библиотека называется Animorph

Я решил описанный пример практически без пользовательского кода javascript (поскольку все тяжелые части находятся в библиотеке).

Обратите внимание, что сейчас он не сортирует списки, а фокусируется только на части анимации.

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;
}

Ответ 2

Конечно, jQuery animate может достичь этого без каких-либо плагинов.

Может быть, не так много строк кода, но они имеют некоторую сложность.

Здесь вы хотите (ps: jquery-ui использовать только для изменения цвета).

$(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="//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>
  

Ответ 3

Простое решение для JavaScript, которое использует:

  • HTMLElement.getBoundingClientRect, чтобы найти различия между старыми и новыми позициями элемента
  • css transition для анимации
  • css transform для перевода

Объяснение подхода:

Основная идея состоит в том, чтобы браузер только вычислил/оплатил DOM один раз. Мы позаботимся о переходе между исходным состоянием и этим новым.

Только путем перехода (a) ускоренного использования графического объекта transform, на (b) небольшой выбор элементов (все элементы <li>), мы постараемся обеспечить высокую частоту кадров.

// 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>

Ответ 4

Поскольку вы уже используете jQuery, мой ответ был довольно легко сделать

$(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>

Ответ 5

Просто скопируйте это на свою страницу HTML, это именно то, что вам нужно

/* 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="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="/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>