浏览代码

C端精品典藏:添加”回到顶部“按钮(在等图标资源)

任一存 3 年之前
父节点
当前提交
040fa3a55d
共有 5 个文件被更改,包括 187 次插入4 次删除
  1. 11 0
      web/package-lock.json
  2. 4 3
      web/package.json
  3. 109 0
      web/src/components/BackTop.vue
  4. 30 1
      web/src/components/lrLayout/index.vue
  5. 33 0
      web/src/config/utils.js

+ 11 - 0
web/package-lock.json

@@ -8,6 +8,7 @@
       "name": "web",
       "version": "0.1.0",
       "dependencies": {
+        "@tweenjs/tween.js": "^18.6.4",
         "axios": "^0.19.2",
         "core-js": "^3.6.5",
         "js-base64": "^3.6.1",
@@ -1443,6 +1444,11 @@
       "integrity": "sha1-pTUV2yXYA4N0OBtzryC7Ty5QjYc=",
       "dev": true
     },
+    "node_modules/@tweenjs/tween.js": {
+      "version": "18.6.4",
+      "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz",
+      "integrity": "sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ=="
+    },
     "node_modules/@types/color-name": {
       "version": "1.1.1",
       "resolved": "https://registry.npm.taobao.org/@types/color-name/download/@types/color-name-1.1.1.tgz?cache=0&sync_timestamp=1588200011932&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fcolor-name%2Fdownload%2F%40types%2Fcolor-name-1.1.1.tgz",
@@ -15423,6 +15429,11 @@
       "integrity": "sha1-pTUV2yXYA4N0OBtzryC7Ty5QjYc=",
       "dev": true
     },
+    "@tweenjs/tween.js": {
+      "version": "18.6.4",
+      "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz",
+      "integrity": "sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ=="
+    },
     "@types/color-name": {
       "version": "1.1.1",
       "resolved": "https://registry.npm.taobao.org/@types/color-name/download/@types/color-name-1.1.1.tgz?cache=0&sync_timestamp=1588200011932&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fcolor-name%2Fdownload%2F%40types%2Fcolor-name-1.1.1.tgz",

+ 4 - 3
web/package.json

@@ -8,15 +8,16 @@
     "lint": "vue-cli-service lint"
   },
   "dependencies": {
+    "@tweenjs/tween.js": "^18.6.4",
     "axios": "^0.19.2",
     "core-js": "^3.6.5",
     "js-base64": "^3.6.1",
-    "vue": "^2.6.11",
-    "vue-chat-scroll": "^1.4.0",
-    "vue-router": "^3.2.0",
     "swiper": "^5.3.8",
     "v-viewer": "^1.6.4",
+    "vue": "^2.6.11",
     "vue-awesome-swiper": "^4.1.1",
+    "vue-chat-scroll": "^1.4.0",
+    "vue-router": "^3.2.0",
     "vuex": "^3.5.1"
   },
   "devDependencies": {

+ 109 - 0
web/src/components/BackTop.vue

@@ -0,0 +1,109 @@
+<template>
+  <div class="back-top" @click="onClickBackTop" v-show="isShowBackTopBtn">
+    <slot>
+      <div class="back-top__default">回到顶部</div>
+    </slot>
+  </div>
+</template>
+
+<script>
+const { debounce } = require('@/config/utils.js')
+const TWEEN = require('@tweenjs/tween.js')
+
+export default({
+  props: {
+    targetId: {
+      type: String,
+      required: true,
+    },
+    triggerDistance: {
+      type: Number,
+      default: 200,
+    }
+  },
+   data() {
+    return {
+      target: null,
+      isShowBackTopBtn: false,
+      isBackingTop: false,
+    }
+  },
+  methods: {
+    onClickBackTop() {
+      if (this.isBackingTop) {
+        return
+      }
+      this.isBackingTop = true
+
+      const tweenTarget = {
+        scrollTop: this.target.scrollTop
+      }
+      new TWEEN.Tween(tweenTarget)
+        .to({scrollTop: 0}, 800)
+        .easing(TWEEN.Easing.Quartic.Out)
+        .onUpdate(() => {
+          this.target.scrollTop = tweenTarget.scrollTop
+        })
+        .onComplete(() => {
+          this.isBackingTop = false
+        })
+        .start()
+      
+      const animate = (time) => {
+        if (this.isBackingTop) {
+          requestAnimationFrame(animate)
+          TWEEN.update(time)
+        }
+      }
+      requestAnimationFrame(animate)
+
+      // 不想引入tween.js的话,可以用这段简单的匀速滚动代码
+      // const startTime = Date.now()
+      // const totalScroll = this.target.scrollTop
+      // const fn = () => {
+      //   if (this.target.scrollTop === 0) {
+      //     this.isBackingTop = false
+      //     return
+      //   } 
+
+      //   const nowTime = Date.now()
+      //   const assumeScrollTop = totalScroll - (nowTime - startTime) * totalScroll / 500
+      //   this.target.scrollTop = assumeScrollTop > 0 ? assumeScrollTop : 0
+      //   requestAnimationFrame(fn)
+      // }
+      // requestAnimationFrame(fn)
+    },
+    onTargetScroll: debounce(function(e) {
+      if (this.isBackingTop) {
+        return
+      }
+      if (e.target.scrollTop >= this.triggerDistance) {
+        this.isShowBackTopBtn = true
+      } else {
+        this.isShowBackTopBtn = false
+      }
+    }),
+  },
+  mounted() {
+    this.target = document.getElementById(this.targetId)
+    if (this.target) {
+      this.target.addEventListener('scroll', this.onTargetScroll, {
+        passive: true,
+      })
+    }
+  },
+  unmounted() {
+    if (this.target) {
+      this.target.removeEventListener('scroll', this.onTargetScroll, {
+        passive: true,
+      })
+    }
+  }
+})
+</script>
+
+<style scoped lang="less">
+.back-top__default {
+  cursor: pointer;
+}
+</style>

+ 30 - 1
web/src/components/lrLayout/index.vue

@@ -1,14 +1,25 @@
 <template>
   <div class="lr_layout">
+    <BackTop class="back-top" :targetId="'collection-list'"></BackTop>
     <div class="lcon">
       <slot name="lcon"></slot>
     </div>
-    <div class="rcon">
+    <div id="collection-list" class="rcon">
       <slot name="rcon"></slot>
     </div>
   </div>
 </template>
 
+<script>
+import BackTop from "@/components/BackTop.vue";
+
+export default {
+  components: {
+    BackTop
+  },
+}
+</script>
+
 <style lang="less" scoped>
 .lr_layout {
   position: relative;
@@ -36,4 +47,22 @@
     }
   }
 }
+
+  .back-top {
+    position: absolute;
+    right: -130px;
+    bottom: 130px;
+    width: 60px;
+    height: 60px;
+    border-radius: 8px;
+    background-color: #fff;
+    color: #C8C9CC;
+    &:hover {
+      color: #323233;
+    }
+    cursor: pointer;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
 </style>

+ 33 - 0
web/src/config/utils.js

@@ -101,5 +101,38 @@ module.exports = {
     }
 
     return front + str2 + middle + str1 + end
+  },
+  /**
+   * 返回一个自带消抖效果的函数,用res表示。
+   * 
+   * fn: 需要被消抖的函数
+   * delay: 消抖时长
+   * isImmediateCall: 是在第一次调用时立即执行fn,还是在最后一次调用后等delay时长再调用fn
+   */
+  debounce: function(fn, delay, isImmediateCall = false) {
+    let timer = null
+    // 上次调用的时刻
+    let lastCallTime = 0
+
+    if (isImmediateCall) {
+      return function (...args) {
+        const context = this
+        const currentTime = Date.now()
+        if (currentTime - lastCallTime >= delay) {
+          fn.apply(context, args)
+        }
+        lastCallTime = currentTime
+      }
+    } else {
+      return function (...args) {
+        if (timer) {
+          clearTimeout(timer)
+        }
+        const context = this
+        timer = setTimeout(() => {
+          fn.apply(context, args)
+        }, delay)
+      }
+    }
   }
 };