在弹性列中要滑动以产生滑动效果的属性

时间:2019-07-02 16:01:31

标签: html css flexbox css-animations

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

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

我不知道要为列表元素transition使用哪个属性。当我将transition: all ...;应用于.toast时,它会在附加一个孩子时正确设置向上滑动的动画效果,但不适用于移除孩子时的动画效果。

很明显,因为transition: all可以工作,该属性可以设置动画,但是我不知道它是哪个属性。我尝试过transition: bottom,{{1 }},height和其他几个人,但他们并没有达到我想要的目标。我希望使用margin-bottom 避免,并且只想将transition: all应用于必要的属性。

编辑:要明确的是,我不是在问如何为单个吐司进/出动画(即正在添加/删除的动画)设置动画,而是要如何控制已经存在的其他烤面包,并进行回流,以便为添加/填充操作腾出空间。

这是代码的粗略草图。

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

3 个答案:

答案 0 :(得分:1)

没有简单的方法可以单独使用CSS来完成此操作,因为auto-dimensions cannot be animated。这意味着您需要先了解自己的尺寸,也可以像libraries such as materialize do这样快速计算尺寸。

也许是这样的:

https://codepen.io/anon/pen/ydjPMj

<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>
#toaster {
    position: fixed;
    left: 0;
    bottom: 0;
   height: 100px;
    max-width: 100px;

   transition: height 2s;
}
.toast {
    background-color: tan;
    color: black;
    padding: 10px;
    margin: 10px;
    width: 100%;
   height: 1em;
    text-align: center;
}
let counter = 3;
let toasterEl = document.getElementById("toaster");
toasterEl.style.height = "9em";

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

const removeToast = () => {
  toasterEl.removeChild(toasterEl.childNodes[toasterEl.childNodes.length - 1]);
   toasterEl.style.height = parseInt(toasterEl.style.height) - 3 + "em";
}

[编辑] ,您可能还想look into flex-grow,但这并不是您想要的,但是如果您对解决方案的几何知识有所了解,则可以帮助您破解解决方案每个吐司内部的DOM。

答案 1 :(得分:1)

我想说,达到预期效果的最佳方法是在过渡完成后监听这些元素上的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. 您需要在其他两个transitionpadding属性以及margin本身上使用height
  2. 为简单起见,我将box-sizing更改为border-box
  3. 如果您决定使用这种方法,最好也使用transitionend的其他带前缀事件的名称。
  4. 如果要选择何时删除元素,则事件函数中有一个e.propertyName属性。

Further information about transitionend.propertyName

答案 2 :(得分:0)

您正在立即从DOM中删除该元素,因此将不应用任何过渡或动画。您可以在要删除元素时添加一个类(带有动画),并在完成过渡一段时间后,删除该元素。

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

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

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

.remove{
   transform:scaleY(0);
   opacity: 0;
 }


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

希望这会有所帮助!