移动端WEB程序,类淘宝猜你喜欢效果

vue开发一个类似于手机淘宝中猜你喜欢的功能组件
组件代码:

<!--
 * @Author: your name
 * @Date: 2020-05-24 16:02:57
 * @LastEditTime: 2020-05-27 16:23:09
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /shopping-mall-template/src/components/MiddlePage/index.vue
-->
<template>
  <div ref="container" class="vvi-middle-page-container">
    <div ref="wrap" class="vvi-middle-page-wrap">
      <div ref="tab" class="vvi-middle-page-tab">
        <slot name="tab">
          <ul class="vvi-middle-page-tab__items">
            <li
              v-for="n in 5"
              :key="n"
              class="vvi-middle-page-tab__item"
              @touchstart="select"
            >
              <div class="title">全部</div>
              <div class="subtitle">猜你喜欢</div>
            </li>
            <li v-show="activeShow" ref="activebar" class="active-bar" />
          </ul>
        </slot>
      </div>
      <div ref="content" class="vvi-middle-page-content">
        <slot name="content">
          <h1 v-for="n in numbers" :key="n">{{ `middle-page-content-${n}` }}</h1>
        </slot>
        <div v-if="isLoading" class="loading">
          <span v-for="n in 8" :key="n" />
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'MiddlePage',
  props: {
    tabTop: {
      type: Number,
      default: 30
    }
  },
  data() {
    return {
      activeShow: false,
      isLoading: false,
      count: 0,
      numbers: 20
    }
  },
  computed: {
    parentHeight() {
      let h = 0
      this.$refs.container.parentElement.children.forEach(item => {
        h += item.clientHeight
      })
      return h
    },
    flag: {
      get() {
        return this.count
      },
      set(v) {
        this.count = v
      }
    }
  },
  mounted() {
    this.$refs.container.parentElement.addEventListener(
      'scroll',
      this.MCU,
      true
    )
  },
  beforeDestroy() {
    this.$refs.container.parentElement.removeEventListener('scroll', this.MCU)
  },
  methods: {
    LoadMore() {},
    removeStyle(nodes) {
      nodes.forEach(item => {
        item.removeAttribute('style')
      })
    },
    /** 副控制器 */
    SCU(self, parent, container, content, tab) {
      if (container.scrollTop < this.flag) {
        // console.log('向下')
        if (container.scrollTop < 5) {
          self.activeShow = false
          self.removeStyle([container, tab, parent, content])
          document.getElementsByClassName('subtitle').forEach(item => {
            item.style.display = 'block'
          })
        }
      } else {
        // console.log('向上')
        // console.log(`container.scrollTop:${container.scrollTop}`)
        // console.log(`差值:${content.clientHeight - container.clientHeight}`)
        if (
          container.scrollTop ===
            content.clientHeight - container.clientHeight
        ) {
          self.isLoading = true
          setTimeout(() => {
            self.numbers += 20
            self.isLoading = false
          }, 3 * 1000)
        }
      }
      setTimeout(() => {
        self.flag = container.scrollTop
      }, 0)
    },
    MCU(e) {
      const container = this.$refs.container // 组件外层容器
      const parent = container.parentElement
      const tab = this.$refs.tab || null // 页中页 tab 导航
      const content = this.$refs.content // 页中页内容部分容器
      const _Top = container.getBoundingClientRect().top // 组件顶部距视口顶部高度
      const self = this
      if (_Top <= this.tabTop) {
        parent.style.cssText = `overflow: hidden;` // 使父元素不可滚动
        parent.scrollTop =
          this.parentHeight - container.clientHeight - this.tabTop // 重新定位父元素位置

        tab.style.cssText = `position: fixed; top: ${this.tabTop}px; background-color: #fff; height: 1rem`
        document.getElementsByClassName('subtitle').forEach(item => {
          item.style.display = 'none'
        })
        content.style.cssText = `padding-top: ${tab.clientHeight}px`
        container.style.cssText = `overflow: scroll;`
        this.activeShow = true

        container.addEventListener('scroll', this.SCU(self, parent, container, content, tab), true)
      } else {
        // container.removeEventListener('scroll', SCU)
      }
    },
    select(e) {
      const activeBar = this.$refs.activebar
      const target = e.currentTarget
      activeBar.style.cssText = `transform: translateX(${target.offsetLeft}px); display: ${activeBar.style.display}`
    }
  }
}
</script>

<style lang="scss" scoped>
.vvi-middle-page-container {
  position: relative;
  height: 100vh;
  width: 100%;
  background-color: #f0f0f0;
  overflow: hidden;
  z-index: 99999;
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* IE 10+ */
  &::-webkit-scrollbar {
    display: none;
  }
}
.vvi-middle-page-wrap {
  position: relative;
  height: 100%;
  width: 100%;
}

.vvi-middle-page-tab {
  position: relative;
  background-color: transparent;
  width: 100%;
  height: 1.9738rem;
  padding: 0.4237rem 0.17rem;
  transition: height 0.5s linear;
  .vvi-middle-page-tab__items {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 100%;
    .active-bar {
      position: absolute;
      bottom: 10%;
      width: 16%;
      height: 3px;
      background-color: red;
      transform: translateX(10%);
      transition: all 0.5s linear;
    }
    .vvi-middle-page-tab__item {
      position: relative;
      display: block;
      width: 20%;
      text-align: center;
      touch-action: none;
      &::after {
        position: absolute;
        right: 0;
        top: 5%;
        content: "";
        display: block;
        width: 1px;
        height: 90%;
        background-color: #888;
      }
      &:nth-child(5)::after {
        display: none;
      }
      .title {
        width: 100%;
        font-size: 1.5em;
        font-weight: bold;
      }
      .subtitle {
        font-size: 1.1em;
        width: 100%;
        color: #888;
        margin-top: 0.5em;
        transition: display 2s linear;
      }
    }
  }
}
.vvi-middle-page-content {
  overflow: hidden;
  padding-left: 0.17rem;
  padding-right: 0.17rem;
  background-color: yellow;
  padding-bottom: 2rem;
  box-sizing: border-box;
}
.loading {
  position: relative;
  width: 1rem;
  height: 1rem;
  margin: 0 auto;
  margin-top: .1rem;
  span {
    position: absolute;
    display: inline-block;
    width: 0.3rem;
    height: 0.1rem;
    border-top-left-radius: 0.05rem;
    border-bottom-left-radius: 0.05rem;
    background: lightgreen;
    animation: load 1.04s ease infinite;
  }
  @keyframes load {
    0% {
      opacity: 1;
    }
    100% {
      opacity: 0.2;
    }
  }
}
.loading span {
  &:nth-child(1) {
    left: 0;
    top: 50%;
    margin-top: -0.05rem;
    animation-delay: 0.13s;
  }
  &:nth-child(2) {
    left: 0.1rem;
    top: 0.2rem;
    transform: rotate(45deg);
    animation-delay: 0.26s;
  }
  &:nth-child(3) {
    left: 50%;
    top: 0.1rem;
    margin-left: -.15rem;
    transform: rotate(90deg);
    animation-delay: 0.39s;
  }
  &:nth-child(4) {
    right: 0.1rem;
    top: 0.2rem;
    transform: rotate(135deg);
    animation-delay: 0.52s;
  }
  &:nth-child(5) {
    right: 0;
    top: 50%;
    transform: rotate(180deg);
    animation-delay: 0.65s;
  }
  &:nth-child(6) {
    right: 0.1rem;
    bottom: 0.2rem;
    transform: rotate(225deg);
    animation-delay: 0.78s;
  }
  &:nth-child(7) {
    left: 50%;
    bottom: 0.1rem;
    margin-left: -.15rem;
    transform: rotate(270deg);
    animation-delay: 0.91s;
  }
  &:nth-child(8) {
    left: 0.1rem;
    bottom: 0.2rem;
    transform: rotate(315deg);
    animation-delay: 1.04s;
  }
}
</style>

调用方法:

<template>
  <div class="test scrollbar-hidden">
    <div class="box" />
    <div class="box2" />
    <middle-page>
      <!-- <template v-slot:tab>
        <ul>
          <li>abc</li>
        </ul>
      </template> -->
      <!-- <template v-slot:content>
        <ul>
          <li>gkl</li>
        </ul>
      </template> -->
    </middle-page>
  </div>
</template>

<script>
import MiddlePage from '@/components/MiddlePage'
export default {
  name: 'ShoppingCar',
  components: {
    MiddlePage
  },
  data() {
    return {
      mark: 0
    }
  },
  mounted() {
    this.show()
    this.getElementInfo()
  },
  methods: {
    /**
     * 1. 获取要移动的元素
     */
    getElementInfo() {

    },
    show() {
      // const styleSheets = document.styleSheets
      // console.log(this.$store.getters.tabbar.index)
    }
  }
}
</script>

<style lang='scss' scoped>
.test {
  width: 100%;
  height: 100%;
  overflow: scroll;
}
.box {
  height: 3rem;
  width: 100%;
  background-color: green;
}
.box2 {
  background-color: orange;
  width: 100%;
  height: 3rem;
}
</style>

组件自带部分样式,可自行插入slot,思路有点乱,如果有代码优化的想法,请给我留言,谢谢。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值