我正在尝试使用React和React.addons.CSSTransitionGroup创建一个带有动画的水平滚动页面导航。目前我可以进行水平滚动(使用flexbox),页面打开/关闭,动画进入和离开。但动画并不是我想要的。
查看this example(jsfiddle)。
当您单击上一页上的按钮时,它当前会将离开屏幕的页面向右推。虽然正确的结果是在同一个地方动画离开页面。我不确定如何使用CSSTransitionGroup实现这种效果,或者知道它是否可以使用它。
function generateId() {
var r = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < 10; i += 1) {
r += possible.charAt(Math.floor(Math.random() * possible.length));
}
return r;
}
var PageList = {
pages: [],
listener: function(newpages) {},
open: function(caption, index) {
if (index != null) {
this.pages = this.pages.slice(0, index + 1);
}
this.pages.push({
id: generateId(),
caption: caption,
width: (150 + Math.random() * 50) | 0
});
this.listener(this.pages);
}
};
PageList.open("Main");
var PageLink = React.createClass({
render: function() {
var self = this;
return React.DOM.button({
className: "pagelink",
onClick: function() {
PageList.open(self.props.caption, self.props.pageIndex);
}
}, self.props.caption);
}
});
var Page = React.createClass({
render: function() {
return React.DOM.article({
className: "page",
style: {
width: this.props.page.width + "px"
}
},
React.DOM.h1({}, this.props.page.caption),
React.createElement(PageLink, {
caption: "Alpha",
pageIndex: this.props.index
}),
React.createElement(PageLink, {
caption: "Beta",
pageIndex: this.props.index
}),
React.createElement(PageLink, {
caption: "Gamma",
pageIndex: this.props.index
})
);
}
});
var Pages = React.createClass({
componentDidMount: function() {
var self = this;
PageList.listener = function(pages) {
self.setState({
pages: pages
});
};
},
getInitialState: function() {
return {
pages: PageList.pages
};
},
render: function() {
var pages = this.state.pages.map(function(page, index) {
return React.createElement(Page, {
key: page.id,
index: index,
page: page
});
});
return React.createElement(
React.addons.CSSTransitionGroup, {
component: "section",
className: "pages",
transitionName: "fall"
}, pages);
}
});
React.initializeTouchEvents(true);
React.render(
React.createElement(Pages),
document.getElementById("main")
);
/* animation */
.fall-enter {
transform: translate(0, -100%);
transform: translate3d(0, -100%, 0);
}
.fall-enter.fall-enter-active {
transform: translate(0, 0);
transform: translate3d(0, 0, 0);
transition: transform 1s ease-out;
}
.fall-leave {
index: -1;
transform: translate(0, 0);
transform: translate3d(0, 0, 0);
transition: transform 1s ease-in;
}
.fall-leave.fall-leave-active {
transform: translate(0, 100%);
transform: translate3d(0, 100%, 0);
}
/* other */
* {
box-sizing: border-box;
}
.pagelink {
padding: 5px;
margin: 5px 0px;
width: 100%;
}
.pages {
display: flex;
flex-flow: row;
overflow-x: scroll;
overflow-y: hidden;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #ddd;
}
.pages:after {
flex: none;
-webkit-flex: none;
display: block;
content: " ";
width: 100px;
}
.page {
flex: none;
-webkit-flex: none;
margin: 8px;
padding: 10px 31px;
background: #fff;
overflow: auto;
box-shadow: 2px 1px 4px rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
<body id="main">
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.12.2/react-with-addons.js"></script>
PS:如果它只适用于最新的浏览器,那就没关系了。
答案 0 :(得分:9)
要获得您正在寻找的页面的垂直阵容,我们将不得不更改HTML以添加额外的图层。这个额外的图层将是article
向上和向下滑动的垂直通道。现在,新的图层被推到一边,因为没有父图层来包含它们。
第二个问题是所有HTML都是由React动态创建的。尝试进入并更改库/插件并不是一个好主意,所以我尝试用jQuery创建你想要的效果。
HTML结构
我做的第一件事就是草拟出所需的布局。我们在整体父母中需要三层:
Parent
Cell
Innercell (doubles in height to contain two articles)
Article
草图中的虚线是Innercell
,Page矩形是Articles
<强> CSS 强>
在大多数情况下,我使用您的原始样式。主要区别在于cell
和article
之间的额外层。 cell
将其高度固定为100%
,然后innercell
获得height:200%
。它也绝对定位于bottom:0
。这很重要,因为它意味着额外的间距高于父母而不是下面。我们还确保文章本身绝对定位,bottom:0
。这将它放在innercell
的下半部分。
<强> JS 强>
这里要做的第一件事就是弄清楚我们希望事情发生的顺序。单击按钮时,我们可以使用三种可能的路径。
如果点击最右边的按钮,我们想要添加一个新单元格,然后在文章中滑动。
如果点击第二个到最右边的按钮,我们只想在新文章中滑动。
如果单击任何其他按钮,我们希望在单元格中向右滑动新文章,并且所有其他右侧单元格应向下滑动而不进行替换。
在change article
圈内,我们需要:
1从模板中创建新的article
,插入所有可变数据,并将其放在innercell
顶部的空白处。
2我们需要向下滑动innercell
。
3动画完成后,我们需要删除之前的article
并重置innercell
var cellTemplate = '<cell data-col="DATACOL"><innercell><article></article></innercell></cell>';
var innerTemplate = '<article class="new"><innerarticle><h1>TITLE</h1><button>Alpha</button><button>Beta</button><button>Gamma</button></innerarticle></article>';
var numCells = 2;
var animating = 0;
$('body').on('click', 'article button', function(event){
if (!animating) {
var currentCell = parseInt($(this).parent().parent().parent().parent().attr('data-col'));
var title = $(this).text();
if (currentCell == numCells) {
addCell(title);
} else if (currentCell == numCells - 1) {
changeArticle(title,numCells);
} else {
removeExtraCells(title,currentCell);
}
}
});
function removeExtraCells(title,currentCell) {
var tempCurrentCell = currentCell;
changeArticle(title,tempCurrentCell+1);
while (tempCurrentCell < numCells) {
tempCurrentCell++;
deleteArticle(title,tempCurrentCell+1);
}
numCells = currentCell+1;
}
function addCell(title){
numCells++
var html = cellTemplate.replace('DATACOL',numCells);
$('section').append(html);
changeArticle(title,numCells);
}
function changeArticle(title,changingCol) {
var cell = $('cell[data-col="'+changingCol+'"] innercell');
var html = innerTemplate.replace('TITLE', title).replace('DATACOL', numCells - 1);
cell.prepend(html);
triggerAnimation(cell);
}
function deleteArticle(title,changingCol) {
var cell = $('cell[data-col="'+changingCol+'"] innercell');
var html = '<article></article>';
cell.prepend(html).addClass('deleting');
triggerAnimation(cell);
}
function triggerAnimation(cell) {
cell.addClass('animating');
window.setTimeout(function(event){
cell.css('bottom','-100%');
animating = 1;
},50);
}
$('body').on('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', 'innercell', function(event){
if ( $(this).hasClass('deleting') ) {
$(this).parent().remove();
} else {
$(this).children('article:last-child').remove();
$(this).children('article.new').removeClass('new');
$(this).removeClass('animating');
$(this).css('bottom','0');
}
animating = 0;
});
&#13;
/* other */
* {
box-sizing: border-box;
}
section {
display: flex;
flex-flow:row;
overflow-x: scroll;
overflow-y: hidden;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #ddd;
}
cell {
flex: none;
-webkit-flex: none;
display:block;
height:100%;
float:left;
position:relative;
width:166px;
}
innercell {
display:block;
height:200%;
width:100%;
position:absolute;
left:0;
bottom:0;
}
.animating {
transition: bottom 1s ease-out;
}
article {
display:block;
padding:8px;
height:50%;
position:absolute;
left:0;
bottom:0;
}
article.new {
bottom:auto;
top:0;
}
innerarticle {
display:block;
padding: 10px 31px;
background: #fff;
overflow: auto;
box-shadow: 2px 1px 4px rgba(0, 0, 0, 0.2);
border-radius: 3px;
height:100%;
}
innerarticle button {
padding: 5px;
margin: 5px 0px;
width: 100%;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section>
<cell data-col="1">
<innercell>
<article>
<innerarticle>
<h1>Main</h1>
<button>Alpha</button>
<button>Beta</button>
<button>Gamma</button>
</innerarticle>
</article>
</innercell>
</cell>
<cell data-col="2">
<innercell>
<article>
<innerarticle>
<h1>Alpha</h1>
<button>Alpha</button>
<button>Beta</button>
<button>Gamma</button>
</innerarticle>
</article>
</innercell>
</cell>
</section>
&#13;
备注强>
我最终添加了第四层,我称之为innerarticle
。这是因为您的8px
有article
个保证金。当两个article
在垂直方向上彼此叠加时,margin
会折叠并且不会堆叠。要添加innerarticle
,然后给article
padding:8px
,我可以确保它堆叠。
另外,您可能已经注意到我使用了很多自定义元素名称。我之所以这样做,是因为它有效,让您轻松了解自己在做什么。您可以随意将它们全部更改为<div class="INSERTCURRENTELEMENT">
答案 1 :(得分:2)
我深入研究了这一点,并通过将其添加到css中来提出一个简单的解决方案:
.fall-enter.fall-enter-active {
position: absolute;
height: 97%;
margin-top: -5px;
}
.fall-enter.fall-enter-active, .fall-leave.fall-leave-active {
transition: transform 1s ease-in;
}
工作示例: http://jsfiddle.net/6985qr33/5/
玩得开心