vue 移動端 日期滑動選擇器

微信截圖_20211130113621.png

手拉手 cv教學(xué) 復(fù)制就能學(xué)會

date.vue 父組件
datepicker.vue 兒子組件
picker.vue 孫子組件
item.vue 太孫子組件


微信圖片_20211130113945.png

date.vue 父組件

<template>
  <div>
    <button @click.prevent="opendate">open</button>
    <p>日期:{{ datetime }}</p>
    <Date
      v-model="datetime"
      :fill0="true"
      placeholder="請選擇"
      ref="datepicker"
      :showUnit="true"
    ></Date>
  </div>
</template>

<script>
import Date from "./datecomponents/datepicker.vue";
export default {
  data() {
    return {
      datetime: "2021-11-11",
    };
  },
  components: {
    Date,
  },
  methods: {
    opendate() {
      this.$refs.datepicker._init();
    },
  },
};
</script>

<style>
</style>

datepicker.vue 兒子組件

<template>
  <div @click="_init">
    <Picker
      :visible.sync="visible"
      :data="data"
      v-model="valueNew"
      :change="_change"
      :cancelEvent="_cancelEvent"
      :confirmEvent="_confirmEvent"
      :cancelText="cancelText"
      :confirmText="confirmText"
      :title="title"
      :visibleCount="visibleCount"
      @input="_input"
    ></Picker>
  </div>
</template>
<script>
import Picker from "./picker.vue";
export default {
  name: "datePicker",
  data() {
    return {
      visible: false,
      valueCache: [], //改變后臨時保存的值
      data: [],
    };
  },
  props: {
    visibles: Boolean,
    min: String,
    max: String,
    type: {
      type: String,
      default: "ymd",
    },
    value: String,
    placeholder: String, //模擬input效果
    cancelText: String,
    cancelEvent: Function,
    confirmText: String,
    confirmEvent: Function,
    change: Function,
    title: String,
    visibleCount: Number,
    showUnit: {
      type: Boolean,
      default: false,
    },
    fill0: {
      //小于10前面補0
      type: Boolean,
      default: false,
    },
  },
  components: { Picker },
  methods: {
    _init() {
      this.visible = true;
    },
    _input(start, end) {
      this.$emit("timer", start, end);
      // console.log(staet, end)
    },
    _change(value, index) {
      const valueNum = parseInt(value); //帶單位時样傍,這個value會是01月
      if (index == 0) {
        this.valueCache[0] = valueNum;
      } else if (index == 1 && this.type.substr(2, 1) == "d") {
        //只在月份改變時做聯(lián)動
        let day = new Date(this.valueCache[0], valueNum, 0);
        let array = this._forArray(1, day.getDate(), "日");
        this.data.splice(2, 1, array);
      }
      this.cancelEvent ? this.cancelEvent(valueNum) : "";
    },
    _cancelEvent(value) {
      this.cancelEvent ? this.cancelEvent(this._format(value)) : "";
    },
    _confirmEvent(value) {
      this.$emit("input", this._format(value));
      this.confirmEvent ? this.confirmEvent(this._format(value)) : "";
    },
    _setDate() {
      this.data.splice(0, this.data.length);
      let min = new Date(this.min);
      let max = new Date(this.max);
      let value = new Date(this.value);
      let cur = new Date();
      let yearMin, yearMax;
      //無起始和結(jié)束時間蔬芥,顯示前后10年
      if (isNaN(min)) {
        yearMin = cur.getFullYear() - 10;
      } else {
        yearMin = min.getFullYear();
      }
      if (isNaN(max)) {
        yearMax = cur.getFullYear() + 10;
      } else {
        yearMax = max.getFullYear();
      }
      //如果沒有初始值仅炊,則設(shè)置為當(dāng)前時間
      if (value == "Invalid Date") {
        value = cur;
      }
      //取當(dāng)月天數(shù)
      //new Date(2018,4,1)輸出2018-5-1,月份從0開始
      //new Date(2018,4,0)輸出2018-4-30妈橄,0表示前一天威始,即上月最后一天
      let day = new Date(
        value.getFullYear(),
        value.getMonth() + 1,
        0
      ).getDate();
      this.data.push(this._forArray(yearMin, yearMax, "年"));
      let type = this.type;
      //type第2位為m時顯示月份
      if (type.substr(1, 1) == "m") {
        this.data.push(this._forArray(1, 12, "月"));
      }
      if (type.substr(2, 1) == "d") {
        this.data.push(this._forArray(1, day, "日"));
      }
      if (type.substr(3, 1) == "h") {
        this.data.push(this._forArray(0, 23, "時"));
      }
      if (type.substr(4, 1) == "m") {
        this.data.push(this._forArray(0, 59, "分"));
      }
      if (type.substr(5, 1) == "s") {
        this.data.push(this._forArray(0, 59, "秒"));
      }
    },
    _forArray(min, max, unit) {
      let array = [];
      let v;
      for (let i = min; i <= max; i++) {
        //前面補0
        v = i.toString();
        if (this.fill0 && i < 10) {
          v = "0" + i;
        }
        if (this.showUnit) {
          v = v + unit;
        }
        array.push(v.toString());
      }
      return { value: array };
    },
    _format(value) {
      //格式化時間
      let day, day2;
      if (this.showUnit) {
        day = value.toString().replace(/,/g, "");
        day2 = value.toString().replace("年,", "-");
        day2 = day2.toString().replace("月,", "-");
        day2 = day2.toString().replace("日,", " ");
        day2 = day2.toString().replace("時,", ":");
        day2 = day2.toString().replace("分,", ":");
        day2 = day2.toString().replace("秒", "");
      } else {
        day = value.toString().replace(",", "-");
        day = day.toString().replace(",", "-");
        day = day.toString().replace(",", " ");
        day = day.toString().replace(",", ":");
        day = day.toString().replace(",", ":");
        day2 = day;
      }
      //當(dāng)選擇的時候超出最大或最小值時鼻吮,做限制
      if (this.min != "" || this.max != "") {
        const minMax = new Date(day2);
        const min = new Date(this.min);
        const max = new Date(this.max);
        if (min > minMax) {
          day = this.min;
        }
        if (max < minMax) {
          day = this.max;
        }
        //這里也要做格式化轉(zhuǎn)換,為簡化代碼先跳過
      }
      return day;
    },
  },
  computed: {
    valueNew: {
      get() {
        let v = new Date(this.value);
        let array = [];
        if (this.value == "") {
          v = new Date();
        }
        this.valueCache[0] = v.getFullYear();
        this.valueCache[1] = v.getMonth();
        if (this.showUnit) {
          array = [
            v.getFullYear().toString() + "年",
            (v.getMonth() + 1).toString() + "月",
            v.getDate().toString() + "日",
            v.getHours().toString() + "時",
            v.getMinutes().toString() + "分",
            v.getSeconds().toString() + "秒",
          ];
        } else {
          array = [
            v.getFullYear().toString(),
            (v.getMonth() + 1).toString(),
            v.getDate().toString(),
            v.getHours().toString(),
            v.getMinutes().toString(),
            v.getSeconds().toString(),
          ];
        }
        //按顯示格式裁剪數(shù)組
        return array.splice(0, this.type.length);
      },
      set() {},
    },
  },
  mounted() {
    this._setDate();
  },
  filters: {},
};
</script>

picker.vue 孫子組件

<template>
  <div class="picker">
    <transition name="fade">
      <div class="mask" v-show="visible" @click="_maskClick"></div>
    </transition>
    <transition name="slide">
      <div class="picker-content" v-show="visible" ref="content">
        <div class="picker-control">
          <a
            href="javascript:;"
            class="picker-cancel"
            v-text="cancelText"
            @click="_cancelClick"
          ></a>
          <span v-text="title" v-if="title" class="picker-title"></span>
          <a
            href="javascript:;"
            class="picker-confirm"
            v-text="confirmText"
            @click="_confirmClick"
            >確定</a
          >
        </div>
        <!-- <div class="picker-tabs">
          <div
            class="tabs-item"
            v-for="(item, index) in tabsList"
            :key="item.id"
          >
            <span
              :class="tabsind == index ? 'active-span' : 'item-span'"
              @click="clickItem(index)"
              >{{ item.text }}</span
            >
            <div :class="tabsind == index ? 'active-div' : 'item-div'"></div>
          </div>
        </div> -->
        <div
          class="picker-group"
          :style="{ height: visibleCount * liHeight + 'px' }"
        >
          <div class="picker-border"></div>
          <pickerItem
            v-for="(item, index) in data"
            :data="item.value"
            :key="index"
            :index="index"
            :height="liHeight"
            :change="_change"
            :value="typeof value == 'string' ? value : value[index]"
            ref="item"
          ></pickerItem>
        </div>
      </div>
    </transition>
  </div>
</template>
<script>
import pickerItem from "./item.vue";
export default {
  name: "picker",
  data() {
    return {
      liHeight: 0,
      newValue: this.value,
      tabsind: 0,
      tabsList: [
        {
          id: 0,
          text: "開始時間",
        },
        {
          id: 1,
          text: "結(jié)束時間",
        },
      ],
      startTime: [],
      endTime: [],
    };
  },
  watch: {
    visible(v) {
      //初始時數(shù)據(jù)為空筹裕,在顯示時再計算位置
      if (v && this.liHeight == 0) {
        this._getDisplayHeight();
      }
    },
  },
  created() {
    console.log(this.data);
  },
  props: {
    visible: {
      //顯示或隱藏醋闭,通過sync實現(xiàn)雙向綁定
      type: Boolean,
      default: false,
    },
    maskClose: {
      //點閉遮罩層是否關(guān)閉
      type: Boolean,
      default: true,
    },
    cancelText: {
      //取消按鈕文本
      type: String,
      default: "取消",
    },
    cancelEvent: Function,
    confirmText: {
      //確定按鈕文本
      type: String,
      default: "確認",
    },
    confirmEvent: Function,
    change: Function,
    title: {
      type: String,
      default: "自定義日期",
    },
    visibleCount: {
      //顯示的個數(shù)
      type: Number,
      default: 5,
    },
    data: Array,
    value: [String, Array],
  },
  components: { pickerItem },
  methods: {
    clickItem(index) {
      this.tabsind = index;
    },
    _maskClick(e) {
      //點閉遮罩層是否關(guān)閉
      this.maskClose ? this._cancelClick(e) : "";
    },
    _cancelClick(e) {
      //點擊取消,關(guān)閉退出
      //恢復(fù)狀態(tài)
      let item = this.$refs.item;
      for (let i in item) {
        item[i]._moveTo();
      }
      this.$emit("update:visible", false);
      this.cancelEvent ? this.cancelEvent(this.value) : "";
      e.stopPropagation();
    },
    _confirmClick(e) {
      //this._cancelClick();
      this.$emit("update:visible", false);
      this.confirmEvent ? this.confirmEvent(this.newValue) : "";
      if (this.tabsind == 1) {
        this.endTime = this.newValue;
      }
      this.$emit("input", this.startTime, this.endTime);
      e.stopPropagation();
    },
    _change(value, index, bool) {
      //這里修改為點擊確認才更新選中值
      if (typeof this.value == "string") {
        //this.$emit('input', value);
        this.newValue = value;
      } else {
        let newValue = this.newValue.slice(0);
        newValue[index] = value;
        if (this.tabsind == 0) {
          this.startTime = newValue;
        }
        if (this.tabsind == 1) {
          this.endTime = newValue;
        }
        //采用上面方法是不會同步更新的朝卒,因為vue監(jiān)聽的是this.value证逻,
        //沒有監(jiān)聽this.value的子項,所以直接改變子項不會觸發(fā)更新
        //newValue.splice(index, 1, value);//先移除再添加
        //this.$emit('input', newValue);
        this.newValue = newValue;
      }
      //bool=false時是初始時設(shè)置的
      if (bool) {
        this.change ? this.change(value, index) : "";
      }
    },
    _getDisplayHeight() {
      //取隱藏標簽的高
      const obj = this.$refs.content;
      const clone = obj.cloneNode(true);
      clone.style.display = "block";
      clone.style.position = "absolute";
      clone.style.opacity = 0;
      clone.style.top = "-10000px";
      obj.parentNode.appendChild(clone);
      const li = clone.querySelector("li");
      if (li) {
        //this.liHeight = li.offsetHeight;//取到的是整數(shù)
        this.liHeight = parseFloat(window.getComputedStyle(li, null).height); //取到的精確到小數(shù)
      }
      obj.parentNode.removeChild(clone);
    },
  },
  computed: {},
  mounted() {
    this._getDisplayHeight();
    this.tabsind = 0;
  },
  filters: {},
};
</script>

<style scoped lang='less'>
.picker {
  touch-action: none;
  .mask {
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 100;
  }
  .picker-content {
    position: fixed;
    left: 0;
    bottom: 0;
    right: 0;
    background: #fff;
    z-index: 101;
    border-radius: 20px 20px 0px 0px;
  }
  .active-span {
    font-family: PingFangSC-Medium;
    font-size: 30px;
    color: #6522e6;
    font-weight: 500;
    margin-top: 15px;
  }

  .active-div {
    width: 52px;
    height: 6px;
    background: #6522e6;
    border-radius: 3px;
    margin-top: 5px;
  }
  .picker-tabs {
    width: 650px;
    margin: 0 auto;
    height: 80px;
    display: flex;
    justify-content: center;
    border-bottom: 2px solid #e6e8ed;

    .tabs-item {
      width: 40%;
      display: flex;
      flex-direction: column;
      align-items: center;

      .item-span {
        font-family: PingFangSC-Regular;
        font-size: 28px;
        color: #130038;
        font-weight: 400;
        margin-top: 15px;
      }
    }
  }
  /*取消確定按鈕*/
  .picker-control {
    height: 100px;
    background: #f8f8f8;
    border-radius: 20px 20px 0px 0px;
    padding: 0 20px;
    display: flex;
    justify-content: center;
    align-items: center;
    .picker-title {
      display: block;
      flex: 2;
      font-family: PingFangSC-Medium;
      font-size: 32px;
      color: #130038;
      text-align: center;
      font-weight: 500;
    }
    a {
      font-family: PingFangSC-Regular;
      display: block;
      color: #818181;
      font-size: 32px;
      font-weight: 400;
      &:last-child {
        font-family: PingFangSC-Medium;
        text-align: right;
        color: #130038;
      }
    }
  }
  .picker-group {
    display: flex;
    position: relative;
    overflow: hidden;
    .picker-border {
      left: 50%;
      top: 50%;
      margin-left: -326px;
      position: absolute;
      height: 100px;
      border-bottom: 2px solid #e5e5e5;
      border-top: 2px solid #e5e5e5;
      width: 650px;
      box-sizing: border-box;
      transform: translateY(-50%);
    }
  }
}
.picker-item {
  width: 100%;
  position: relative;
  overflow: hidden;
  .picker-mask {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    width: 100%;
    z-index: 3;
    background: linear-gradient(
        180deg,
        rgba(255, 255, 255, 0.95),
        rgba(255, 255, 255, 0.6)
      ),
      linear-gradient(0deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.6));
    background-repeat: no-repeat;
    background-position: top, bottom;
    /*background-size: 100% 102px;*/
    /*兩個線性過度疊在一起抗斤,通過size設(shè)定顯示的高度*/
  }
  li {
    height: 100px;
    line-height: 100px;
    text-align: center;
    overflow: hidden;
    box-sizing: border-box;
    font-family: PingFangSC-Medium;
    font-size: 32px;
    color: #130038;
    font-weight: 500;
    &.disabled {
      font-style: italic;
    }
  }
}
// .fade-enter-active {
//   animation: fadeIn 0.5s;
// }
// .fade-leave-active {
//   animation: fadeOut 0.5s;
// }
// @keyframes fadeIn {
//   0% {
//     opacity: 0;
//   }
//   100% {
//     opacity: 1;
//   }
// }
// @keyframes fadeOut {
//   0% {
//     opacity: 1;
//   }
//   100% {
//     opacity: 0;
//   }
// }
// .slide-enter-active {
//   animation: fadeUp 0.5s;
// }
// .slide-leave-active {
//   animation: fadeDown 0.5s;
// }
// @keyframes fadeUp {
//   0% {
//     opacity: 0.6;
//     transform: translateY(100%);
//   }
//   100% {
//     opacity: 1;
//     transform: translateY(0);
//   }
// }
// @keyframes fadeDown {
//   0% {
//     opacity: 1;
//     transform: translateY(0);
//   }
//   100% {
//     opacity: 0.6;
//     transform: translateY(100%);
//   }
// }
</style>

item.vue 太孫子組件

<template>
  <div
    class="picker-item"
    @touchstart="_onTouchStart"
    @touchmove.prevent="_onTouchMove"
    @touchend="_onTouchEnd"
    @touchcancel="_onTouchEnd"
  >
    <div class="picker-mask" :style="pickerMask"></div>
    <ul class="picker-li" :style="transformStyle">
      <li
        v-for="(item, index) in data"
        v-text="item.name || item"
        :key="index"
        :class="{ disabled: item.disabled }"
      ></li>
    </ul>
  </div>
</template>
<script type="text/ecmascript-6">
export default {
  name: "picker-item",
  data() {
    return {
      startY: 0, //touch時鼠標所有位置
      startOffset: 0, //touch前已移動的距離
      offset: 0, //當(dāng)前移動的距離
    };
  },
  watch: {
    height() {
      //父組件mounted后更新了height的高度囚企,這里將數(shù)據(jù)移動到指定位置
      this._moveTo();
    },
    data() {
      //在聯(lián)動時,數(shù)據(jù)變化了瑞眼,下級還會保持在上一次的移動位置
      this._moveTo();
    },
  },
  props: {
    height: Number, //移動單位的高度
    data: Array,
    change: Function,
    value: String, //選中的值
    index: Number, //當(dāng)前索引龙宏,多個選擇時如聯(lián)動時,指向的是第幾個選擇伤疙,在change時返回去區(qū)別哪項改變了
  },
  components: {},
  methods: {
    _getTouch(event) {
      return event.changedTouches[0] || event.touches[0];
    },
    _getVisibleCount() {
      //取顯示條數(shù)的一半烦衣,因為選中的在中間,顯示條數(shù)為奇數(shù)
      return Math.floor(this.$parent.visibleCount / 2);
    },
    _onTouchStart(event) {
      const touch = this._getTouch(event);
      this.startOffset = this.offset;
      this.startY = touch.clientY;
    },
    _onTouchMove(event) {
      const touch = this._getTouch(event);
      const currentY = touch.clientY;
      const distance = currentY - this.startY;
      this.offset = this.startOffset + distance;
    },
    _onTouchEnd() {
      let index = Math.round(this.offset / this.$parent.liHeight);
      const vc = this._getVisibleCount();
      // console.log("liHeight:" + this.$parent.liHeight);
      // console.log("this.offset:" + this.offset);
      // console.log("index:" + index);
      // index的有效范圍
      const indexMax = vc - this.data.length;
      if (index >= vc) {
        index = 0; // 選擇第一個
      } else if (index < indexMax) {
        // 選擇最后一個
        index = this.data.length - 1; //最后一個
      } else {
        index = vc - index;
      }
      this._setIndex(index, true);
    },
    _setIndex(index, bool) {
      //按顯示5條計算掩浙,選擇第3條時花吟,偏移為0,選擇第1條時厨姚,偏移為li的高*2
      //即偏移距離為(5/2取整-index)*liHeight
      //如果當(dāng)前選中的為disabled狀態(tài)衅澈,則往下選擇,僅在滑動選擇時判斷谬墙,默認填值時不作判斷
      //存在數(shù)據(jù)加載問題今布,有可能初始時數(shù)據(jù)是空的
      if (this.data.length > 0) {
        bool ? (index = this._isDisabled(index, index)) : "";
        this.offset = (this._getVisibleCount() - index) * this.height;
        //回調(diào)
        const value = this.data[index].value || this.data[index];
        this.change ? this.change(value, this.index, bool) : "";
      }
    },
    _isDisabled(index, index2) {
      if (this.data[index].disabled) {
        if (index == this.data.length - 1) {
          index = -1; //到最后一條時经备,再從第一條開始找
        }
        //防止死循環(huán),全都是disabled時部默,原路返回
        if (index + 1 == index2) {
          return index2;
        }
        return this._isDisabled(index + 1);
      }
      return index;
    },
    _moveTo() {
      //根據(jù)value移動動相對應(yīng)的位置侵蒙,這個是組件加載完引用
      let index = 0;
      for (let i = 0; i < this.data.length; i++) {
        let v = this.data[i].value || this.data[i];
        if (this.value === v) {
          index = i;
          break;
        }
      }
      this._setIndex(index, false);
      //沒有默認時或是value不存在于數(shù)據(jù)數(shù)組中時index=0
    },
  },
  computed: {
    pickerMask() {
      return {
        //設(shè)定過度遮罩的顯示高度,即總顯示個數(shù)減1(高亮)的一半
        backgroundSize: "100% " + this._getVisibleCount() * this.height + "px",
      };
    },
    transformStyle() {
      return {
        transition: "all 150ms ease",
        transform: `translate3d(0, ${this.offset}px, 0)`,
      };
    },
  },
  mounted() {},
  filters: {},
};
</script>
<style scoped lang='less'>
.picker {
  touch-action: none;
  .mask {
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 100;
  }
  .picker-content {
    position: fixed;
    left: 0;
    bottom: 0;
    right: 0;
    background: #fff;
    z-index: 101;
    border-radius: 20px 20px 0px 0px;
  }
  .active-span {
    font-family: PingFangSC-Medium;
    font-size: 30px;
    color: #6522e6;
    font-weight: 500;
    margin-top: 15px;
  }

  .active-div {
    width: 52px;
    height: 6px;
    background: #6522e6;
    border-radius: 3px;
    margin-top: 5px;
  }
  .picker-tabs {
    width: 650px;
    margin: 0 auto;
    height: 80px;
    display: flex;
    justify-content: center;
    border-bottom: 2px solid #e6e8ed;

    .tabs-item {
      width: 40%;
      display: flex;
      flex-direction: column;
      align-items: center;

      .item-span {
        font-family: PingFangSC-Regular;
        font-size: 28px;
        color: #130038;
        font-weight: 400;
        margin-top: 15px;
      }
    }
  }
  /*取消確定按鈕*/
  .picker-control {
    height: 100px;
    background: #f8f8f8;
    border-radius: 20px 20px 0px 0px;
    padding: 0 20px;
    display: flex;
    justify-content: center;
    align-items: center;
    .picker-title {
      display: block;
      flex: 2;
      font-family: PingFangSC-Medium;
      font-size: 32px;
      color: #130038;
      text-align: center;
      font-weight: 500;
    }
    a {
      font-family: PingFangSC-Regular;
      display: block;
      color: #818181;
      font-size: 32px;
      font-weight: 400;
      &:last-child {
        font-family: PingFangSC-Medium;
        text-align: right;
        color: #130038;
      }
    }
  }
  .picker-group {
    display: flex;
    position: relative;
    overflow: hidden;
    .picker-border {
      left: 50%;
      top: 50%;
      margin-left: -326px;
      position: absolute;
      height: 100px;
      border-bottom: 2px solid #e5e5e5;
      border-top: 2px solid #e5e5e5;
      width: 650px;
      box-sizing: border-box;
      transform: translateY(-50%);
    }
  }
}
.picker-item {
  width: 100%;
  position: relative;
  overflow: hidden;
  .picker-mask {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    width: 100%;
    z-index: 3;
    background: linear-gradient(
        180deg,
        rgba(255, 255, 255, 0.95),
        rgba(255, 255, 255, 0.6)
      ),
      linear-gradient(0deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.6));
    background-repeat: no-repeat;
    background-position: top, bottom;
    /*background-size: 100% 102px;*/
    /*兩個線性過度疊在一起傅蹂,通過size設(shè)定顯示的高度*/
  }
  li {
    height: 100px;
    line-height: 100px;
    text-align: center;
    overflow: hidden;
    box-sizing: border-box;
    font-family: PingFangSC-Medium;
    font-size: 32px;
    color: #130038;
    font-weight: 500;
    &.disabled {
      font-style: italic;
    }
  }
}
// .fade-enter-active {
//   animation: fadeIn 0.5s;
// }
// .fade-leave-active {
//   animation: fadeOut 0.5s;
// }
// @keyframes fadeIn {
//   0% {
//     opacity: 0;
//   }
//   100% {
//     opacity: 1;
//   }
// }
// @keyframes fadeOut {
//   0% {
//     opacity: 1;
//   }
//   100% {
//     opacity: 0;
//   }
// }
// .slide-enter-active {
//   animation: fadeUp 0.5s;
// }
// .slide-leave-active {
//   animation: fadeDown 0.5s;
// }
// @keyframes fadeUp {
//   0% {
//     opacity: 0.6;
//     transform: translateY(100%);
//   }
//   100% {
//     opacity: 1;
//     transform: translateY(0);
//   }
// }
// @keyframes fadeDown {
//   0% {
//     opacity: 1;
//     transform: translateY(0);
//   }
//   100% {
//     opacity: 0.6;
//     transform: translateY(100%);
//   }
// }
</style>

引用原文大佬代碼
原文地址 https://github.com/337547038/Vue-Wap

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纷闺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子份蝴,更是在濱河造成了極大的恐慌犁功,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婚夫,死亡現(xiàn)場離奇詭異浸卦,居然都是意外死亡,警方通過查閱死者的電腦和手機案糙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門限嫌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人时捌,你說我怎么就攤上這事萤皂。” “怎么了匣椰?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵裆熙,是天一觀的道長。 經(jīng)常有香客問我禽笑,道長入录,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任佳镜,我火速辦了婚禮僚稿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蟀伸。我一直安慰自己蚀同,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布啊掏。 她就那樣靜靜地躺著蠢络,像睡著了一般。 火紅的嫁衣襯著肌膚如雪迟蜜。 梳的紋絲不亂的頭發(fā)上刹孔,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機與錄音娜睛,去河邊找鬼髓霞。 笑死卦睹,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的方库。 我是一名探鬼主播结序,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼纵潦!你這毒婦竟也來了徐鹤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤酪穿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后晴裹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體被济,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年涧团,在試婚紗的時候發(fā)現(xiàn)自己被綠了只磷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡泌绣,死狀恐怖钮追,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阿迈,我是刑警寧澤元媚,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站苗沧,受9級特大地震影響刊棕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜待逞,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一甥角、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧识樱,春花似錦嗤无、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至割疾,卻和暖如春灶壶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杈曲。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工驰凛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胸懈,地道東北人。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓恰响,卻偏偏與公主長得像趣钱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子胚宦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容