react-native 一起来实现一个高性能循环滚动的轮播图吧!
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react-native 一起来实现一个高性能循环滚动的轮播图吧!相关的知识,希望对你有一定的参考价值。
参考技术AHey,可以叫我Doho,我是一名前端开发,目前在我司负责一款由RN开发的App。
很兴奋和大家分享我在使用 react-native-reanimated 并制作 react-native-reanimated-carousel 中学习到的经验,由于 react-native-reanimated 的中文资料及介绍视频较少所以我的学习途径大多来自于 官方文档 、 油管上光头大哥的视频 这两个。
官方文档 内容大多是英文,所以查阅起来对英文不是很好的伙伴还是有一些障碍的。
油管上光头大哥的视频 大部分视频还是不错的,但部分视频的讲解部分有些跳跃所以也会存在有些地方难以跟上的情况。
所以我打算把我学习过的视频尝试重新实现一遍,并且用中文文章的方式进行产出,其实也是想锻炼下自己的总结能力,和文笔。后续会以文章或者视频系列的方式推送!~致敬 光头大哥
我的GitHub主页
我的Carousel开源库
Snack 演示
为什么选择Reanimated呢,因为在React Native中,默认情况下,所有更新都会至少延迟一帧,因为UI和JS线程之间的通信是异步的,UI线程永远不会等待JS线程处理完全所有事件。
而且除了JS在执行Diff、更新、执行应用程序的业务逻辑、处理网络请求…以外,事件通常也无法立即处理,从而导致会有更严重的延迟。
Reanimated的方式则是把处理动画和事件的逻辑从JSt线程放到UI线程去做。
大概是这样子,更细节的地方不是本文的重点,大家可以自行google~
受业务需求要在首页增加一个支持循环滚动的轮播图,所以在github上搜索了一下社区里使用比较好用的组件。排除掉一些四、五年前才更新过的库,还剩下一个名叫 react-native-snap-carousel 的组件库。
但在使用过程中发现了问题,在循环滚动中快速滑动时会出现卡顿的情况,情况看起来像是滚动到头部或者末尾时需要等待元素追加到指定位置。
因为时间问题,所以并没有深入去看源码的实现部分,中间通过社区里大家提到的各种办法进行了尝试也是无法解决,大多是通过增加前后的预渲染数量,但其实还是治标不治本的方式。
而且这个库的维护频率比较低,也堆积着大量未解决的问题,虽然README中有说到 未来 会有一个完全用 react-native-gesture-handler + react-native-reanimated 实现的版本,而且会非常好用。但距离他们宣布时间已经过去了很久还没有发布正式版,所以求人不如求己,自己撸一个吧,但误打误撞使用了和他们同样计划的库,就是上方的手势与动画库。
循环滚动时向一侧快速滑动不会出现卡顿的情况
包裹视图容器后可以通过回调来获得不同手势移动的参数。
一个简单的hook,用在 PanGestureHandler 的 onHandlerStateChange props上,可以在hook中设置onStart、onActive、onEnd…各种事件的响应事件。
Reanimated产生的动画值,它的变化将影响动画的行为。
Reanimated 需要使用 useAnimatedStyle 来生成 style ,因为它将在 SharedValue 变化时控制生成变化后的样式,同样他生成的样式也允许与 Reanimated.View 进行关联。
使用Reanimated动画值的基本条件,将 SharedValue 置入 useAnimatedStyle 后,可将返回的 style 传递给 styles 属性,即可使元素产生动画效果。
响应一个 SharedValue 值的变化,并产生一个只读的值。
使 SharedValue 产生一个映射,这在修改一个动画效果的时候非常有用,比方说我们有一个头像,想没登录的时候它宽度是100,登录后放大到200。
Types: interpolate(SharedValue,inputRange,outputRange,?ExtrapolateParameter)
SharedValue: 动画的值
inputRange: 输入区间
outputRange: 输出区间
ExtrapolateParameter?: 当输入区间溢出后,是否继续按照输出区间变化(可选)
这样我们就有了一个可以使用的初始项目,他将抹平我们后续可能会带来的各种依赖差异。
然后想让元素动起来,我们还需要两个步骤 生成偏移值X 、 使用偏移值X的元素 。
Carousel.tsx
useComputedAnim.ts
这里我们完成了 生成偏移值X 。
Carousel.tsx
Layouts.tsx
这里我们完成了 使用偏移值X的元素 ,现在我们的轮播图已经有了一个基本的样子,他已经可以朝着一侧进行滑动了,但如果想让他进行循环,则还需要完成 useOffsetX 中的逻辑。
这个方法内计算部分比较绕,但大概思路就是当偏移值X变化后,确认他是否超过我们所设定的边界,超过了就放到另一边。不理解也没关系,因为或许你可以理清思路实现一套自己的运算逻辑
useOffsetX.ts
本篇文章的完整版Demo react-native-reanimated-carousel-example
后续会在我的项目 react-native-reanimated-carousel 中完善更多的API,让这个组件变得更易用,但或许并不会增加 react-native-snap-carousel 中那样复杂的UI效果,目的还是让这个组件变得更加简单与灵活。
希望更多伙伴能参与进来一起维护,或者来提更多建议,来吧来吧!~ 项目
感谢大家的阅读,希望能收到建议、问题或指正。
觉得好用就来个star🌟吧,嘻嘻 react-native-reanimated-carousel ,谢谢~!
后续我会写更多关于react-native-reanimated v2的系列文章,希望对大,家有所帮助! 我的GitHub主页
使用react-native做一个简单的应用-06商品界面的实现
商品界面实现起来很简单,其实就是一个listview的使用:
关于listview的使用,在官方文档里面已经介绍的很详细了。在这里我要提一个坑。
listview在Android和iOS中的效果是不一样的,listview对iOS的支持已经很好了,但是对android很多属性都不支持。有些地方官方文档也说的模棱两可的,因此如果想做炫酷的上、下拉效果的话,建议做iOS。
下面看一下代码吧:
1 ‘use strict‘ 2 3 var React = require(‘react-native‘) 4 import Icon from ‘react-native-vector-icons/FontAwesome‘ 5 import AniImage from ‘../components/AniImage‘ 6 import AniRoll from ‘../components/AniRoll‘ 7 var { 8 Image, 9 ListView, 10 StyleSheet, 11 Text, 12 View, 13 TouchableOpacity, 14 Navigator, 15 PropTypes 16 } = React 17 18 var REQUEST_URL = ‘http://192.168.6.5:8888/getCommidity‘ 19 20 class Commodity extends React.Component { 21 static propTypes = { 22 navigator: PropTypes.object, 23 fetch: PropTypes.func 24 }; 25 constructor (props) { 26 super(props) 27 this.state = { 28 dataSource: new ListView.DataSource({ 29 rowHasChanged: (row1, row2) => row1 !== row2 30 }), 31 loaded: false 32 } 33 } 34 componentDidMount () { 35 this.fetchData() 36 } 37 38 fetchData () { 39 fetch (REQUEST_URL) 40 .then((response) => response.json()) 41 .then((responseData) => { 42 this.setState({ 43 dataSource: this.state.dataSource.cloneWithRows(responseData), 44 loaded: true 45 }) 46 }) 47 .done() 48 } 49 50 render () { 51 if (!this.state.loaded) { 52 return this.renderLoadingView() 53 } 54 55 return ( 56 <ListView 57 initialListSize={3} 58 dataSource={this.state.dataSource} 59 renderRow={this.renderCommidity.bind(this)} 60 style={styles.listView}/> 61 ) 62 } 63 64 renderLoadingView () { 65 return ( 66 <View style={styles.txtContainer}> 67 <AniRoll inputRange={[0, 1]} outputRange={[‘0deg‘, ‘10000deg‘]} style={{width: 50, height: 50}} /> 68 <Text> 69 正在加载图文。。。 70 </Text> 71 </View> 72 ) 73 } 74 75 renderCommidity (commidities) { 76 return ( 77 <View style={styles.container} > 78 <Image style={styles.viewTopImage} source={require(‘../assets/listbg.png‘)}> 79 <TouchableOpacity onPress={this.toCommodityScreen.bind(this, commidities)}> 80 <AniImage 81 type={‘image‘} 82 inputRange={[0, 100]} 83 outputRange={[0, 1]} 84 style={styles.aniImage} 85 url={‘http://192.168.6.5:8888/getImage?imgName=‘ + commidities.imgPath1} /> 86 </TouchableOpacity> 87 </Image> 88 <View style={styles.viewTxtContent}> 89 <TouchableOpacity> 90 <Text style={styles.txtContent}>{commidities.title}</Text> 91 </TouchableOpacity> 92 </View> 93 <View style={styles.viewBottom}> 94 <View style={styles.viewBottomLeft}> 95 <Icon name=‘heart-o‘ size={20} /> 96 <Text style={styles.txtContent} >{commidities.love}</Text> 97 </View> 98 <View style={styles.viewBottomRight}> 99 <View style={styles.viewBottomRightContainer}> 100 <Icon name=‘clock-o‘ size={20} /> 101 <Text style={styles.txtContent} >{commidities.date}</Text> 102 </View> 103 </View> 104 </View> 105 <Image source={require(‘../assets/splite.png‘)} style={styles.imageSplit}/> 106 </View> 107 ) 108 } 109 110 toCommodityScreen (commidities) { 111 var commodity = commidities 112 let navigator = this.props.navigator 113 navigator.push({id: ‘CommodityScreen‘, sceneConfig: Navigator.SceneConfigs.HorizontalSwipeJump, passProp: {commodity}}) 114 } 115 } 116 117 var styles = StyleSheet.create({ 118 container: { 119 flex: 1, 120 flexDirection: ‘column‘, 121 backgroundColor: ‘#ffffff‘ 122 }, 123 txtContainer: { 124 flex: 1, 125 alignItems: ‘center‘, 126 justifyContent: ‘center‘, 127 backgroundColor: ‘#fffff‘ 128 }, 129 aniImage: { 130 height: 330, 131 width: null 132 }, 133 viewTopImage: { 134 height: 330, 135 width: null, 136 margin: 10 137 }, 138 txtContent: { 139 fontSize: 16, 140 marginLeft: 5 141 }, 142 viewTxtContent: { 143 marginLeft: 10, 144 marginRight: 10, 145 marginBottom: 5, 146 flex: 1 147 }, 148 txtBottom: { 149 flex: 1, 150 fontSize: 12 151 }, 152 viewBottom: { 153 marginLeft: 10, 154 marginRight: 10, 155 marginBottom: 5, 156 flex: 1, 157 flexDirection: ‘row‘ 158 }, 159 imageSplit: { 160 flex: 0.3, 161 backgroundColor: ‘#F0F0F0‘ 162 }, 163 numberText: { 164 fontSize: 15, 165 marginLeft: 20 166 }, 167 viewBottomLeft: { 168 flex: 1, 169 flexDirection: ‘row‘ 170 }, 171 viewBottomRight: { 172 flex: 1, 173 alignItems: ‘flex-end‘ 174 }, 175 viewBottomRightContainer: { 176 flex: 1, 177 flexDirection: ‘row‘ 178 } 179 }) 180 181 module.exports = Commodity
一、构造方法:
1 constructor (props) { 2 super(props) 3 this.state = { 4 dataSource: new ListView.DataSource({ 5 rowHasChanged: (row1, row2) => row1 !== row2 6 }), 7 loaded: false 8 } 9 }
dataSource就是listview的数据源,loaded则是判断数据是否加载完毕。
二、fetchData方法
获取listview的接口数据,数据请求完成之后,重新渲染界面将loaded设置为true
三、toCommodityScreen方法
进入商品的详情界面。
以上是关于react-native 一起来实现一个高性能循环滚动的轮播图吧!的主要内容,如果未能解决你的问题,请参考以下文章