最近要用到一個(gè)選擇菜單,由于nuke自帶的樣式不符合項(xiàng)目的統(tǒng)一樣式規(guī)范,所以自己抽了一個(gè)出來,關(guān)鍵代碼是動(dòng)態(tài)向body插入子元素以及transition效果的實(shí)現(xiàn)。
IMG_3826.PNG
調(diào)用示例:
const data = {
onSale:'出售中',
inventory:'倉(cāng)庫(kù)中',
all:'全部'
}
ActionSheet.select(data).then(result=>{
//result:{key:'onSale',value:'出售中'}
})
1-16優(yōu)化,選項(xiàng)過多超過一屏后顯示滾動(dòng)列表,高度響應(yīng)qap安卓鍵盤呼出關(guān)閉
組件實(shí)現(xiàn):
/**
* Actionsheet
* 下拉動(dòng)作菜單
* @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) //根據(jù)比例計(jì)算屏幕真實(shí)高度
let keyBoardHeight = 0 //鍵盤高度
if (!QN.os.ios) {
QN.on('Global.KeyboardWillShow', ({height}) => {
keyBoardHeight = height
Actionsheet.updateHeight() //重新計(jì)算組件高度
})
QN.on('Global.KeyboardWillHide', () => {
keyBoardHeight = 0
Actionsheet.updateHeight()
})
}
export default class Actionsheet extends PureComponent {
/**
*
* @param data eg:{onSale:`出售中`,out:`已售完`}
* @returns {Promise<any>}
*/
static select(data) {
if (!SelectDown.isExist) {
Actionsheet.isExist = true;//單例模式,同一頁面只允許存在一個(gè)實(shí)例
Actionsheet.dom = document.createElement('div');//頁面渲染節(jié)點(diǎn)
document.body.appendChild(Actionsheet.dom);//將dom節(jié)點(diǎn)插入document
render('<SelectDown data={data}/>', Actionsheet.dom);//渲染組件到dom節(jié)點(diǎn)中
return new Promise((resolve, reject) => {
Actionsheet.promise = {resolve, reject}//回調(diào)promise
})
}
}
componentDidMount() {
const option = {
timingFunction: 'ease-in-out',
delay: 0,
duration: 200
}
//初始化動(dòng)畫效果,遮罩層由透明變?yōu)?.5,body區(qū)域內(nèi)容從最底部上滑
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)) {//存儲(chǔ)選擇數(shù)據(jù)
map.set(k, data[k]);
}
this.state = {
map,
showSide: false,
remainHeight:pureHeight-keyBoardHeight,
allHeight:(map.size + 1) * 114 + 16,////計(jì)算組件高度,(數(shù)據(jù)長(zhǎng)度+取消)*114+取消marginTop
}
Actionsheet.instance=this
const {remainHeight,allHeight} = this.state
Actionsheet.bottom = remainHeight>allHeight?allHeight:remainHeight
}
destroy() {
unmountComponentAtNode(SelectDown.dom)//銷毀react實(shí)例
document.body.removeChild(SelectDown.dom)//移除dom節(jié)點(diǎn)
Actionsheet.isExist = false//銷毀組件
}
chooseItem({key, value}) {
Actionsheet.promise.resolve({key, value})//處理選擇事件
this.destroy()
}
//渲染選項(xiàng)列表
renderBody = () => {
let self = this;
let list = []
let i = 0;
for (let [key, values] of this.state.map.entries()) {
let ownStyle;
if (i == 0) {//處理首尾選項(xiàng)邊框
ownStyle = styles.topBorder
}
if (i == this.state.map.size - 1) {
ownStyle = styles.bottomBorder
}
list.push(
<TouchableHighlight key={key} style={[style.content, ownStyle, i != 0 ? styles.border : '']}
onPress={this.chooseItem.bind(self, {key, value: values})}>
<Text style={style.text}>
{values}
</Text>
</TouchableHighlight>
)
i++;
}
const {remainHeight,allHeight} = this.state
//屏幕剩余高度大于選項(xiàng)高度,顯示選項(xiàng)實(shí)際高度,否則顯示屏幕剩余高度
const height = remainHeight>allHeight?allHeight:remainHeight
return (
<View ref="side" style={[style.bottom, {bottom: -Actionsheet.bottom,height:height}]}>
<ScrollView style={style.list}>
{list}
</ScrollView>
<TouchableHighlight style={style.cancle} onPress={this.destroy}>
<Text style={style.textCancle}>取消</Text>
</TouchableHighlight>
</View>
)
}
render() {
return (
<View ref='mask' onClick={this.destroy} style={[style.container, {height: height}]}>
{this.renderBody()}
</View>
)
}
}
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;
}