react-native-auto-fade-images

内容

N 张图片,每间隔一段时间隐藏一张,后显示第二张,依次过渡

思路

  1. 绝对定位
  2. 使用两个 Image 组件
  3. 在第一个 Image fadeOut 完成后,将第一个 Image source 设置为 下一张图片。此时状态:隐藏opacity: 0的第二张图片,盖住一张显示opacity: 1的第二张图片
  4. 第一个 Image 组件从隐藏opacity: 0变更为显示opacity: 1,并设置第二个 Image 组件 source 为 第三张图片。此时状态:显示的第二张图片,盖住一张显示的第三章图片。
  5. 开始 fadeOut 动画,重复 3、4、5 步骤

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import * as React from 'react'
import { View, Image, StyleSheet } from 'react-native'
import * as Animatable from 'react-native-animatable'

const styles = StyleSheet.create({
image: {
position: 'absolute',
top: 0,
left: 0,
zIndex: 1,
},
animateImage: {
position: 'absolute',
top: 0,
left: 0,
zIndex: 2,
},
})

interface Props {
containerStyle: Object
imageStyle: Object
sources: any[]
animDuration: number
holdDuration: number
}

interface State {
current: number
paused: boolean
}

export default class extends React.Component<Props, State> {
state = {
current: 0,
paused: false,
}

animateImage: any
timer: any

componentDidMount() {
this.startAnimation()
}

getNextIndex = () => {
const { sources } = this.props
const { current } = this.state
return current === sources.length - 1 ? 0 : current + 1
}

componentWillUnmount() {
this.timer && clearTimeout(this.timer)
this.animateImage && this.animateImage.stopAnimation()
this.animateImage = null
}

startAnimation = () => {
const { animDuration } = this.props
if (this.animateImage) {
this.animateImage.transitionTo({
opacity: 0,
}, animDuration, 'ease-out')
this.timer && clearTimeout(this.timer)
this.timer = setTimeout(this.onAnimateEnd, animDuration)
}
}

onAnimateEnd = () => {
const { holdDuration } = this.props
// 先停留一下,等 Animatable.Image 设置成和 下一张图片一样的图片
this.setState({ paused: true }, () => {
if (this.animateImage) {
// 再将这张相同的图片显示出来,避免直接设置造成闪烁的问题
this.animateImage.transitionTo({ opacity: 1 })
this.timer && clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.setState({ paused: false, current: this.getNextIndex() })
this.startAnimation()
}, holdDuration)
}
})
}

render() {
const { containerStyle, imageStyle, sources } = this.props
if (!sources || !sources.length) {
return null
}

if (sources.length === 1) {
return <View style={containerStyle}>
<Image source={sources[0]} style={[styles.image, imageStyle]} />
</View>
}

const { current, paused } = this.state
const next = this.getNextIndex()

return <View style={containerStyle}>
<Animatable.Image
ref={ref => this.animateImage = ref}
style={[styles.animateImage, imageStyle]}
useNativeDriver={true}
source={sources[paused ? next : current]}
/>
<Image source={sources[next]} style={[styles.image, imageStyle]} />
</View>
}
}

缺陷

  1. sources 属性长度需不可变,否则会引发意外情况