Browse Source

Backtop: Add backtop component (#15541)

iamkun 6 năm trước cách đây
mục cha
commit
45383cd655

+ 1 - 0
components.json

@@ -74,5 +74,6 @@
   "divider": "./packages/divider/index.js",
   "image": "./packages/image/index.js",
   "calendar": "./packages/calendar/index.js",
+  "backtop": "./packages/backtop/index.js",
   "infiniteScroll": "./packages/infiniteScroll/index.js"
 }

+ 60 - 0
examples/docs/en-US/backtop.md

@@ -0,0 +1,60 @@
+## Backtop
+
+A button to back to top
+
+### Basic usage
+
+Scroll down to see the bottom-right button.
+:::demo
+
+```html
+<template>
+  Scroll down to see the bottom-right button.
+  <el-backtop target=".page-component__scroll .el-scrollbar__wrap"></el-backtop>
+</template>
+```
+
+:::
+
+### Customizations
+
+Display area is 40px \* 40px.
+:::demo
+
+```html
+<template>
+  Scroll down to see the bottom-right button.
+  <el-backtop target=".page-component__scroll .el-scrollbar__wrap" :bottom="100">
+    <div
+      style="{
+        height: 100%;
+        width: 100%;
+        background-color: #f2f5f6;
+        box-shadow: 0 0 6px rgba(0,0,0, .12);
+        text-align: center;
+        line-height: 40px;
+        color: #1989fa;
+      }"
+    >
+      UP
+    </div>
+  </el-backtop>
+</template>
+```
+
+:::
+
+### Attributes
+
+| Attribute         | Description                                                         | Type            | Accepted Values | Default |
+| ----------------- | ------------------------------------------------------------------- | --------------- | --------------- | ------- |
+| target            | the target to trigger scroll                                        | string          |                 |         |
+| visibility-height | the button will not show until the scroll height reaches this value | number |                 | 200     |
+| right             | right distance                                                      | number |                 | 40      |
+| bottom            | bottom distance                                                     | number |                 | 40      |
+
+### Events
+
+| Event Name | Description         | Parameters  |
+| ---------- | ------------------- | ----------- |
+| click      | triggers when click | click event |

+ 60 - 0
examples/docs/es/backtop.md

@@ -0,0 +1,60 @@
+## Backtop
+
+A button to back to top
+
+### Basic usage
+
+Scroll down to see the bottom-right button.
+:::demo
+
+```html
+<template>
+  Scroll down to see the bottom-right button.
+  <el-backtop target=".page-component__scroll .el-scrollbar__wrap"></el-backtop>
+</template>
+```
+
+:::
+
+### Customizations
+
+Display area is 40px \* 40px.
+:::demo
+
+```html
+<template>
+  Scroll down to see the bottom-right button.
+  <el-backtop target=".page-component__scroll .el-scrollbar__wrap" :bottom="100">
+    <div
+      style="{
+        height: 100%;
+        width: 100%;
+        background-color: #f2f5f6;
+        box-shadow: 0 0 6px rgba(0,0,0, .12);
+        text-align: center;
+        line-height: 40px;
+        color: #1989fa;
+      }"
+    >
+      UP
+    </div>
+  </el-backtop>
+</template>
+```
+
+:::
+
+### Attributes
+
+| Attribute         | Description                                                         | Type            | Accepted Values | Default |
+| ----------------- | ------------------------------------------------------------------- | --------------- | --------------- | ------- |
+| target            | the target to trigger scroll                                        | string          |                 |         |
+| visibility-height | the button will not show until the scroll height reaches this value | number |                 | 200     |
+| right             | right distance                                                      | number |                 | 40      |
+| bottom            | bottom distance                                                     | number |                 | 40      |
+
+### Events
+
+| Event Name | Description         | Parameters  |
+| ---------- | ------------------- | ----------- |
+| click      | triggers when click | click event |

+ 60 - 0
examples/docs/fr-FR/backtop.md

@@ -0,0 +1,60 @@
+## Backtop
+
+A button to back to top
+
+### Basic usage
+
+Scroll down to see the bottom-right button.
+:::demo
+
+```html
+<template>
+  Scroll down to see the bottom-right button.
+  <el-backtop target=".page-component__scroll .el-scrollbar__wrap"></el-backtop>
+</template>
+```
+
+:::
+
+### Customizations
+
+Display area is 40px \* 40px.
+:::demo
+
+```html
+<template>
+  Scroll down to see the bottom-right button.
+  <el-backtop target=".page-component__scroll .el-scrollbar__wrap" :bottom="100">
+    <div
+      style="{
+        height: 100%;
+        width: 100%;
+        background-color: #f2f5f6;
+        box-shadow: 0 0 6px rgba(0,0,0, .12);
+        text-align: center;
+        line-height: 40px;
+        color: #1989fa;
+      }"
+    >
+      UP
+    </div>
+  </el-backtop>
+</template>
+```
+
+:::
+
+### Attributes
+
+| Attribute         | Description                                                         | Type            | Accepted Values | Default |
+| ----------------- | ------------------------------------------------------------------- | --------------- | --------------- | ------- |
+| target            | the target to trigger scroll                                        | string          |                 |         |
+| visibility-height | the button will not show until the scroll height reaches this value |  number |                 | 200     |
+| right             | right distance                                                      | number |                 | 40      |
+| bottom            | bottom distance                                                     | number |                 | 40      |
+
+### Events
+
+| Event Name | Description         | Parameters  |
+| ---------- | ------------------- | ----------- |
+| click      | triggers when click | click event |

+ 60 - 0
examples/docs/zh-CN/backtop.md

@@ -0,0 +1,60 @@
+## Backtop 回到顶部
+
+返回页面顶部的操作按钮
+
+### 基础用法
+
+滑动页面即可看到右下方的按钮。
+:::demo
+
+```html
+<template>
+  Scroll down to see the bottom-right button.
+  <el-backtop target=".page-component__scroll .el-scrollbar__wrap"></el-backtop>
+</template>
+```
+
+:::
+
+### 自定义显示内容
+
+显示区域被固定为 40px \* 40px 的区域, 其中的内容可支持自定义。
+:::demo
+
+```html
+<template>
+  Scroll down to see the bottom-right button.
+  <el-backtop target=".page-component__scroll .el-scrollbar__wrap" :bottom="100">
+    <div
+      style="{
+        height: 100%;
+        width: 100%;
+        background-color: #f2f5f6;
+        box-shadow: 0 0 6px rgba(0,0,0, .12);
+        text-align: center;
+        line-height: 40px;
+        color: #1989fa;
+      }"
+    >
+      UP
+    </div>
+  </el-backtop>
+</template>
+```
+
+:::
+
+### Attributes
+
+| 参数              | 说明                             | 类型            | 可选值 | 默认值 |
+| ----------------- | -------------------------------- | --------------- | ------ | ------ |
+| target            | 触发滚动的对象                   | string          |        |        |
+| visibility-height | 滚动高度达到此参数值才出现       | number |        | 200    |
+| right             | 控制其显示位置, 距离页面右边距   | number |        | 40     |
+| bottom            | 控制其显示位置, 距离页面底部距离 | number |        | 40     |
+
+### Events
+
+| 事件名 | 说明               | 回调参数 |
+| ------ | ------------------ | -------- |
+| click  | 点击按钮触发的事件 | 点击事件 |

+ 16 - 0
examples/nav.config.json

@@ -268,6 +268,10 @@
               "path": "/image",
               "title": "Image 图片"
             },
+            {
+              "path": "/backtop",
+              "title": "Backtop 回到顶部"
+            },
             {
               "path": "/infiniteScroll",
               "title": "InfiniteScroll 无限滚动"
@@ -546,6 +550,10 @@
               "path": "/image",
               "title": "Image"
             },
+            {
+              "path": "/backtop",
+              "title": "Backtop"
+            },
             {
               "path": "/infiniteScroll",
               "title": "InfiniteScroll"
@@ -824,6 +832,10 @@
               "path": "/image",
               "title": "Image"
             },
+            {
+              "path": "/backtop",
+              "title": "Backtop"
+            },
             {
               "path": "/infiniteScroll",
               "title": "InfiniteScroll"
@@ -1102,6 +1114,10 @@
               "path": "/image",
               "title": "Image"
             },
+            {
+              "path": "/backtop",
+              "title": "Backtop"
+            },
             {
               "path": "/infiniteScroll",
               "title": "InfiniteScroll"

+ 11 - 54
examples/pages/template/component.tpl

@@ -101,38 +101,6 @@
         }
       }
     }
-
-    .page-component-up {
-      background-color: #fff;
-      position: fixed;
-      right: 100px;
-      bottom: 150px;
-      width: 40px;
-      height: 40px;
-      size: 40px;
-      border-radius: 20px;
-      cursor: pointer;
-      transition: .3s;
-      box-shadow: 0 0 6px rgba(0,0,0, .12);
-      z-index: 5;
-
-      i {
-        color: #409EFF;
-        display: block;
-        line-height: 40px;
-        text-align: center;
-        font-size: 18px;
-      }
-
-      &.hover {
-        opacity: 1;
-      }
-    }
-    .back-top-fade-enter,
-    .back-top-fade-leave-active {
-      transform: translateY(-30px);
-      opacity: 0;
-    }
   }
 
   @media (max-width: 768px) {
@@ -157,9 +125,6 @@
         overflow: auto;
         display: block;
       }
-      .page-component-up {
-        display: none;
-      }
     }
   }
 </style>
@@ -173,17 +138,12 @@
       <router-view class="content"></router-view>
       <footer-nav></footer-nav>
     </div>
-    <transition name="back-top-fade">
-      <div
-        class="page-component-up"
-        :class="{ 'hover': hover }"
-        v-show="showBackToTop"
-        @mouseenter="hover = true"
-        @mouseleave="hover = false"
-        @click="toTop">
-        <i class="el-icon-caret-top"></i>
-      </div>
-    </transition>
+    <el-backtop 
+      v-if="showBackToTop"
+      target=".page-component__scroll .el-scrollbar__wrap"
+      right="100"
+      bottom="150"
+    ></el-backtop>
   </div>
   </el-scrollbar>
 </template>
@@ -197,8 +157,6 @@
       return {
         lang: this.$route.meta.lang,
         navsData,
-        hover: false,
-        showBackToTop: false,
         scrollTop: 0,
         showHeader: true,
         componentScrollBar: null,
@@ -238,15 +196,9 @@
           }, 50);
         }
       },
-      toTop() {
-        this.hover = false;
-        this.showBackToTop = false;
-        this.componentScrollBox.scrollTop = 0;
-      },
 
       handleScroll() {
         const scrollTop = this.componentScrollBox.scrollTop;
-        this.showBackToTop = scrollTop >= 0.5 * document.body.clientHeight;
         if (this.showHeader !== this.scrollTop > scrollTop) {
           this.showHeader = this.scrollTop > scrollTop;
         }
@@ -259,6 +211,11 @@
         this.scrollTop = scrollTop;
       }
     },
+    computed: {
+      showBackToTop() {
+        return !this.$route.path.match(/backtop/);
+      }
+    },
     created() {
       bus.$on('navFade', val => {
         this.navFaded = val;

+ 8 - 0
packages/backtop/index.js

@@ -0,0 +1,8 @@
+import Backtop from './src/main';
+
+/* istanbul ignore next */
+Backtop.install = function(Vue) {
+  Vue.component(Backtop.name, Backtop);
+};
+
+export default Backtop;

+ 101 - 0
packages/backtop/src/main.vue

@@ -0,0 +1,101 @@
+<template>
+  <transition name="el-fade-in">
+    <div
+      v-if="visible"
+      @click.stop="handleClick"
+      :style="{
+        'right': styleRight,
+        'bottom': styleBottom
+      }"
+      class="el-backtop">
+      <slot>
+        <el-icon name="caret-top"></el-icon>
+      </slot>
+    </div>
+  </transition>
+</template>
+
+<script>
+import throttle from 'throttle-debounce/throttle';
+
+export default {
+  name: 'ElBacktop',
+
+  props: {
+    visibilityHeight: {
+      type: Number,
+      default: 200
+    },
+    target: [String],
+    right: {
+      type: Number,
+      default: 40
+    },
+    bottom: {
+      type: Number,
+      default: 40
+    }
+  },
+
+  data() {
+    return {
+      el: null,
+      container: null,
+      visible: false
+    };
+  },
+
+  computed: {
+    styleBottom() {
+      return `${this.bottom}px`;
+    },
+    styleRight() {
+      return `${this.right}px`;
+    }
+  },
+
+  mounted() {
+    this.init();
+    this.throttledScrollHandler = throttle(300, this.onScroll);
+    this.container.addEventListener('scroll', this.throttledScrollHandler);
+  },
+
+  methods: {
+    init() {
+      this.container = document;
+      this.el = document.documentElement;
+      if (this.target) {
+        this.el = document.querySelector(this.target);
+        if (!this.el) {
+          throw new Error(`target is not existed: ${this.target}`);
+        }
+        this.container = this.el;
+      }
+    },
+    onScroll() {
+      const scrollTop = this.el.scrollTop;
+      this.visible = scrollTop >= this.visibilityHeight;
+    },
+    handleClick(e) {
+      this.scrollToTop();
+      this.$emit('click', e);
+    },
+    scrollToTop() {
+      let el = this.el;
+      let step = 0;
+      let interval = setInterval(() => {
+        if (el.scrollTop <= 0) {
+          clearInterval(interval);
+          return;
+        }
+        step += 10;
+        el.scrollTop -= step;
+      }, 20);
+    }
+  },
+
+  beforeDestroy() {
+    this.container.removeEventListener('scroll', this.throttledScrollHandler);
+  }
+};
+</script>

+ 22 - 0
packages/theme-chalk/src/backtop.scss

@@ -0,0 +1,22 @@
+@import "mixins/mixins";
+@import "common/var";
+
+@include b(backtop) {
+  position: fixed;
+  background-color: $--backtop-background-color;
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  color: $--backtop-font-color;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 20px;
+  box-shadow: 0 0 6px rgba(0,0,0, .12);
+  cursor: pointer;
+  z-index: 5;
+
+  &:hover {
+    background-color: $--backtop-hover-background-color
+  }
+}

+ 9 - 0
packages/theme-chalk/src/common/var.scss

@@ -905,6 +905,15 @@ $--timeline-node-size-normal: 12px !default;
 $--timeline-node-size-large: 14px !default;
 $--timeline-node-color: $--border-color-light !default;
 
+/* Backtop
+--------------------------*/
+/// color||Color|0
+$--backtop-background-color: $--color-white !default;
+/// color||Color|0
+$--backtop-font-color: $--color-primary !default;
+/// color||Color|0
+$--backtop-hover-background-color: $--border-color-extra-light !default;
+
 /* Link
 --------------------------*/
 /// fontSize||Font|1

+ 1 - 0
packages/theme-chalk/src/index.scss

@@ -71,3 +71,4 @@
 @import "./divider.scss";
 @import "./image.scss";
 @import "./calendar.scss";
+@import "./backtop.scss";

+ 3 - 0
src/index.js

@@ -75,6 +75,7 @@ import Link from '../packages/link/index.js';
 import Divider from '../packages/divider/index.js';
 import Image from '../packages/image/index.js';
 import Calendar from '../packages/calendar/index.js';
+import Backtop from '../packages/backtop/index.js';
 import InfiniteScroll from '../packages/infiniteScroll/index.js';
 import locale from 'element-ui/src/locale';
 import CollapseTransition from 'element-ui/src/transitions/collapse-transition';
@@ -151,6 +152,7 @@ const components = [
   Divider,
   Image,
   Calendar,
+  Backtop,
   CollapseTransition
 ];
 
@@ -266,5 +268,6 @@ export default {
   Divider,
   Image,
   Calendar,
+  Backtop,
   InfiniteScroll
 };

+ 28 - 0
test/unit/specs/backtop.spec.js

@@ -0,0 +1,28 @@
+import { createVue, destroyVM, wait } from '../util';
+
+describe('Backtop', () => {
+  let vm;
+  afterEach(() => {
+    destroyVM(vm);
+  });
+
+  it('create', async() => {
+    vm = createVue({
+      template: `
+        <div ref="scrollTarget" class="test-scroll"  style="height: 100px; overflow: auto">
+          <div style="height: 10000px; width: 100%">
+            <el-backtop target=".test-scroll">
+              <span>test_up_text</span>
+            </el-backtop>
+          </div>
+        </div>
+      `
+    }, true);
+    expect(vm.$el).to.exist;
+    expect(vm.$el.innerText).to.be.equal('');
+    vm.$refs.scrollTarget.scrollTop = 2000;
+    await wait();
+    expect(vm.$el.innerText).to.be.equal('test_up_text');
+  });
+});
+

+ 16 - 0
types/backtop.d.ts

@@ -0,0 +1,16 @@
+import { ElementUIComponent } from './component'
+
+/** Backtop Component */
+export declare class ElBacktop extends ElementUIComponent {
+  /** Backtop target */
+  target: string
+  
+  /** Backtop visibility height */
+  visibilityHeight: string | number
+
+  /** Backtop right position */
+  right: string | number
+
+  /** Backtop bottom position */
+  bottom: string | number
+}

+ 5 - 0
types/element-ui.d.ts

@@ -74,6 +74,7 @@ import { ElDivider } from './divider'
 import { ElIcon } from './icon'
 import { ElCalendar } from './calendar'
 import { ElImage } from './image'
+import { ElBacktop } from './backtop'
 import { ElInfiniteScroll } from './infiniteScroll'
 
 export interface InstallationOptions {
@@ -322,5 +323,9 @@ export class Icon extends ElIcon {}
 /** Calendar Component */
 export class Calendar extends ElCalendar {}
 
+/** Backtop Component */
+export class Backtop extends ElBacktop {}
+
 /** InfiniteScroll Component */
 export class InfiniteScroll extends ElInfiniteScroll {}
+