ng-animate-ref
allows to create a transition from one dom node to another.
ng-animate uses all the css styles like position
, font-size
, font-color
and more from the first dom element and the second dom element and creates a css 3 animation to move the element from state a
to state b
.
This is exactly what I need but unfortunately I can't use Angular 1 in the current project.
Is there any reusable way to achieve the same css3 animation without moving all styles from my css files to javascript?
To illustrate the problem please see the following example. As you can see the example has no custom javascript animation code at all but only javascript code which handles the state logic switching elements from list a
to b
.
The animation definition is written in pure css.
Demo:
https://codepen.io/jonespen/pen/avBZpO/
Preview:
TL;DR # Use CSS animations for simpler "one-shot" transitions, like toggling UI element states. Use JavaScript animations when you want to have advanced effects like bouncing, stop, pause, rewind, or slow down.
CSS allows animation of HTML elements without using JavaScript or Flash! In this chapter you will learn about the following properties: @keyframes.
Inserting Nodes into the DOM The methods appendChild() and insertBefore() are used to add items to the beginning, middle, or end of a parent element, and replaceChild() is used to replace an old node with a new node.
Animation CodeJavaScript animations are done by programming gradual changes in an element's style. The changes are called by a timer. When the timer interval is small, the animation looks continuous.
Of course, jQuery animate
can achieve it without any plugins.
Maybe there are not many lines of code, but they do have some complexity.
Here is what you want ( ps: jquery-ui
only use to change color ).
$(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>
A plain javascript solution that uses:
HTMLElement.getBoundingClientRect
to find differences between the old and new positions of elementtransition
to animatetransform
to translateThe core idea is to have the browser only calculate/reflow the DOM once. We'll take care of the transition between the initial state and this new one ourselves.
By only transitioning (a) the GPU accelerated transform
property, on (b) a small selection of elements (all <li>
elements), we'll try to ensure a high frame rate.
// 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:
To add support for other properties you'd like to animate, follow this 4 step approach:
Add the css rule to the .animated
transition
property:
transition: transform .5s ease-in-out, background-color .5s ease-in-out;
Store the properties computed style before you modify the DOM:
obj[key].bgColor = window .getComputedStyle(element, null) .getPropertyValue("background-color");
After modifying, quickly set a temporary override for the property, like we already did for the transform
prop.
el.style.backgroundColor = bbox.before.bgColor;
After turning on the css animations, remove the temporary override to trigger the css transition:
el.style.backgroundColor = "";
In action: http://codepen.io/anon/pen/pELzdr
Please note that css transitions work very well on some properties, like transform
and opacity
, while they might perform worse on others (like height
, which usually triggers repaints). Make sure you monitor your frame rates to prevent performance issues!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With