写了一个背景菜单3D动画,累吐血



  • 不多说,上效果

    alt text

    在线 演示 https://rnplay.org/apps/pBrrUg

    bootstrap就有这种响应式3D效果的菜单,非常炫,
    我也想用RN做一个,结果就开始了漫长的自虐,各种gg,stackoverflow,没有现成的,只能问,还好老外都很厉害,给了思路,思路到落地又是漫长的自虐模式,各种试,好在每次上厕所的时候都灵光一现,搞了几天,终于完成,有需要的童鞋拿去发挥吧,保证你的app不再是呆板上下左右来回换来换去的UI

    总结一下

    • transform.perspective 这个家伙,默认值0,就报错,跟W3C上说的不一样,还有就是Animated的时候不用管它,当初以为关闭菜单要给它还原成默认值,结果坑了好久

    • Animated 一个变量不能控制多个动画,只能1对1,然后多值一起变化用Animated.parallel,
      还有,在Animated.timing的时候,变量可以设置超出上下限,也没搞清楚原理

    • interpolate,插值,比较混乱,没搞太明白,就知道一点,anim_translateX.inputRange的max决定首次加载的位置,有懂的童鞋还请赐教

    累吐血,上代码:

    'use strict';
    
    var React = require('react-native');
    var {
      AppRegistry,
      StyleSheet,
      Component,
      Image,
      View,
      Text,
      Navigator,
      ListView,
      Alert,
      TouchableHighlight,
      TouchableOpacity,
      TouchableWithoutFeedback,
      LayoutAnimation,
      Animated,
    } = React;
    
    import Dimensions from 'Dimensions'
    var width = Dimensions.get('window').width;
    var height = Dimensions.get('window').height;
    
    export default class MenuScreen extends Component {
        constructor(props) {
            super(props);
            this.state={
                loaded:false,
                anim: new Animated.Value(0),
                anim_rotateY: new Animated.Value(0),
                anim_translateX:new Animated.Value(width),
                transform:[],
                isMenuOpen:false,
            }
        }
        componentWillMount(){
            
    
        }
        componentDidMount(){
            this.setState({
                loaded:true
            })
        }
        onpressShowmethemenu(){
        }
        showMenu(){
            if(this.state.isMenuOpen){
                this.setState({isMenuOpen:false});
                Animated.parallel([
                    Animated.timing(
                        this.state.anim_translateX,{
                            toValue:width
                    }),
                    Animated.timing(
                        this.state.anim_rotateY,{
                            toValue:0
                    }),
                ]).start();
            }
            else {
                this.setState({isMenuOpen:true});
                Animated.parallel([
                    Animated.timing(
                        this.state.anim_translateX,{
                            toValue:width*1.24
                    }),
                    Animated.timing(
                        this.state.anim_rotateY,{
                            toValue:1
                    }),
                ]).start();
            }
        }
        closeMenu(){
            this.setState({isMenuOpen:false});
            Animated.parallel([
                Animated.timing(
                    this.state.anim_translateX,{
                        toValue:width
                }),
                Animated.timing(
                    this.state.anim_rotateY,{
                        toValue:0
                }),
            ]).start();
        }
        render(){
            return(
                    <Image style={[styles.container,{resizeMode:'cover'}]} source={{uri:'https://s-media-cache-ak0.pinimg.com/236x/24/f9/bc/24f9bcf5734dad1ce17be856d6e16719.jpg'}}>
                        <View style={{position:'absolute',width:120,right:0,top:120,justifyContent:'center'}}>
                            <Text style={[styles.text,styles.menutext,styles.red]}>Home</Text>
                            <Text style={[styles.text,styles.menutext]}>Features</Text>
                            <Text style={[styles.text,styles.menutext]}>Gallery</Text>
                            <Text style={[styles.text,styles.menutext]}>Profile</Text>
                            <Text style={[styles.text,styles.menutext]}>Pages</Text>
                            <Text style={[styles.text,styles.menutext]}>Contact</Text>
                            <Text style={[styles.text,styles.menutext]}>Pages</Text>
                            <TouchableOpacity onPress={this.closeMenu.bind(this)}>
                                <Text style={[styles.text,styles.menutext]}>Close</Text>
                            </TouchableOpacity>
                        </View>
    
                        <Animated.View ref='content' style={styles.content,{width:width,backgroundColor:'gray',flex:1,alignItems:'center',
                            transform:[{ perspective: 850},
                                       { translateX: this.state.anim_translateX.interpolate({
                                                         inputRange: [0, width],
                                                         outputRange: [width, 0],
                                       })},
                                       { rotateY: this.state.anim_rotateY.interpolate({
                                                         inputRange: [0, 1],
                                                         outputRange: ['0deg', '60deg'],
                                       })},
                                      ]
                            }}>
                            <Text style={{top:100,color:'white',backgroundColor:'gray',fontSize:20}}>
                                Image style=width:width,height:height
                            </Text>
                            <TouchableOpacity onPress={this.showMenu.bind(this)}>
                                <View style={{width:100,height:40,top:200,backgroundColor:'red',alignItems:'center',justifyContent:'center'}}>
                                    <Text style={{fontSize:20,color:'white'}}>Menu</Text>
                                </View>
                            </TouchableOpacity>
                        </Animated.View>
                    </Image>
            )
    
        }
    }
    
    const styles = StyleSheet.create({
        text:{color:'#E0E0E0'},gray:{color:'gray'},green:{color:'green'},red:{color:'red'},
        menutext:{fontSize:20,padding:10},
        container:{width:width,height:height,flex:1,justifyContent:'center',alignItems:'center'},
        menu:{width:width,height:height,flex:1,position:'absolute',left:0,top:0,backgroundColor:'#ff00ff'},
        menulist:{width:200,position:'absolute',right:0,top:100},
    })
    
    AppRegistry.registerComponent('SampleApp', () => MenuScreen);
    


  • 好酷炫啊
    (发帖不能少于8个字)



  • 直接copy了你的代码,发觉没有你截图的效果,还有就是你的查看效果的连接好像失效了。。。



  • 代码老了,跑不起来了,今天弄了一下,发现新rn有好多改进,不错

    还发现 rnplay.org 死了,他们启用新网址 https://sketch.expo.io/HJYvJKI3l

    一楼的代码留着做个版本控制吧,下面贴上新代码(无非就是声明变了)

    @DayDayUping

    
    'use strict';
    
    import React, { Component } from 'react';
    
    import {
      AppRegistry,
      Animated,
      StyleSheet,
      Image,
      View,
      TouchableOpacity,
      Dimensions,
      Text,
    } from 'react-native'
    
    
    var {height, width} = Dimensions.get('window');
    
    export default class MenuScreen extends Component {
        constructor(props) {
            super(props);
            this.state={
                loaded:false,
                anim: new Animated.Value(0),
                anim_rotateY: new Animated.Value(0),
                anim_translateX:new Animated.Value(width),
                transform:[],
                isMenuOpen:false,
            }
        }
        componentWillMount(){
            
    
        }
        componentDidMount(){
           
        }
        onpressShowmethemenu(){
        }
        showMenu(){
            if(this.state.isMenuOpen){
                this.setState({isMenuOpen:false});
                Animated.parallel([
                    Animated.timing(
                        this.state.anim_translateX,{
                            toValue:width
                    }),
                    Animated.timing(
                        this.state.anim_rotateY,{
                            toValue:0
                    }),
                ]).start();
            }
            else {
                this.setState({isMenuOpen:true});
                Animated.parallel([
                    Animated.timing(
                        this.state.anim_translateX,{
                            toValue:width*1.24
                    }),
                    Animated.timing(
                        this.state.anim_rotateY,{
                            toValue:1
                    }),
                ]).start();
            }
        }
        closeMenu(){
            this.setState({isMenuOpen:false});
            Animated.parallel([
                Animated.timing(
                    this.state.anim_translateX,{
                        toValue:width
                }),
                Animated.timing(
                    this.state.anim_rotateY,{
                        toValue:0
                }),
            ]).start();
        }
        render(){
            return(
                    <Image style={[styles.container,{resizeMode:'cover'}]} source={{uri:'https://s-media-cache-ak0.pinimg.com/236x/24/f9/bc/24f9bcf5734dad1ce17be856d6e16719.jpg'}}>
                        <View style={{position:'absolute',width:120,right:0,top:120,justifyContent:'center'}}>
                            <Text style={[styles.text,styles.menutext,styles.red]}>Home</Text>
                            <Text style={[styles.text,styles.menutext]}>Features</Text>
                            <Text style={[styles.text,styles.menutext]}>Gallery</Text>
                            <Text style={[styles.text,styles.menutext]}>Profile</Text>
                            <Text style={[styles.text,styles.menutext]}>Pages</Text>
                            <Text style={[styles.text,styles.menutext]}>Contact</Text>
                            <Text style={[styles.text,styles.menutext]}>Pages</Text>
                            <TouchableOpacity onPress={this.closeMenu.bind(this)}>
                                <Text style={[styles.text,styles.menutext]}>Close</Text>
                            </TouchableOpacity>
                        </View>
    
                        <Animated.View style={styles.content,{width:width,backgroundColor:'gray',flex:1,alignItems:'center',
                            transform:[{ perspective: 850},
                                       { translateX: this.state.anim_translateX.interpolate({
                                                         inputRange: [0, width],
                                                         outputRange: [width, 0],
                                       })},
                                       { rotateY: this.state.anim_rotateY.interpolate({
                                                         inputRange: [0, 1],
                                                         outputRange: ['0deg', '60deg'],
                                       })},
                                      ]
                            }}>
                            <Text style={{top:100,color:'white',backgroundColor:'gray',fontSize:20}}>
                                Image style=width:width,height:height
                            </Text>
                            <TouchableOpacity onPress={this.showMenu.bind(this)}>
                                <View style={{width:100,height:40,top:200,backgroundColor:'red',alignItems:'center',justifyContent:'center'}}>
                                    <Text style={{fontSize:20,color:'white'}}>Menu</Text>
                                </View>
                            </TouchableOpacity>
                        </Animated.View>
                    </Image>
            )
    
        }
    }
    
    const styles = StyleSheet.create({
        text:{color:'#E0E0E0'},gray:{color:'gray'},green:{color:'green'},red:{color:'red'},
        menutext:{fontSize:20,padding:10},
        container:{width:width,height:height,flex:1,justifyContent:'center',alignItems:'center'},
        menu:{width:width,height:height,flex:1,position:'absolute',left:0,top:0,backgroundColor:'#ff00ff'},
        menulist:{width:200,position:'absolute',right:0,top:100},
    })
    
    AppRegistry.registerComponent('SampleApp', () => MenuScreen);
    

登录后回复