【问题标题】:javascript/css animation from dom node to dom node从 dom 节点到 dom 节点的 javascript/css 动画
【发布时间】:2017-01-19 18:35:57
【问题描述】:

ng-animate-ref 允许创建从一个 dom 节点到另一个的过渡

ng-animate 使用第一个 dom 元素和第二个 dom 元素的所有 css 样式,如 positionfont-sizefont-color 等,并创建一个 css 3 动画以将元素从状态 a 移动声明b

这正是我需要的,但不幸的是我不能在当前项目中使用 Angular 1。

是否有任何可重用的方法来实现相同的 css3 动画,而无需将所有样式从我的 css 文件移动到 javascript?

为了说明问题,请参阅以下示例。 如您所见,该示例完全没有自定义 javascript 动画代码,只有处理从列表 ab 的状态逻辑切换元素的 javascript 代码。

动画定​​义是用纯CSS编写的

演示:
https://codepen.io/jonespen/pen/avBZpO/

预览:

【问题讨论】:

  • 我使用 vanilla jQuery 做了一些类似于你想要的 here 的东西。我用this post 来制作它。它没有您想要的一切(例如褪色和中心项目移开),但它不是一个糟糕的 80/20 解决方案。明天我会花更多的时间。
  • @Danman 也许stackoverflow.com/questions/4493449/… 可能会有所帮助,但我更喜欢带有单元测试的库而不是快速的 jquery sn-p..
  • 要求我们推荐或查找书籍、工具、软件库、教程或其他非现场资源的问题对于 Stack Overflow 来说是题外话,因为它们往往会吸引固执己见答案和垃圾邮件。相反,请描述问题以及迄今为止为解决该问题所做的工作。
  • @jantimon 您能否在问题中包含您尝试解决问题的内容? “但我更喜欢带有单元测试的库而不是快速 jquery sn-p” 为什么需要一个库来实现需求?虽然如果使用库是必需的,为什么使用 jQuery 返回预期结果不适用?
  • @jantimon 同时,问题中没有出现“纯css”方法来展示您在哪里尝试解决自己的问题?

标签: javascript html css animation


【解决方案1】:

当然jQuery animate不用任何插件也可以实现。

也许代码行数不多,但确实有些复杂。

Here是你想要的(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>
  

【讨论】:

  • 我想将样式保留在 css 中,而不是将它们移动到 javascript 中。这也可能吗?是否可以不添加整个 jquery 和 jquery ui 库?我也更喜欢css3动画而不是js动画..就像在codepen示例中一样
  • @jantimon 原来的问题没有提到要求是只使用css?事实上,你确定javascript没有用在显示预期效果的链接代码笔上吗?
  • 它仅将javascript用于状态逻辑而不用于动画逻辑。这也是我想要实现的目标,但没有 Angular 1
  • @jantimon “它仅将javascript用于状态逻辑而不用于动画逻辑。”“状态逻辑”是什么意思? .push().splice().indexOf() 如何影响“状态逻辑”? “也更喜欢 css3 动画而不是 js 动画.. 就像在 codepen 示例中一样” 参见 codepen 的右侧面板; Question 本身的示例使用javascript。您能否将仅使用css 解决问题的尝试包括在内?
  • @guest271314 在我看来,你不是想帮忙,只是想告诉我我错了。 TTCC 因其出色的回答而获得了 120 的声誉,但这不是我想要的。如果您真的在寻找建设性的讨论,请联系我chat.stackoverflow.com/rooms/17/javascript
【解决方案2】:

一个普通的 javascript 解决方案,它使用:

  • HTMLElement.getBoundingClientRect查找元素新旧位置的差异
  • css transition 动画
  • css transform 翻译

方法说明:

核心思想是让浏览器只计算/重排DOM一次。我们将自己处理初始状态和新状态之间的转换。

通过仅转换 (a) GPU 加速的transform 属性,(b) 一小部分元素(所有 &lt;li&gt; 元素),我们将尝试确保高帧速率。

// 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 步方法:

  1. 将css规则添加到.animatedtransition属性:

    transition: transform .5s ease-in-out,
                background-color .5s ease-in-out;
    
  2. 在修改 DOM 之前存储属性计算样式:

    obj[key].bgColor = window
      .getComputedStyle(element, null)
      .getPropertyValue("background-color");
    
  3. 修改后,快速为属性设置一个临时覆盖,就像我们已经为 transform 属性所做的那样。

    el.style.backgroundColor = bbox.before.bgColor;
    
  4. 开启css动画后,去掉临时覆盖触发css过渡:

    el.style.backgroundColor = "";
    

实际操作中:http://codepen.io/anon/pen/pELzdr

请注意,css 过渡在某些属性上效果很好,例如 transformopacity,而在其他属性上效果可能会更差(例如 height,这通常会触发重绘)。确保监控帧速率以防止出现性能问题!

【讨论】:

  • 这看起来真的很令人印象深刻——它还包括颜色变化吗?你能提供一个codepen吗?
  • 目前不包括颜色变化。我已经对其进行了一些重构,使其更通用。在这个 codepen 中,我展示了如何包含颜色支持:codepen.io/anon/pen/pELzdr 基础知识:在 DOM 修改之前存储计算的背景颜色,为属性添加 css 过渡,在过渡元素上设置临时覆盖以动画到新元素状态。寻找说 Background color support 的 cmets 以在代码中看到它。请注意,背景颜色的变化会更多地影响您的帧速率,因为它们不是 GPU 加速的(如果我没记错的话)
  • 我还在回答中包含了有关如何在编辑中支持其他属性的说明。
  • 如果您在第一个动画完成之前开始第二个动画,似乎缓动被重置
  • @jantimon 是的。这是想要在 CSS 中管理转换的缺点之一。每次变更的过渡时间都是固定的;无论您将元素移动 1 像素还是 100 像素,它总是需要相同的时间。缓动也是如此:如果你在半过渡中改变方向,一个新的将开始。如果您想支持中间过渡更新,我相信您将不得不远离 CSS。你最终会得到一个你独立更新的转换列表。我想这是一个 100% 完美的复杂解决方案或一个相当简单的 80% 实现之间的权衡......
【解决方案3】:

因为你已经用过 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>

CSS 确保 left 类的元素在左侧,right 类的元素在右侧,但由于以下两行

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

left 和 transform 值将被转换,但看起来好像 right 设置为 0。

脚本在 3 次重新计算所有内容

  • 文档准备就绪
  • 窗口大小调整
  • 当其中一项被点击时

当您单击其中一项时,它会简单地将其类别从left 切换到right。之后,将进行重新计算。它保留了一个变量data,该变量跟踪每列与其中的每个项目的高度,并在此之后从顶部移动每一个。

如果需要,此脚本可以考虑具有边距、内边距、多行和图像的元素。

此外,列表有一个类wait,它隐藏所有元素,直到它们第一次被设置。这可以防止用户在项目尚未放置时看到它们。

希望对你有帮助

【讨论】:

  • 我不想更改标记 - 标记只是一种更复杂标记的示例,我在不同包装器之间移动元素并需要对其进行动画处理。
  • 所以你真的希望元素交换父元素,是这样吗?
【解决方案4】:

我受到之前所有精彩帖子的启发,并将其变成了一个库,允许在没有角度的情况下使用 ng-animate。

这个库叫做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;
}

【讨论】:

  • 哇,这和我要找的很接近 - 我必须使用 am 前缀吗?
  • 很高兴你喜欢它。不,名称空间是完全可配置的。 – 我会尽快更新文档并实施更多示例
  • 干得好,克劳迪奥!您能否解释一下您的方法以及它与其他答案的不同之处?如果我理解正确,您将复制节点以具有 (1) 动画到隐藏显示模式的节点,(2) 动画到新位置的节点,以及 (3) 清除空间的节点新职位?这是否需要通过 css 提升固定高度?
  • 谢谢,很高兴听到你喜欢它。我的方法深受来自 angular.js 的 ng-animate 原理的启发。没错,它创建了两个克隆,并使用一个作为占位符,另一个作为动画元素。如果要为元素的高度设置动画,则必须使用固定高度(目前)。至少在动画运行时。
【解决方案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>

【讨论】:

  • 不幸的是,这并不是他想要的。有关更多详细信息,请参阅他包含的演示 (codepen.io/jonespen/pen/avBZpO?editors=0100)
  • 我不是在寻找拖放。我正在寻找动画的抽象 - 只需设置两个状态的样式并在两者之间设置动画。
猜你喜欢
  • 2021-01-22
  • 2010-12-02
  • 2014-07-27
  • 2013-12-19
  • 2015-08-17
  • 1970-01-01
  • 2015-03-26
  • 2015-05-20
  • 1970-01-01
相关资源
最近更新 更多