快应用-选择器组件 picker

        目前仅通过小米真机调试并使用,后续回持续优化该组件适用于其他厂商

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选中器一样,显示除当前选中数据外,上下两边的选项透明度变化,在真机调试上所有选项颜色都是一样的。

        非常希望有大佬可以提供解决思路。

参考资料:

1、Apex三方组件--其中有借鉴和使用其中的样式代码及mask组件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值