当我从弹性框中删除项目时,其余项目会立即"捕捉"到新位置,而不是动画。
从概念上讲,由于项目正在改变其位置,我希望过渡适用。
我已经在所有涉及的元素(弹性框和子元素)上设置了过渡属性
有没有办法对弹性框进行动画编辑(添加和删除)?这对我来说实际上是一个阻碍,也是弹性框缺失的部分。
我已经根据 Treehouse 的这个例子修复了@skyline3000的演示。不确定如果浏览器发生变化,这是否会再次中断,但这似乎是对 flex 大小更改进行动画处理的预期方法:
http://jsfiddle.net/2gbPg/2/
我也使用了jQuery,但从技术上讲,它不是必需的。
.flexed {
background: grey;
/* The border seems to cause drawing artifacts on transition. Probably a browser bug. */
/* border: 1px solid black; */
margin: 5px;
height: 100px;
flex-grow: 1;
transition: flex-grow 1000ms linear;
}
.removed {
/* Setting this to zero breaks the transition */
flex-grow: 0.00001;
}
关于CSS需要注意的一件事是你不能过渡到零的flex-grow
,它不会过渡它会消失。您只需输入一个非常小的值。绘制边框时似乎也存在人为错误,因此在这种情况下我使用了背景。
请记住,灵活框模型和网格布局规范会不断变化,甚至属性和有效值也是如此。浏览器实现也远未完成。话虽如此,您可以在 flex
属性上进行转换,以便元素平滑过渡,然后只需侦听TransitionEnd
即可最终从 DOM 树中删除节点。
下面是一个在Chrome 21中运行的JSFiddle示例:http://jsfiddle.net/5kJjM/(单击中间的div)
var node = document.querySelector('#remove-me');
node.addEventListener('click', function(evt) {
this.classList.add('clicked');
}, false);
node.addEventListener('webkitTransitionEnd', function(evt) {
document.querySelector('#flexbox').removeChild(this);
}, false);
#flexbox {
display: -webkit-flex;
-webkit-flex-flow: row;
}
.flexed {
border: 1px solid black;
height: 100px;
-webkit-flex: 1 0 auto;
-webkit-transition: -webkit-flex 250ms linear;
}
.clicked {
-webkit-flex: 0 0 auto;
}
<div id="flexbox">
<div class="flexed"></div>
<div class="flexed" id="remove-me"></div>
<div class="flexed"></div>
</div>
编辑:为了进一步澄清,当您删除节点时,应将其 flex 设置为 0,然后将其从 DOM 中删除。添加节点时,使用 flex: 0 添加节点,然后将其转换为 flex:1
我已经做了一个代码笔,当你删除一个元素时,它会对元素进行动画处理,看看:https://codepen.io/MauriciAbad/pen/yLbrpey
.HTML
<div class="container">
<div></div>
<div></div>
... more elements ...
</div>
.CSS
.container{
display: flex;
flex-wrap: wrap;
}
.container > * {
transform-origin: left top;
}
打字稿
如果你想要JavaScript,只需从函数的签名和顶部的界面中删除: Anything
。
interface FlexItemInfo {
element: Element
x: number
y: number
width: number
height: number
}
const container = document.querySelector('.container')
for (const item of container.children) {
item.addEventListener('click', () => {
removeFlexItem(container, item)
})
}
function removeFlexItem(container: Element, item: Element): void {
const oldFlexItemsInfo = getFlexItemsInfo(container)
container.removeChild(item)
const newFlexItemsInfo = getFlexItemsInfo(container)
aminateFlexItems(oldFlexItemsInfo, newFlexItemsInfo)
}
function getFlexItemsInfo(container: Element): FlexItemInfo[] {
return Array.from(container.children).map((item) => {
const rect = item.getBoundingClientRect()
return {
element: item,
x: rect.left,
y: rect.top,
width: rect.right - rect.left,
height: rect.bottom - rect.top,
}
})
}
function aminateFlexItems(
oldFlexItemsInfo: FlexItemInfo[],
newFlexItemsInfo: FlexItemInfo[]
): void {
for (const newFlexItemInfo of newFlexItemsInfo) {
const oldFlexItemInfo = oldFlexItemsInfo.find(
(itemInfo) => itemInfo.element === newFlexItemInfo.element
)
const translateX = oldFlexItemInfo.x - newFlexItemInfo.x
const translateY = oldFlexItemInfo.y - newFlexItemInfo.y
const scaleX = oldFlexItemInfo.width / newFlexItemInfo.width
const scaleY = oldFlexItemInfo.height / newFlexItemInfo.height
newFlexItemInfo.element.animate(
[
{
transform: `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`,
},
{ transform: 'none' },
],
{
duration: 250,
easing: 'ease-out',
}
)
}
}
我不小心让它以一种简单的方式工作。基本上,您设置width:0;flex-grow:1
,当然添加transition:all 2s;
,仅此而已。这是一个奇怪的黑客。
看到它工作
@skyline3000和@chris-nicola的解决方案的另一种改编:http://jsfiddle.net/2gbPg/2/
您可以简单地对max-width
(或根据需要max-height
)进行动画处理,在删除时以动画形式设置为0
,在插入时100%
。
添加或删除子项时,可以使用 MutationObserver 启动动画。
优点是,您不必修改任何现有代码,包括 Angular、React 或 Blazor 等 SPA 框架(甚至服务器端)。您不必添加人工动画类。
它应该适用于任何布局,而不仅仅是弹性框。
以下是基于Maurici Abad使用MutationObserver的回答:
//add this to your project
function animateChildren(container) {
function getFlexItemsInfo(container) {
return Array.from(container.children).map((item) => {
const rect = item.getBoundingClientRect()
return {
element: item,
x: rect.left,
y: rect.top,
width: rect.right - rect.left,
height: rect.bottom - rect.top,
}
})
}
function animateFlexItems(oldFlexItemsInfo, newFlexItemsInfo) {
for (const newFlexItemInfo of newFlexItemsInfo) {
const oldFlexItemInfo = oldFlexItemsInfo.find(e => e.element == newFlexItemInfo.element);
if (!oldFlexItemInfo) {
continue;
}
const translateX = oldFlexItemInfo.x - newFlexItemInfo.x
const translateY = oldFlexItemInfo.y - newFlexItemInfo.y
const scaleX = oldFlexItemInfo.width / newFlexItemInfo.width
const scaleY = oldFlexItemInfo.height / newFlexItemInfo.height
newFlexItemInfo.element.animate(
[
{
transform: `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`,
},
{ transform: 'none' },
],
{
duration: 250,
easing: 'ease-out',
}
)
}
}
let oldFlexItemsInfo = getFlexItemsInfo(container);
// Callback function to execute when mutations are observed
const childListMutationCallback = function(mutationsList, observer) {
const newFlexItemsInfo = getFlexItemsInfo(container);
if (oldFlexItemsInfo) {
animateFlexItems(oldFlexItemsInfo, newFlexItemsInfo);
}
oldFlexItemsInfo = newFlexItemsInfo;
};
new MutationObserver(childListMutationCallback).observe(container, { childList: true });
}
const container = document.querySelector('.container');
animateChildren(container);
//emulate existing adding/removing items
document.addEventListener('click', e => {
const item = e.target.closest('.item');
if (item) {
e.target.matches('.btn-close')
? container.removeChild(item)
: container.prepend(item.cloneNode(true));
}
});
.container {
display: flex;
flex-wrap: wrap;
gap: 2em;
width: 100%;
overflow: none;
}
.container>* {
transform-origin: left top;
}
.container>* {
flex-grow: 1;
max-width: 50%;
min-width: 20%;
border-radius: 5px;
height: 5em;
margin: 0.5rem;
box-shadow: 0 1px 8px rgba(0,0,0,0.3);
padding: 1em;
background: white;
}
.btn-close:after {
padding: .25em .25em;
content: 'X';
border: solid 1px
}
<div class="container">
<div class="item">
Item 1 <span class="btn-close"></span>
</div>
<div class="item" style="background: #f64f59">
Item 2 <span class="btn-close"></span>
</div>
<div class="item" style="background: #c471ed ">
Click to add <span class="btn-close"></span>
</div>
</div>
我一直在尝试为弹性框中的行设置动画。这仅通过 css 是不可能的。所以我用了一些javascript和一个额外的父级来做我的flexbox行。
.HTML:
<div class="outer-container">
<div class="inner-container">
<div class="row">Row number 1</div>
</div>
</div>
<button id="add">Add one more item</button>
.CSS:
.row{
height : 40px;
width : 200px;
border: 1px solid #cecece;
text-align : center;
padding-top : 20px;
}
.outer-container {
height : 500px;
border : 1px solid blue;
width : 250px;
display: flex;
justify-content : center;
align-items: center;
}
.inner-container {
height : 42px;
transition : height 0.3s;
}
button {
width : 200px;
height: 30px;
margin-left : 80px;
margin-top : 10px;
}
Javascript :
(() => {
let count = 1;
document.querySelector("#add").addEventListener('click', () => {
const template = document.createElement('div');
count += 1;
template.textContent = `Row number ${count}`;
template.className = 'row';
const innerContainer = document.querySelector('.inner-container');
innerContainer.appendChild(template);
innerContainer.style.height = `${innerContainer.clientHeight + 42}px`;
})
})();
工作演示 : https://jsfiddle.net/crapbox/dnx654eo/1/