我正在尝试在React(0.14)/ Omniscient应用程序中实现无限滚动。到目前为止,我一直在使用react-infinity库的修改版本,但它需要知道元素高度。如何使用动态元素高度实现无限滚动?
我已经部署了一个名为InfinitelyScrolling的演示应用程序来显示我尝试做的事情,源代码位于GitHub。这个应用程序展示了不同高度的元素,并且反应无限强迫每个元素具有一定的高度。
我修改的react-infinity源代码实现了无限滚动,如下所示:
'use strict';
var React = require('react');
let s = require('react-prefixr')
function realRender(direction) {
var windowWidth = this.state.windowWidth;
var windowHeight = this.state.windowHeight;
var elementHeight = this.props.mobileWidth <= windowWidth ? this.props.elementHeight :
this.props.elementMobileHeight;
var windowY, elementY;
if (direction === 'vertical') {
windowY = windowHeight;
elementY = elementHeight;
} else {
windowY = windowWidth;
elementY = elementWidth;
}
var numElements = 1;
// Number of pixels the container has been scrolled from the top
var scrollStart = this.state.scrollTop - this.props.scrollDelta;
console.log(`Scrolled ${this.state.scrollTop} pixels from the top`)
var numBefore = Math.floor(scrollStart / elementHeight);
console.log(`Number of elements before scroll start: ${numBefore}`)
var numVisible = Math.ceil((numBefore * elementY + windowY) / elementY);
console.log(`Number of visible elements: ${numVisible}`)
// Keep some extra elements before and after visible elements
var extra = numElements === 1 ? Math.ceil(numVisible / 2) : 2;
var lowerLimit = (numBefore - extra) * numElements;
var higherLimit = (numVisible + extra * 2) * numElements;
console.log(`Lower limit: ${lowerLimit}, higherLimit: ${higherLimit}, extra: ${extra}`)
var elementsToRender = [];
this.props.data.forEach(function (obj, index) {
if (index >= lowerLimit && index < higherLimit) {
console.log(`Rendering data item ${index}:`, obj)
var column, row;
if (direction === 'vertical') {
column = index % numElements;
row = Math.floor(index / numElements);
} else {
row = index % numElements;
column = Math.floor(index / numElements);
}
var id = obj.id != null ? obj.id : obj._id;
var yOffset = (row * elementHeight);
var subContainer = SubContainer(
{
key: id,
transform: 'translate(0, ' + yOffset + 'px)',
height: elementHeight + 'px',
},
this.props.childComponent(obj)
);
elementsToRender.push(subContainer);
} else {
console.log(`Skipping data item ${index}`)
}
}.bind(this));
return React.createElement(this.props.containerComponent,
{
className: 'infinite-container', style: {
height: (elementHeight * Math.ceil(this.props.data.length / numElements)) + 'px',
width: '100%',
position: 'relative',
},
},
elementsToRender
);
}
var SubContainer = React.createFactory(React.createClass({
displayName: 'Sub-Infinity',
getInitialState: function () {
return {
transform: this.props.transform + ' scale(1)',
opacity: '0',
};
},
componentDidMount: function (argument) {
this.setState({transform: this.props.transform + ' scale(1)', opacity: '1',});
},
componentWillReceiveProps: function (newProps) {
this.setState({transform: newProps.transform + ' scale(1)',});
},
componentWillEnter: function (cb) {
this.setState({transform: this.props.transform + ' scale(1)', opacity: '0',});
setTimeout(cb, 100);
},
componentDidEnter: function () {
this.setState({transform: this.props.transform + ' scale(1)', opacity: '1',});
},
componentWillLeave: function (cb) {
this.setState({transform: this.props.transform + ' scale(1)', opacity: '0',});
setTimeout(cb, 400);
},
render: function () {
return React.DOM.div({style: s({
position: 'absolute',
top: '0',
left: '0',
transform: this.state.transform,
width: this.props.width,
height: this.props.height,
transition: this.props.transition,
opacity: this.state.opacity,
}),},
this.props.children
);
},
}));
var Infinite = React.createClass({
displayName: 'React-Infinity',
getDefaultProps: function () {
return {
data: [],
maxColumns: 100,
transition: '0.5s ease',
id: null,
className: 'infinite-container',
elementClassName: '',
component: 'div',
containerComponent: 'div',
mobileWidth: 480,
justifyOnMobile: true,
scrollDelta: 0,
direction: 'vertical',
preRender: false,
};
},
propTypes: {
data: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
maxColumns: React.PropTypes.number,
id: React.PropTypes.string,
className: React.PropTypes.string,
elementHeight: React.PropTypes.number,
mobileWidth: React.PropTypes.number,
elementMobileHeight: React.PropTypes.number,
elementMobileWidth: React.PropTypes.number,
justifyOnMobile: React.PropTypes.bool,
preRender: React.PropTypes.bool,
scrollDelta: React.PropTypes.number,
},
getInitialState: function () {
return {
scrollTop: 0,
windowWidth: this.props.windowWidth || 800,
windowHeight: this.props.windowHeight || 600,
loaded: false,
extra: {
count: 0,
},
};
},
componentDidMount: function () {
global.addEventListener('resize', this.onResize);
global.addEventListener('scroll', this.onScroll);
this.onScroll()
this.setState({
loaded: true,
windowWidth: global.innerWidth,
windowHeight: global.innerHeight,
elementHeight: this.props.elementHeight ||
this.refs.element1.getDOMNode().getClientRects()[0].height,
scrollTop: global.scrollY || 0,
});
},
onScroll: function () {
var scrollTop = global.scrollY;
if (this.state.scrollTop !== scrollTop) {
this.setState({scrollTop: scrollTop,});
}
},
onResize: function () {
this.setState({windowHeight: global.innerHeight, windowWidth: global.innerWidth,});
},
componentWillUnmount: function () {
global.removeEventListener('resize', this.onResize);
global.removeEventListener('scroll', this.onScroll);
},
render: function(){
if (!this.state.loaded) {
return this.props.preRender ? React.createElement(this.props.containerComponent,
{
className: this.props.className,
id: this.props.id,
style: {
fontSize: '0',
position: 'relative',
},
}, this.props.data.map(function (elementData, i) {
return React.createElement(this.props.component, {
style: {display: 'inline-block', margin: '32px', verticalAlign: 'top',},
}, React.createElement(this.props.childComponent, elementData));
}.bind(this)))
: null;
}
var direction = this.props.direction;
if (direction !== 'horizontal' && direction !== 'vertical') {
direction = 'vertical';
console.warn('the prop `direction` must be either "vertical" or "horizontal". It is set to',
direction);
}
return realRender.call(this, direction);
},
});
module.exports = Infinite;
答案 0 :(得分:1)
我在Flex容器中实现了无限滚动,其中包含多个砌体组件(每个组件具有可变项高度,来自互联网的图像)。
我使用react-list来执行此操作(https://github.com/orgsync/react-list)。
以下是一个例子:
import loadAccount from 'my-account-loader';
import React from 'react';
import ReactList from 'react-list';
class MyComponent extends React.Component {
state = {
accounts: []
};
componentWillMount() {
loadAccounts(this.handleAccounts);
}
handleAccounts(accounts) {
this.setState({accounts});
}
renderItem(index, key) {
return <div key={key}>{this.state.accounts[index].name}</div>;
}
render() {
return (
<div>
<h1>Accounts</h1>
<div style={{overflow: 'auto', maxHeight: 400}}>
<ReactList
itemRenderer={this.renderItem}
length={this.state.accounts.length}
type='variable'
/>
</div>
</div>
);
}
}
&#13;
如果您有可变的元素高度,则必须将类型设置为variable
。