vue2/van-search 搜索框关键词轮播,focus当前词条搜索

449 阅读1分钟

参考文档:blog.csdn.net/hktkkkk/art…

以下需求在上面文档基础上进行了修改优化。

实现效果:

1.搜索框内热门词条滚动显示

2.滚动到当前词条focus之后回车搜索

3.清空搜索框显示 placeholder '请输入关键词搜索'

4.点击取消文本框清空关键字,并显示 placeholder '请输入关键词搜索',之后恢复热门词条滚动显示

image.png

实现代码:

<template>
  <div>
    <form action="/">
      <div class="search-box">
        <label
          :data-before="before"
          :data-after="after"
          :class="{ animate: shouldAnimate }"
          @animationend="animationEnded"
        >
          <van-search
            v-model="keyWord"
            @focus="focus"
            @search="onSearch"
            @cancel="onCancel"
            @clear="clear"
            :placeholder="myPlaceholder"
          />
        </label>
        <div class="search-cancel" @click="onCancel">取消</div>
      </div>
    </form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      keyWord: null,

      keywords: ["暑假去哪儿玩", "新能源电动车", "充电站点", "优惠券"],
      isFocusing: false,
      currentIndex: 0,
      animationTriggerFlag: false,
      timer: null,
      myPlaceholder: null,
    };
  },
  computed: {
    before() {
      let keyword = this.keywords[this.currentIndex];
      return this.isFocusing ? "" : keyword;
    },
    after() {
      let keyword =
        typeof this.keywords[this.currentIndex + 1] === "undefined"
          ? this.keywords[0]
          : this.keywords[this.currentIndex + 1];
      return this.isFocusing ? "" : keyword;
    },
    placeholder() {
      let keyword = this.keywords[this.currentIndex];
      return !this.isFocusing ? "" : keyword;
    },
    // 是否开启动画
    shouldAnimate() {
      return !this.isFocusing && this.animationTriggerFlag;
    },
  },
  methods: {
    clear() {
      this.keyWord = null;
      this.myPlaceholder = "请输入关键字搜索";
    },
    onSearch(query) {
      this.$emit("onSearch", query);
    },
    onCancel() {
      this.keyWord = null;
      this.clear();
      this.$emit("onCancel");
      this.blur();
    },
    // 动画结束调用事件
    animationEnded(e) {
      // pseudoElement伪元素
      if (e.pseudoElement === "::after") {
        this.animationTriggerFlag = false;
        let newIndex =
          typeof this.keywords[this.currentIndex + 1] === "undefined"
            ? 0
            : this.currentIndex + 1;
        this.currentIndex = newIndex;
        setTimeout(() => {
          this.animationTriggerFlag = true;
        }, 3000);
      }
    },
    focus() {
      if(!this.myPlaceholder) {
        this.keyWord = this.keywords[this.currentIndex];
      }
      // 如果一直点击,动画会一直执行,要先清除动画
      if (this.timer) {
        clearTimeout(this.timer);
      }
      this.isFocusing = true;
      this.animationTriggerFlag = false;
      if (!this.isFocusing && !this.keyWord) {
        this.myPlaceholder = "请输入关键字搜索";
      }
    },
    blur() {
      // 鼠标blur后3秒之后再执行动画
      this.timer = setTimeout(() => {
        this.myPlaceholder = null;
        this.keyWord = null;
        this.isFocusing = false;
        this.animationTriggerFlag = true;
      }, 3000);
    },
    doSearch(e) {
      if (e.keyCode === 13) {
        alert(this.keywords[this.currentIndex]);
      }
    },
  },
  created() {
    // 刚进入页面1秒后再执行动画
    setTimeout(() => {
      this.animationTriggerFlag = true;
    }, 1000);
  },
  mounted() {},
};
</script>
<style scoped lang="less">
input[type="text"] {
  border: 3px solid #333;
  font-size: 0.24rem;
  border-radius: 0.5rem;
  padding: 0.5rem 1rem;
  margin: 0;
  font-family: inherit;
  outline: none;
}

label {
  position: relative;
  display: block;
  overflow: hidden;
  height: 0.76rem;
  width: 90%;
}

// 动态绑定元素
label::before {
  content: attr(data-before);
}

label::after {
  content: attr(data-after);
}

label::before,
label::after {
  display: block;
  position: absolute;
  width: 100%;
  height: 100%;
  border: 3px solid transparent;
  font-size: 0.28rem;
  padding: 0.28rem 0.78rem;
  font-family: inherit;
  box-sizing: border-box;
  color: #999;
}

@keyframes placeholder-before {
  0% {
    transform: translateY(0%);
    opacity: 1;
  }
  100% {
    transform: translateY(-100%);
    opacity: 0;
  }
}

@keyframes placeholder-after {
  0% {
    transform: translateY(0%);
    opacity: 0;
  }
  100% {
    transform: translateY(-100%);
    opacity: 1;
  }
}

.animate::before {
  animation: 0.3s placeholder-before ease-in-out;
}

.animate::after {
  animation: 0.3s placeholder-after ease-in-out;
}

.animate::before,
.animate::after {
  animation-fill-mode: forwards;
}
.search-box {
  display: flex;
  .search-cancel {
    width: 10%;
    line-height: 0.94rem;
  }
}
</style>