视频学习地址https://www.bilibili.com/video/BV1h7411o7zE
代码
import React, { Component } from 'react'
import {
StyleSheet,
Text,
View,
PanResponder,
Animated,
Image,
Dimensions
} from 'react-native'
import styled from 'styled-components'
import Card from './Card'
import { connect } from 'react-redux'
import Login from './login/Login'
const screenWidth = Dimensions.get('window').width
const screenHeight = Dimensions.get('window').height + 50
class Home extends Component {
state = {
backgroundColor: '#fffae5',
pan: new Animated.ValueXY(0, 0),
scale: new Animated.Value(0.9),
translateY: new Animated.Value(40),
thirdTranslateY: new Animated.Value(20),
dataIndex: 0
}
UNSAFE_componentWillMount() {
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (event,gestureState) => {
if(gestureState.dx === 0 && gestureState.dy === 0) {
return false
} else {
if(this.props.action === 'openCard') {
return false
} else {
return true
}
}
},
onPanResponderGrant: () => {
Animated.spring(this.state.scale, { toValue: 1, useNativeDriver: false }).start()
Animated.spring(this.state.translateY, { toValue: 0, useNativeDriver: false }).start()
this.setState({ backgroundColor: '#ccc' })
Animated.spring(this.state.thirdTranslateY, { toValue: 45, useNativeDriver: false }).start()
},
onPanResponderMove: Animated.event([
null,
{ dx: this.state.pan.x, dy: this.state.pan.y }
],
{ useNativeDriver: false }),
onPanResponderRelease: () => {
const currentY = this.state.pan.y.__getValue();
if (currentY > 250) {
Animated.timing(this.state.pan, {
toValue: { x: 0, y: 800 },
duration: 200,
useNativeDriver: false
}).start(() => {
Animated.timing(this.state.pan, {
toValue: { x: 0, y: 0 },
duration: 0,
useNativeDriver: false
}).start()
this.setState({ dataIndex: shouldIndex(this.state.dataIndex) })
Animated.spring(this.state.scale, { toValue: 0.9, useNativeDriver: false }).start()
})
} else {
Animated.spring(this.state.pan, {
toValue: { x: 0, y: 0 },
useNativeDriver: false
}).start()
Animated.spring(this.state.scale, { toValue: 0.9, useNativeDriver: false }).start()
Animated.spring(this.state.translateY, { toValue: 40, useNativeDriver: false }).start()
Animated.spring(this.state.thirdTranslateY, { toValue: 20, useNativeDriver: false }).start()
}
this.setState({ backgroundColor: '#fffae5' })
}
})
}
render() {
const { scale, translateY, backgroundColor, thirdScale, thirdTranslateY, dataIndex } = this.state
return (
<View style={[styles.container, { backgroundColor: backgroundColor }]}>
<Animated.View style={
{
transform: [
{ translateX: this.state.pan.x, },
{ translateY: this.state.pan.y, }
]
}}
{...this._panResponder.panHandlers}
>
<Card canOpen={true} {...CardData[dataIndex]} />
</Animated.View>
<Animated.View style={[
styles.otherCard,
{
transform: [
{ scale: scale },
{ translateY: translateY }
]
}]}
>
<Card {...CardData[shouldIndex(dataIndex)]} />
</Animated.View>
<Animated.View style={[
styles.otherCard,
{
transform: [
{ scale: 0.9 },
{ translateY: thirdTranslateY }
],
zIndex: -2
}]}
>
<Card {...CardData[shouldIndex(dataIndex + 1)]} />
</Animated.View>
{/* <Login /> */}
</View>
);
}
}
const styles = StyleSheet.create({
otherCard: {
position: 'absolute',
zIndex: -1
},
container: {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#ddd',
width: '100%',
height: screenHeight
}
});
function shouldIndex(index) {
let shouldIndex = index + 1
if (shouldIndex > CardData.length - 1) {
return 0
}
return shouldIndex
}
const CardData = [
{
image: require('../assets/0.jpg'),
title: 'Awesome Styled Components',
startNum: 29.5,
info: 'Visual primitives for the component age Use the best bits of ES6 and CSS to style your apps without stress 💅Visual primitives for the component age Use the best bits of ES6 and CSS to style your apps without stress 💅Visual primitives for the component age Use the best bits of ES6 and CSS to style your apps without stress 💅Visual primitives for the component age Use the best bits of ES6 and CSS to style your apps without stress 💅'
},
{
image: require('../assets/vuelogo.jpg'),
title: 'Vue.js',
startNum: 165,
info: 'Vue is a JavaScript framework for building websites. The intent of Vue is to make the integration of other JavaScript libraries easy. It is designed to organize and simplify web development.'
},
{
image: require('../assets/reactnative.jpg'),
title: 'react-native',
startNum: 87.5,
info: 'A framework for building native apps with React. '
},
{
image: require('../assets/reactlogl.jpg'),
title: 'react.js',
startNum: 149,
info: 'A declarative, efficient, and flexible JavaScript library for building user interfaces.'
},
{
image: require('../assets/nodelogo.jpg'),
title: 'node.js',
startNum: 70,
info: '一个用Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时'
},
{
image: require('../assets/jQuery.jpg'),
title: 'jQuery',
startNum: 53.4,
info: 'jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers. '
}
]
function mapStateToProps (state) {
return {action: state.action}
}
export default connect(
mapStateToProps
)(Home)
卡片组件
import React, { Component } from 'react'
import { TouchableNativeFeedback, Animated, Dimensions, StatusBar } from 'react-native'
import styled from 'styled-components'
import Icon from 'react-native-vector-icons/FontAwesome'
import { connect } from 'react-redux'
import LinearGradient from 'react-native-linear-gradient'
const screenWidth = Dimensions.get('window').width
const screenHeight = Dimensions.get('window').height + 50
class Card extends Component {
static defaultProps = {
image:require('../assets/0.jpg'),
title:'Awesome Styled Components',
startNum:30,
info:'Visual primitives for the component age Use the best bits of ES6 and CSS to style your apps without stress 💅'
}
state = {
ViewWidth: new Animated.Value(300),
ViewHeight: new Animated.Value(450),
textTop: new Animated.Value(20),
closeBtnOpacity: new Animated.Value(0),
}
render() {
const { ViewHeight, ViewWidth, textTop,closeBtnOpacity } = this.state
return (
<TouchableNativeFeedback
onPress={this.openCard}>
<AnimatedView style={{
elevation: 5,
width: ViewWidth,
height: ViewHeight
}}>
<Image source={this.props.image} />
<AnimatedText
style={{
marginTop: textTop
}}
>{this.props.title}</AnimatedText>
<TouchableNativeFeedback onPress={this.closeCard}>
<AnimatedCloseVtn style={{ opacity: closeBtnOpacity }}>
<Icon name='times' size={30} />
</AnimatedCloseVtn>
</TouchableNativeFeedback>
<Start>Star {this.props.startNum}K+</Start>
<Info>{this.props.info}</Info>
<LinearGradient
colors={["rgba(255,255,255,0.2)","rgba(255,255,255,0.8)"]}
style={{
position: 'absolute',
height: '30%',
width: '100%',
bottom: 0,
borderRadius: 20
}}
/>
</AnimatedView>
</TouchableNativeFeedback>
)
}
openCard = () => {
if(!this.props.canOpen) return
this.AnimatedSpring(this.state.ViewWidth,screenWidth)
this.AnimatedSpring(this.state.ViewHeight,screenHeight)
this.AnimatedSpring(this.state.textTop,60)
this.AnimatedSpring(this.state.closeBtnOpacity,1)
StatusBar.setHidden(true)
this.props.openCard()
}
closeCard = () => {
this.AnimatedSpring(this.state.ViewWidth,300)
this.AnimatedSpring(this.state.ViewHeight,450)
this.AnimatedSpring(this.state.textTop,20)
this.AnimatedSpring(this.state.closeBtnOpacity,0)
StatusBar.setHidden(false)
this.props.closeCard()
}
AnimatedSpring = (AnimatedName,toValue) => {
Animated.spring(AnimatedName,{
toValue,
useNativeDriver: false
}).start()
}
}
const CloseBtn = styled.View`
width: 40px;
height: 40px;
border-radius: 40px;
background-color: #fff;
position: absolute;
top: 10px;
right: 10px;
justify-content: center;
align-items : center
`
const AnimatedCloseVtn = Animated.createAnimatedComponent(CloseBtn)
const Info = styled.Text`
margin: 5px 10px;
position: absolute;
top: 300px;
max-height: 25%;
height: 450px
`
const Text = styled.Text`
font-size: 25px;
color: ${props => props.color || '#fff'};
font-weight: bold;
text-transform: capitalize;
margin: 5px 10px;
text-shadow: 0 0 5px #000
`
const AnimatedText = Animated.createAnimatedComponent(Text)
const Start = styled(Text)`
position: absolute;
top: 250px;
text-transform: uppercase;
text-shadow: 0 4px 10px #000
`
const Image = styled.Image`
width: 100%;
height: 290px;
position: absolute
`
const View = styled.View`
border-radius: 12px;
background-color: #fff;
overflow: hidden;
margin: 0
`
const AnimatedView = Animated.createAnimatedComponent(View)
function mapStateToProps (state) {
return {action: state.action}
}
function mapDispatchToProps (dispatch) {
return {
openCard: () => dispatch({
type: 'OPEN_CARD'
}),
closeCard: () => dispatch({
type: 'CLOSE_CARD'
})
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Card)
控制卡片点击展开时不可移动需要使用redux来控制onMoveShouldSetPanResponder为false
redux
import { createStore, combineReducers } from 'redux'
function isLogin (state = false, action) {
if (action.type === 'LOGIN') {
return true
} else if (action.type === 'LOGIN') {
return false
} else {
return state
}
}
function action (state = 'closeCard', action) {
if (action.type === 'OPEN_CARD') {
return 'openCard'
} else if (action.type === 'CLOSE_CARD') {
return 'closeCard'
} else {
return state
}
}
const reducer = combineReducers({
action,
isLogin
})
const store = createStore(reducer)
export default store
来找我玩啊