vue自制PC简易版picker

539 阅读1分钟

效果图如下:

代码

<template>
  <div class="contain" ref="contain" @mousedown.prevent="mousedown">
    <ul class="picker-column" :style="`transform: translate3d(0px, ${containY}px, 0px);`">
      <li
        :class="['picker-column-item',index==selectIndex?'column-item_select':'']"
        :key="index"
        v-for="(item,index) in list"
        :style="`height:${itemHeight}px`"
        @click="selectItem(index)"
      >{{item}}</li>
    </ul>
    <div class="picker-mask" style="background-size: 100% 110px;"></div>
  </div>
</template>

<script>
export default {
  mounted() {
    this.$nextTick(() => {
      this.init();
    });
  },
  data() {
    return {
      currentY: 0,
      containDom: null,
      selectIndex: 0,
      itemHeight: 44,
      containY: 110,
      list: ["杭州", "北京", "苏州", "南京", "上海",'天津']
    };
  },
  watch: {
    selectIndex(val){
      console.log(val)
    }
  },
  methods: {
    //项目初始化
    init() {
      let dom = this.$refs["contain"];
      let cantainHeight = dom.offsetHeight;//整个窗口的高度
      this.containDom = dom;//记录一下
      //初始top
      this.currentY = this.containY =
        cantainHeight / 2 -
        this.itemHeight / 2 -
        this.selectIndex * this.itemHeight;
    },
    mousedown(e) {
      //节流阀
      let status = true;
      //获取点击元素
      const startDom = e.target;
      // 刚点击屏幕的坐标
      let startPageY = e.clientY;
      document.onmousemove = em => {
        if (status) {
          //移动中的坐标
          let movePageY = em.clientY;
          //   current 拖拽变化
          let current = movePageY - startPageY;
          //   y轴变化最大最小值
          let maxY =
            this.containDom.offsetHeight / 2 -
            this.itemHeight / 2 +
            this.itemHeight;
          let minY =
            this.containDom.offsetHeight / 2 -
            this.itemHeight / 2 -
            this.list.length * this.itemHeight;
          //   滑动距离
          let containY = this.currentY + current;
          //控制距离
          if (containY < minY) {
            containY = minY + this.itemHeight;
          } else if (containY > maxY) {
            containY = maxY - this.itemHeight;
          } else {
            this.containY = containY;
          }
        }
      };
      document.onmouseup = e => {
        if(status){
        status = false;
        let indexObj = this.getIndex(
          this.itemHeight,
          this.containY - this.currentY
        );
        if (indexObj.status) {
          this.selectIndex =this.selectIndex + indexObj.index;
        }else{
          this.selectIndex =this.selectIndex - indexObj.index;
        }
        //索引控制 选中控制
        if (this.selectIndex < 0) {
          this.selectIndex = 0;
        } else if (this.selectIndex > this.list.length) {
          this.selectIndex = this.list.length;
        }
        //弹起记录当前y轴
        this.currentY = this.containY =
          this.containDom.offsetHeight / 2 -
          this.itemHeight / 2 -
          this.selectIndex * this.itemHeight;
        document.onmousemove = null;
        document.onmouseup = null;
        }
      };
      e.stopPropagation();
    },
    //点击选中
    selectItem(index) {
      this.selectIndex = index;
      this.containY =
        this.containDom.offsetHeight / 2 -
        this.itemHeight / 2 -
        this.selectIndex * this.itemHeight;
    },
    // 计算索引
    getIndex(height, range) {
      let num = parseInt(range); //划整数
      let status = false; //下划状态
      let index = 0;//滑动了多少方位
      if (num && num < 0) {
        //上划
        status = true;
      }
      if (Math.abs(num) < parseInt(height / 2)||Math.abs(num) ==0) {
        //少于一般或者没动
        index = 0;
      }else {
        index = Math.floor(Math.abs(num) / height);
      }
      return {
        status,
        index
      };
    }
  }
};
</script>

<style>
.contain {
  position: relative;
  width: 336px;
  height: 264px;
  background: #eee;
  overflow: hidden;
  /* padding-top: 110px; */
  box-sizing: border-box;
  margin: 20px;
}
.picker-column {
  font-size: 16px;
  cursor: grab;
  transition: all 0.4s;
}
.picker-column-item {
  display: -webkit-box;
  display: -webkit-flex;
  display: flex;
  -webkit-box-align: center;
  -webkit-align-items: center;
  align-items: center;
  -webkit-box-pack: center;
  -webkit-justify-content: center;
  justify-content: center;
  padding: 0 4px;
  color: #000;
}
.picker-mask {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  background-image: -webkit-linear-gradient(
      top,
      hsla(0, 0%, 100%, 0.9),
      hsla(0, 0%, 100%, 0.4)
    ),
    -webkit-linear-gradient(bottom, hsla(0, 0%, 100%, 0.9), hsla(0, 0%, 100%, 0.4));
  background-image: linear-gradient(
      180deg,
      hsla(0, 0%, 100%, 0.9),
      hsla(0, 0%, 100%, 0.4)
    ),
    linear-gradient(0deg, hsla(0, 0%, 100%, 0.9), hsla(0, 0%, 100%, 0.4));
  background-repeat: no-repeat;
  background-position: top, bottom;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  pointer-events: none;
}
ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}
</style>