目前仅通过小米真机调试并使用,后续回持续优化该组件适用于其他厂商
1、基本使用
原理同uview组件一样,通过绑定一个布尔变量来控制选择器组件的弹出与收起
<import name="my-picker" src="../../components/my-ui/picker/index.ux"></import>
<template>
<div class="wrapper">
<div class="content-box" @click="openPopup">
<text>打开弹出层</text>
<text>
{{ resultData }}
</text>
</div>
<my-picker
visible="{{show}}"
title="{{title}}"
init-data="{{ininData}}"
options="{{options}}"
@cancel="cancel"
@confirm="confirm"
></my-picker>
</div>
</template>
<script>
export default {
public: {},
private: {
show: false,
title: "弹出层标题",
// 选择器数据源
options: [
{ label: "中国", value: 1 },
{ label: "美国", value: 2 },
{ label: "日本", value: 3 },
],
// 初始值,设置弹出层打开时选择器默认选中的值
ininData: {
label: "中国",
value: 1
},
resultData: null
},
async onInit() {
},
openPopup() {
this.show = true
},
// 弹出层确定
confirm(eve) {
this.resultData = eve.detail
this.show = false
},
// 弹出层关闭
cancel() {
this.show = false
}
}
</script>
<style lang="less">
@import '../../assets/styles/style.less';
.wrapper {
flex-direction: column;
padding-top: 120px;
}
.content-box {
flex-direction: column;
text {
margin-bottom: 50px;
}
}
</style>
picker 组件
该组件中的 mask 组件 及 style样式中引入的 base.less 文件皆为apex组件的源码并未做任何改动。
<import name="my-mask" src="../mask/index"></import>
<template>
<div class="picker {{isAnimationEnd?'hide':'show'}}">
<my-mask id="mask" onclick="maskHandle"></my-mask>
<div class="popup popup-bottom {{animationClass()}}">
<div class="popup-header">
<div class="cancel-btn" @click="cancelBtnHandle">
<text>取消</text>
</div>
<div class="title">
<text class="txt">{{ title }}</text>
</div>
<div class="comfirm-btn" @click="confirmHandle">
<text>确定</text>
</div>
</div>
<div class="box">
<div class="picker-mask"></div>
<div class="picker-indicator"></div>
<div class="list-box">
<list
id="list"
class="opt-list"
@scroll="scrollHandle"
@scrollend="scrollendHandle"
>
<list-item type="opt-item">
<div class="opt-item"></div>
</list-item>
<list-item type="opt-item" for="{{options}}">
<div class="opt-item">
<text>{{ $item.label }}</text>
</div>
</list-item>
<list-item type="opt-item">
<div class="opt-item"></div>
</list-item>
</list>
</div>
</div>
<slot></slot>
</div>
</div>
</template>
<script>
export default {
props: {
// 初始值
initData: {
type: Object,
},
// 数据源
options: {
type: Array,
default: []
},
// 弹窗标题
title: {
default: ""
},
visible: {
type: Boolean,
default: false
},
// 点击蒙层是否允许关闭
maskClosable: {
type: Boolean,
default: false
},
// 是否显示背景蒙层
mask: {
type: Boolean,
default: true
},
},
data() {
return {
isAnimationEnd: true,
isVisible: false,
canScroll: false,
itemHeight: 88, //每个item高度,这里没有用于style设置高度,仅作记录
selectRowInfo: {},
listScrollY: 0,
defaultScroll: 0, // 默认滚动数据保存,用户本地开发工具上调试时,累加了第一次设置默认数据滚动长度
}
},
onInit() {
this.$watch('visible', 'visibleWatch')
},
visibleWatch(newV, oldV) {
if (newV) {
this.isAnimationEnd = false
this.setInitSelect()
this.toggleMask("show")
} else {
this.isAnimationEnd = true
this.toggleMask("hide")
}
this.isVisible = newV
},
setInitSelect() {
let index = this.options.findIndex((e) => e.value == this.initData.value)
if (index > -1) {
this.selectRowInfo = this.options[index]
this.$element('list').scrollTo({ index: index })
// 计算初始滚动距离:行数据高度(88) * 初始默认数据所在数组位置(index)
this.listScrollY = this.itemHeight * index
this.defaultScroll = this.itemHeight * index
}
},
// 内容弹出动画
animationClass() {
if (this.isVisible) {
return "translate-bottom-to-center";
} else {
return "translate-center-to-bottom";
}
},
maskHandle() {
// 判断点击遮罩层是否允许关闭
if (this.maskClosable) {
this.$emit("cancel");
}
},
// 弹窗内取消按钮
cancelBtnHandle() {
this.$emit("cancel");
},
// 弹窗内确认按钮
confirmHandle() {
this.$emit("confirm", this.selectRowInfo)
},
toggleMask(fn) {
if (this.mask) {
this.$child("mask")[fn]();
}
},
scrollHandle(e) {
this.$emit('scroll')
// 滚动距离累加
this.listScrollY += e.scrollY
this.canScroll = true
// console.log(`滚动总距离:${this.listScrollY},滚动距离:${e.scrollY}`);
},
scrollendHandle(e) {
let index = 0
let al = this.itemHeight * this.options.length
// 兼容本地开发时list滚动事件累加了设置默认显示位置时累加距离
if (this.listScrollY > al) {
let j = this.listScrollY - this.defaultScroll
index = Math.round(j / this.itemHeight)
} else {
index = this.listScrollY <= 0 ? 0 : Math.round(this.listScrollY / this.itemHeight)
}
// console.log("滚动索引:", index);
if (this.canScroll) {
this.$element('list').scrollTo({ index: index, smooth: true })
this.canScroll = false
this.selectRowInfo = this.options[index]
}
},
}
</script>
<style lang="less">
@import '../styles/base.less';
.picker {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.popup {
background-color: rgb(255, 255, 255);
width: 100%;
border-top-left-radius: 20px;
border-top-right-radius: 20px;
z-index: 1002;
flex-direction: column;
animation-duration: 200ms;
animation-timing-function: linear;
animation-fill-mode: forwards;
&-bottom {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
/* 弹窗顶部操作栏 */
.popup-header {
padding: 0 40px;
height: 84px;
align-items: center;
justify-content: space-between;
.title {
padding: 0 26px;
width: 320px;
height: 100%;
.txt {
width: 320px;
font-size: 32px;
lines: 1;
color: #ff0000;
text-overflow: ellipsis;
text-align: center;
}
}
.cancel-btn {
text {
color: #000000;
}
}
.comfirm-btn {
text {
color: #2b94fc;
}
}
.comfirm-btn,
.cancel-btn {
padding: 15px 30px;
}
}
.box {
height: 440px;
position: relative;
overflow: hidden;
.picker-mask {
background-size: 100% 88px;
position: absolute;
top: 0;
width: 100%;
height: 100%;
background: linear-gradient(
rgba(255, 255, 255, 0.95) 0px,
rgba(255, 255, 255, 0.6) 176px,
rgba(255, 255, 255, 0) 176px,
rgba(255, 255, 255, 0) 264px,
rgba(255, 255, 255, 0.6) 264px,
rgba(255, 255, 255, 0.5) 440px
);
z-index: 3;
background-repeat: no-repeat;
}
.picker-indicator {
height: 88px;
width: 100%;
position: absolute;
top: 176px;
left: 0;
border: 2px solid #e5e5e5;
}
.list-box {
position: absolute;
top: 0;
left: 0;
overflow: visible;
padding: 88px 0;
width: 100%;
height: 440px;
.opt-list {
width: 100%;
height: 264px;
/* height: 100%; */
.opt-item {
width: 100%;
height: 88px;
justify-content: center;
text {
margin: 0 auto;
color: #303133;
}
}
}
}
}
}
</style>
效果图:
缺陷:
由于对于快应用开发不是很熟悉,所以造成该组件并未实现类似uview组件中的picker选中器一样,显示除当前选中数据外,上下两边的选项透明度变化,在真机调试上所有选项颜色都是一样的。
非常希望有大佬可以提供解决思路。