首页 > 解决方案 > 为弹性列中的滑动效果转换哪个属性

问题描述

我有一个驻留在容器中的列表,该列表将定期从容器底部删除/添加项目。容器本身固定在视口的左下方。

当添加一个新孩子时,我希望所有现有的孩子向上滑动,当一个孩子被移除时,它上面的所有兄弟姐妹都向下滑动。

我不知道要transition为列表元素打开哪个属性。当我正确应用它时,当一个孩子被附加时transition: all ...;.toast它会向上滑动 - 但不是当一个孩子被移除时。

显然,因为transition: all有效,该属性可以动画,但我无法弄清楚它是哪个属性。我已经尝试过transition: bottom, height,margin-bottom和其他几个 - 但他们没有达到我想要的。我希望避免使用transition: all并希望transition仅适用于必要的属性。

编辑要明确:我不是在询问动画单个 toast 进入/退出动画(即正在添加/删除的动画),而是如何控制已经存在的其他toast 的移动并重新流动以便制作添加/填写删除的空间。

这是代码的粗略草图。

let counter = 3;
let toasterEl = document.getElementById("toaster");

const newToast = () => toasterEl.insertAdjacentHTML('beforeend',`<div class="toast">${++counter}</div>`);

const removeToast = () => toasterEl.removeChild(toasterEl.childNodes[toasterEl.childNodes.length - 1])
#toaster {
    position: fixed;
    left: 0;
    bottom: 0;
    flex-direction: column;
    max-width: 100px;
    min-height: 100px;
}
.toast {
    transform-origin: center bottom;
    transition: ??? .5s linear;
    animation: toast-entry .5s ease-in;
  
    background-color: tan;
    color: black;
    padding: 10px;
    margin: 10px;
    width: 100%;
    text-align: center;
}

@keyframes toast-entry {
    0% {
        transform: rotateX(90deg);
        opacity: 0.5;
    }
    50% {
        transform: rotateX(0deg);
        opacity: 1;
    }
}
<button onclick="newToast()">Make toast</button>
<button onclick="removeToast()">Burn toast</button>

<div id="toaster">
    <div class="toast">1</div>
    <div class="toast">2</div>
    <div class="toast">3</div>
</div>

标签: htmlcssflexboxcss-animations

解决方案


我宁愿说达到预期效果的最佳方法是在过渡完成后侦听transitionend这些元素和该元素上的事件。.remove()

这是一个工作示例:

let counter = 3;
let toasterEl = document.getElementById("toaster");

const newToast = () => toasterEl.insertAdjacentHTML('beforeend',`<div class="toast">${++counter}</div>`);

const removeToast = () => { 

  var child = toasterEl.childNodes[ toasterEl.childNodes.length - 1 ];
  
  child.style.height = child.offsetHeight + 'px';
  
  child.addEventListener( 'transitionend', function( e ) {
  
    this.remove();
  
  });
  
  // this is a trick to let browser paint set `offsetHeight` first then start transition from that value to zero
  setTimeout( function() { 
  
    child.style.height = child.style.paddingTop = child.style.paddingBottom = child.style.marginTop = child.style.marginBottom = 0;
    
   }, 10 );

}
#toaster {
    position: fixed;
    left: 0;
    bottom: 0;
    flex-direction: column;
    max-width: 200px;
    min-height: 200px;
}
.toast {
    transform-origin: center top;
    overflow: hidden;
    transition: height .3s ease-out, margin .3s ease-out, padding .3s ease-out;
    animation: toast-entry .5s ease-in;
    background-color: tan;
    color: black;
    padding: 20px;
    margin: 10px;
    width: 100%;
    box-sizing: border-box;
    text-align: center;
}

@keyframes toast-entry {
    0% {
        transform: rotateX(90deg);
        opacity: 0.5;
    }
    50% {
        transform: rotateX(0deg);
        opacity: 1;
    }
}
<button onclick="newToast()">Make toast</button>
<button onclick="removeToast()">Burn toast</button>

<div id="toaster">
    <div class="toast">1</div>
    <div class="toast">2</div>
    <div class="toast">3</div>
</div>

值得一提:

  1. 您需要transition在其他两个paddingmargin属性上使用以及它height本身。
  2. 为了简单起见,我已更改box-sizingborder-box.
  3. 如果您决定使用这种方法,最好也使用其他前缀事件名称transitionend
  4. e.propertyName如果您想选择何时删除元素,则事件函数中有一个属性可供使用。

transitionend关于和的更多信息.propertyName


推荐阅读