rax 内置组件 WaterFall 及 无限瀑布流的实现
一、描述
rax 内置组件 rax-waterfall
是 rax 为了更加方便的构建瀑布流方案而实现的内置组件。
使用 rax-waterfall 需要 weex 版本 v0.11.0+
。
使用的时候,建议单独安装,目前 rax 已经不建议安装 rax-components
了,使用哪个组件就单独安装依赖,更能够减少 bundle 的大小。
官方文档地址:
一些涉及官方文档的参数说明不再重复了。
二、源码
1、依赖
rax-waterfall
使用了下面这些依赖:
import {isWeex} from 'universal-env';
import View from 'rax-view';
import ScrollView from 'rax-scrollview';
import RefreshControl from 'rax-refreshcontrol';
2、web 环境的渲染方式
和 ScrollView
组件一样,针对 web 和 weex 平台, 采用了不同的渲染方式,在 web 平台上,rax 构建了无状态的 WebFall
组件, 用来渲染在 web 容器下的单个 item。
class WebFall extends PureComponent {
calcHeightSum = (arr) => {
let sum = 0;
arr && arr.forEach(item => {
sum += item;
});
return sum;
};
render() {
const {renderItem = () => {}, dataSource, columnCount = 1} = this.props;
let columns = [];
let moduleHeights = [];
for (let i = 0; i < columnCount; i++) {
columns[i] = [];
moduleHeights[i] = 0;
}
dataSource && dataSource.forEach((item, i) => {
let targetColumnIndex = 0;
let minHeight = moduleHeights[0];
for (let j = 0; j < columnCount; j++) {
if (moduleHeights[j] < minHeight) {
minHeight = moduleHeights[j];
targetColumnIndex = j;
}
}
moduleHeights[targetColumnIndex] += item.height;
columns[targetColumnIndex].push(item);
});
return (<View style={styles.waterfallWrap}>
{columns.map((column, index) => {
return (<View key={'column' + index} style={styles.waterfallColumn}>
{column.map((item, j) => {
return renderItem(item, 'c_' + index + j);
})}
</View>);
})}
</View>);
}
}
如果在方法 renderItem(item,index)
中,将 index
直接渲染出来,会发现在 weex 容器和 web容器中渲染出来的 index 是不一样的, 在上面的代码中可以看出来,在 web 平台中,传递 index ,实际上传递的是 'c_' + index + j
。
这个设计目前没有发现理由,核心的 return 部分如下:
return (<View style={styles.waterfallWrap}>
{columns.map((column, index) => {
return (<View key={'column' + index} style={styles.waterfallColumn}>
{column.map((item, j) => {
return renderItem(item, 'c_' + index + j);
})}
</View>);
})}
但是,看源码能够发现,在 web 环境中,rax-waterfall
最终竟然最终输出的是 <ScrollView>
组件,那在结合 <ScrollView>
组件在 web 环境的渲染结果,最终还是渲染了 <View>
。
因此,最终在 web 平台渲染的代码如下,其中 cells
是 <WebFall>
组件的组合。
return (<ScrollView {...props} ref="scrollview">
{cells}
</ScrollView>);
3、weex 容器环境的渲染方式
如果是 weex 容器,则直接使用了 weex 的内置组件 waterfall。
由于渲染方式的不同,对于数据的处理和内部组件的输出也是不同的,weex 容器环境因为使用了 waterfall
,所以子组件是 <cell>
,而 web 环境下,则是自定义的无状态组件 <WebFall>
。
if (isWeex) {
dataSource && dataSource.forEach((item, index) => {
cells.push(<cell {...cellProps}>{renderItem(item, index)}</cell>);
});
} else {
cells = cells.concat(<WebFall {...props} />);
}
因此 weex 容器环境的渲染代码如下(其实整个 rax-waterfall
的设计还是集合了 waterfall
):
if (isWeex) {
return (<waterfall
style={{width: 750}}
{...props}
onLoadmore={props.onEndReached}
loadmoreoffset={props.onEndReachedThreshold}
loadmoreretry={this.state.loadmoreretry}
>
{cells}
</waterfall>);
}
4、不同容器环境下 header 组件的渲染方式
因为 web 和 weex 的整体渲染方式的不同,所以 header 的渲染方式也不相同。
在 rax-waterfall
的代码中,自定义了一个 Header 无状态组件,代码如下:
class Header extends PureComponent {
render() {
if (isWeex) {
return <header {...this.props} append="tree" />;
} else {
return <View {...this.props} />;
}
}
}
三、使用 rax-waterfall
实现无限瀑布流
1、实现效果
期望效果:
上面动图是在 web 平台上截取的,可以发现本来 index 是一个数字 ,如 141
,但是渲染出来的是 c_141
,这就是在 web 环境下,对于 renderItem(item,index)
中 index 参数的处理。
1、state 的设计
瀑布流的重点就是,图片的高度是不一样的,因此一般返回的数据中,都有高度这个值。
state = {
list:[
{height:300,item:{}},
{height:200,item:{}},
{height:100,item:{}},
{height:150,item:{}},
{height:250,item:{}},
{height:350,item:{}},
{height:300,item:{}},
{height:200,item:{}},
{height:100,item:{}},
{height:150,item:{}},
{height:250,item:{}},
{height:350,item:{}},
{height:300,item:{}},
{height:200,item:{}},
{height:100,item:{}},
{height:150,item:{}},
{height:250,item:{}},
{height:350,item:{}},
]
}
2、renderItem 方法
renderItem = (item, index) => {
return (
<View style={[styles.item,{height:item.height}]}>
<Text>Item {index}</Text>
</View>
)
}
3、App.js
完整的组件代码
import {createElement, Component} from 'rax';
import View from 'rax-view';
import Text from 'rax-text';
import WaterFall from 'rax-waterfall';
import styles from './App.css';
class App extends Component {
state = {
list:[
{height:300,item:{}},
{height:200,item:{}},
{height:100,item:{}},
{height:150,item:{}},
{height:250,item:{}},
{height:350,item:{}},
{height:300,item:{}},
{height:200,item:{}},
{height:100,item:{}},
{height:150,item:{}},
{height:250,item:{}},
{height:350,item:{}},
{height:300,item:{}},
{height:200,item:{}},
{height:100,item:{}},
{height:150,item:{}},
{height:250,item:{}},
{height:350,item:{}},
]
}
renderHeader = () =>{
return (
<View>
<Text style={styles.header}>WaterFall Header </Text>
</View>
);
}
rednerFooter = () => {
return (
<View>
<Text style={styles.header}>WaterFall Footer</Text>
</View>
)
}
renderItem = (item, index) => {
return (
<View style={[styles.item,{height:item.height}]}>
<Text>Item {index}</Text>
</View>
)
}
onEndReached = () => {
const tmp = [
{height:100,item:{}},
{height:500,item:{}},
{height:200,item:{}},
{height:450,item:{}},
{height:250,item:{}},
{height:350,item:{}},
];
this.setState({
list:this.state.list.concat(tmp)
});
}
render() {
return (
<View style={styles.app}>
<WaterFall
style={styles.waterfall}
dataSource = {this.state.list}
renderItem = {this.renderItem}
renderHeader = {this.renderHeader}
renderFooter = {this.rednerFooter}
onEndReached = {this.onEndReached}
columnCount = {3}
columnWidth = {200}
></WaterFall>
</View>
);
}
}
export default App;
4、完整的 css 代码
.app{
width:750;
}
.header{
text-align: center;
width:750;
height:50;
background-color: #cccccc;
}
.item{
justify-content: center;
align-items: center;
margin-bottom: 10;
width:200;
background-color: #dfdfdf;
}
.waterfall{
flex-direction: row;
height:1333;
}
文章版权:Postbird-There I am , in the world more exciting!
本文链接:http://www.ptbird.cn/rax-waterfall.html
转载请注明文章原始出处 !