ReactNative ActionSheet的封装

最近要用到一个选择菜单,由于nuke自带的样式不符合项目的统一样式规范,所以自己抽了一个出来,关键代码是动态向body插入子元素以及transition效果的实现。


IMG_3826.PNG

调用示例:

const data = {
    onSale:'出售中',
    inventory:'仓库中',
    all:'全部'
}
ActionSheet.select(data).then(result=>{
      //result:{key:'onSale',value:'出售中'}
})

1-16优化,选项过多超过一屏后显示滚动列表,高度响应qap安卓键盘呼出关闭
组件实现:

/**
 * Actionsheet
 * 下拉动作菜单
 * @author lxy
 */
import {createElement, PureComponent, render, unmountComponentAtNode} from 'rax';
import {View, Text, TouchableHighlight, Dimensions,ScrollView} from 'nuke';
import style from './style.less'
import QN from 'QAP-SDK'
import doTransition from "$util/PyTransition";

let {height, width} = Dimensions.get(`screen`)//获取屏幕宽高
let pureHeight = height / (width / 750) - 200 / (width / 750) //根据比例计算屏幕真实高度
let keyBoardHeight = 0 //键盘高度
if (!QN.os.ios) {
  QN.on('Global.KeyboardWillShow', ({height}) => {
    keyBoardHeight = height
    Actionsheet.updateHeight() //重新计算组件高度
  })
  QN.on('Global.KeyboardWillHide', () => {
    keyBoardHeight = 0
    Actionsheet.updateHeight()
  })
}

export default class Actionsheet extends PureComponent {
  /**
   *
   * @param data eg:{onSale:`出售中`,out:`已售完`}
   * @returns {Promise}
   */
  static select(data) {
    if (!SelectDown.isExist) {
      Actionsheet.isExist = true;//单例模式,同一页面只允许存在一个实例
      Actionsheet.dom = document.createElement('div');//页面渲染节点
      document.body.appendChild(Actionsheet.dom);//将dom节点插入document
      render('', Actionsheet.dom);//渲染组件到dom节点中
      return new Promise((resolve, reject) => {
        Actionsheet.promise = {resolve, reject}//回调promise
      })
    }
  }

  componentDidMount() {
    const option = {
      timingFunction: 'ease-in-out',
      delay: 0,
      duration: 200
    }
    //初始化动画效果,遮罩层由透明变为0.5,body区域内容从最底部上滑
    doTransition(this.refs.side, {transform: `translateY(${-Actionsheet.bottom - 10})`}, option)//10 marginBottom
    doTransition(this.refs.mask, {backgroundColor: 'rgba(0,0,0,0.5)'})
  }
  constructor(props) {
    let {data} = props
    super(props);
    let map = new Map();
    for (let k of Object.keys(data)) {//存储选择数据
      map.set(k, data[k]);
    }
    this.state = {
      map,
      showSide: false,
      remainHeight:pureHeight-keyBoardHeight,
      allHeight:(map.size + 1) * 114 + 16,////计算组件高度,(数据长度+取消)*114+取消marginTop
    }
   Actionsheet.instance=this
    const {remainHeight,allHeight} = this.state
    Actionsheet.bottom = remainHeight>allHeight?allHeight:remainHeight
  }
  destroy() {
    unmountComponentAtNode(SelectDown.dom)//销毁react实例
    document.body.removeChild(SelectDown.dom)//移除dom节点
    Actionsheet.isExist = false//销毁组件
  }

  chooseItem({key, value}) {
    Actionsheet.promise.resolve({key, value})//处理选择事件
    this.destroy()
  }
//渲染选项列表
  renderBody = () => {
    let self = this;
    let list = []
    let i = 0;
    for (let [key, values] of this.state.map.entries()) {
      let ownStyle;
      if (i == 0) {//处理首尾选项边框
        ownStyle = styles.topBorder
      }
      if (i == this.state.map.size - 1) {
        ownStyle = styles.bottomBorder
      }
      list.push(
        
          
            {values}
          
        
      )
      i++;
    }
    const {remainHeight,allHeight} = this.state
    //屏幕剩余高度大于选项高度,显示选项实际高度,否则显示屏幕剩余高度
    const height = remainHeight>allHeight?allHeight:remainHeight
    return (
      
        
          {list}
        
        
          取消
        
      
    )
  }

  render() {
    return (
      
        {this.renderBody()}
      
    )
  }
}
Actionsheet.isExist = false
Actionsheet.updateHeight=function(){ //更新屏幕剩余高度
  Actionsheet.instance&& Actionsheet.instance.setState({
    remainHeight: pureHeight-keyBoardHeight
  })

}
const styles = {
  topBorder: {
    borderTopRightRadius: '10rem',
    borderTopLeftRadius: '10rem',
    height:'104rem'
  },
  bottomBorder: {
    borderBottomRightRadius: '10rem',
    borderBottomLeftRadius: '10rem',
    height:'104rem'
  },
  border: {
    borderTopWidth: '1rem',
    borderTopColor: '#DCDEE3'
  }
}

style.less

.container{
  width: 750rem;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  background-color: rgba(0,0,0,0);
  position: fixed;
  bottom: 0;
}
.bottom{
  width: 750rem;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  position: fixed;
}
.cancle{
  margin-top: 16rem;
  width: 710rem;
  justify-content: center;
  align-items: center;
  height: 114rem;
  background-color: #fff;
  border-radius: 10rem;
}
.content{
  width: 710rem;
  justify-content: center;
  align-items: center;
  height: 114rem;
  background-color: #fff;
}
.list{
  width: 710rem;
  background-color: #fff;
  border-radius: 10rem;
  justify-content: flex-start;
  align-items: center;
  padding-top: 10rem;
  padding-bottom: 10rem;
}
.text{
  font-family: MicrosoftYaHei;
  width: 710rem;
  font-size: 32rem;
  color: #333333;
  text-align: center;
}
.textCancle{
  font-family: MicrosoftYaHei;
  width: 710rem;
  font-size: 32rem;
  color: #F54531;
  text-align: center;
}

你可能感兴趣的:(ReactNative ActionSheet的封装)