React Native 扫码组件react-native-camera与自定义UI界面动画套装

扫码这个功能在普通项目中用到的案例还是比较多的,签到、加好友、WebView跳转等等

针对扫码这个功能,react-native的前辈们已经封装好了可以跨平台的组件
react-native-camera
亲测是非常好用的,虽然有很多提Issue,但是最新的版本还是比较稳定的

给大家说一下我在用的时候遇到的一个自己挖的坑
因为我的项目有不止一个界面用到了这个功能,所以我在不同的导航器中用到了同一个界面,界面的key值也是相同的,导航倒是没有出现错乱,但是在打开Camera的时候会出现白屏,相机始终不能渲染出来,当然对Key值进行区分,这个问题也就解决了,刚开始以为是组件的问题,去官网一顿找相似的issue

好了,忘记这个issue继续说,UI,功能测试没问题之后发现这个customer界面是真的没做什么优化,真的很丑,所以,拿来微信的扫一扫功能来参考一下

React Native 扫码组件react-native-camera与自定义UI界面动画套装_第1张图片

经过对一些功能的筛选,我们对一些细节进行了模仿

先看一下效果吧

说一下具体进行的模仿模块

1、四周阴影和中间全透明的效果实现
实现这个效果我走了点弯路,实现方法也是比较麻烦,先给大家分享一下,我是将界面分成了5部分(View),中间框是一部分,四周是一部份

2、四个绿色的边框角
这四个边框角的原型是两个View,每个View是一条线

<View style={{ position: 'absolute', left: 0, top: 0 }}>
  <View style={{ height: 2, width: px2dp(60), backgroundColor: '#37b44a' }} />
  <View style={{ height: px2dp(60), width: 2, backgroundColor: '#37b44a' }} />
View>

其实就是一横一竖,宽度和高度分别是2,这个是左上角的,右上角的边框是通过左上角的旋转角度而来

<View style={{ position: 'absolute', right: 1, top: -1, transform: [{rotate: '90deg'}] }}>
  <View style={{ height: 2, width: px2dp(60), backgroundColor: '#37b44a' }} />
  <View style={{ height: px2dp(60), width: 2, backgroundColor: '#37b44a' }} />
View>

其他的处理方法类似

3、扫描线
扫描线就是一条线,然后赋予动画,

<Animated.View style={{ width: px2dp(500),
  height: px2dp(2),
  backgroundColor: '#37b44a',
  transform: [{
    translateY: this.state.top.interpolate({
      inputRange: [0, 1],
      outputRange: [0, px2dp(500)]
    })
  }]}} 
/>

而且这个动画是由setInterval定时器进行驱动的

setInterval(function () {
  Animated.timing(that.state.top, {
    toValue: 1,
    duration: 2200
  }).start(() => that.setState({ top: new Animated.Value(0) }))
}, 2260)

接下来我把项目代码脱敏之后给大家共享出来,供参考

class ScannerCodeScreen extends Component {
  static navigationOptions = ({ navigation }) => {
    return ({
      title: '二维码扫描',
      ...ApplicationStyles.blackHeaderStyle,
      headerLeft: <HeaderLeft navigation={navigation} color={Colors.C8} />
    })
  }

  constructor (props) {
    super(props)
    this.state = {
      firstLoad: true,
      top: new Animated.Value(0),
      isSigned: false
    }
    /** object */
    this.resultUrl = null
    /** function */
    this.onBarCodeRead = this.onBarCodeRead.bind(this)
    this.interval = null
  }

  componentDidMount () {
  }

  componentWillReceiveProps (nextProps) {
    if (!this.props.isFocused && nextProps.isFocused) {
      this.onFocus()
    }
    if (this.props.isFocused && !nextProps.isFocused) {
      this.onBlur()
    }
  }

  onFocus () {
    this.props.toggleTabBarAction(false)
    /** 获取视频、现场首页信息 */
    if (this.state.firstLoad) {
      this.setState({ firstLoad: false })
    }
    /** 定时器触发动画 */
    const that = this
    this.interval =
    setInterval(function () {
      Animated.timing(that.state.top, {
        toValue: 1,
        duration: 2200
      }).start(() => that.setState({ top: new Animated.Value(0) }))
    }, 2260)
  }

  onBlur () {
    /** 清除定时任务 */
    clearInterval(this.interval)
  }

  getQueryString (url, name) {
    var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
    var r = url.substr(1).match(reg)
    if (r != null) {
      return unescape(r[2])
    }
    return null
  }

  onBarCodeRead (e) {
    /** 避免多次提交 */
    console.log(e)
    if (!this.resultUrl || this.resultUrl !== e.data) {
      if (e.data.indexOf('http://www.baidu.com/') !== 0) {
        this.props.toastAction('请扫描正确的二维码')
      } else {
        this.resultUrl = e.data
        const id = this.getQueryString(e.data, 'id')
        this.props.navigation.navigate('applicationMessage', { receiver: id })
        this.resultUrl = null
      }
    }
  }

  barcodeReceived (e) {
    console.log('Barcode: ' + e.data)
    console.log('Type: ' + e.type)
  }

  render () {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }} >
        <View>
          <Camera
            ref={(cam) => {
              this.camera = cam
            }}
            onBarCodeRead={this.onBarCodeRead.bind(this)}
            style={{ width, height, marginTop: 40 }}
            aspect={Camera.constants.Aspect.fill}
          />
          <View style={{ position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 }}>
            <View style={{ height: (height - px2dp(600)) / 2, backgroundColor: 'black', opacity: 0.5 }} />
            <View style={{ flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center' }}>
              <View style={{ height: px2dp(500), width: (width - px2dp(500)) / 2, backgroundColor: 'black', opacity: 0.5 }} />
              <View style={{ height: px2dp(500), width: px2dp(500) }} >
                <View style={{ position: 'absolute', left: 0, top: 0 }}>
                  <View style={{ height: 2, width: px2dp(60), backgroundColor: '#37b44a' }} />
                  <View style={{ height: px2dp(60), width: 2, backgroundColor: '#37b44a' }} />
                View>
                <View style={{ position: 'absolute', right: 1, top: -1, transform: [{rotate: '90deg'}] }}>
                  <View style={{ height: 2, width: px2dp(60), backgroundColor: '#37b44a' }} />
                  <View style={{ height: px2dp(60), width: 2, backgroundColor: '#37b44a' }} />
                View>
                <View style={{ position: 'absolute', left: 1, bottom: -1, transform: [{rotateZ: '-90deg'}] }}>
                  <View style={{ height: 2, width: px2dp(60), backgroundColor: '#37b44a' }} />
                  <View style={{ height: px2dp(60), width: 2, backgroundColor: '#37b44a' }} />
                View>
                <View style={{ position: 'absolute', right: 0, bottom: 0, transform: [{rotateZ: '180deg'}] }}>
                  <View style={{ height: 2, width: px2dp(60), backgroundColor: '#37b44a' }} />
                  <View style={{ height: px2dp(60), width: 2, backgroundColor: '#37b44a' }} />
                View>
                <Animated.View style={{ width: px2dp(500),
                  height: px2dp(2),
                  backgroundColor: '#37b44a',
                  transform: [{
                    translateY: this.state.top.interpolate({
                      inputRange: [0, 1],
                      outputRange: [0, px2dp(500)]
                    })
                  }]}} />
              View>
              <View style={{ height: px2dp(500), width: (width - px2dp(500)) / 2, backgroundColor: 'black', opacity: 0.5 }} />
            View>
            <View style={{ flex: 1, backgroundColor: 'black', opacity: 0.5, alignItems: 'center' }} >
              <Text style={{ fontSize: FontSize(28), color: Colors.C5, marginTop: px2dp(40) }}>将二维码放入框内,即可自动扫描Text>
            View>
          View>
        View>
      View>
    )
  }
}

你可能感兴趣的:(React,Native)