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/
Предварительный просмотр:
![Предварительный просмотр демо]()
Ответы
Ответ 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>