На какое свойство переходить для скользящего эффекта в flex-столбце - PullRequest
1 голос
/ 02 июля 2019

У меня есть список, который находится в контейнере, в котором периодически будут удаляться / добавляться элементы из нижней части контейнера. Сам контейнер прикреплен к левому нижнему углу области просмотра.

Когда добавляется новый ребенок, я бы хотел, чтобы все существующие дети скользили вверх, а когда ребенок удалялся, все братья и сестры над ним скользили вниз.

Я не могу понять, какое свойство transition включено для элементов списка. Когда я применяю transition: all ...; к .toast, он правильно оживляет скольжение при добавлении ребенка, но не при удалении ребенка.

Очевидно, что transition: all работает, свойство можно анимировать, но я не могу понять, какое это свойство. Я пробовал transition: bottom, 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;
    }
}
<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>

Ответы [ 3 ]

1 голос
/ 02 июля 2019

Нет простого способа сделать это, используя только CSS, потому что auto -размеры не могут быть анимированы . Это означает, что вам нужно знать свои размеры априори, или вы можете вычислить их на лету, как библиотеки, такие как 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";
}

[РЕДАКТИРОВАТЬ] Вы также можете изучить flex-grow, что не совсем то, что вы хотите, но может помочь вам взломать ваш путь к решению, если вы знаете кое-что о геометрии DOM внутри каждого тоста.

1 голос
/ 02 июля 2019

Я бы сказал, что лучший способ достичь желаемого эффекта - прослушивать событие 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 на двух других padding и marginсвойства, а также height.
  2. Ради простоты я изменил box-sizing на border-box.
  3. Если вы решили использовать этот подход, лучшеиспользуйте также другие имена префиксных событий transitionend.
  4. Внутри вашей функции события есть свойство e.propertyName, которое вы можете использовать, если хотите выбрать, когда удалять свой элемент.

Дополнительная информация о transitionend и .propertyName

0 голосов
/ 02 июля 2019

Вы удаляете элемент из 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>

Надеюсь, это поможет!

...