From 91ccfc58bbb0b870fd08d604d9068b9779e89d45 Mon Sep 17 00:00:00 2001 From: sight <1453017105@qq.com> Date: Mon, 20 Dec 2021 07:19:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=88=9D=E7=89=88=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E5=8A=9F=E8=83=BD=EF=BC=8C=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E9=A1=B6=E9=83=A8=EF=BC=8C=E5=9B=BA=E5=AE=9A=E6=8C=89=E9=92=AE?= =?UTF-8?q?=EF=BC=8C=E8=87=AA=E5=AE=9A=E4=B9=89=E5=86=85=E5=AE=B9=EF=BC=8C?= =?UTF-8?q?=E7=89=B9=E5=AE=9A=E5=AE=B9=E5=99=A8=E5=86=85=E9=83=A8=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/docs/zh-CN/components/backtop.md | 170 +++++++++++++++++++++++ example/src/layouts/Layout.vue | 5 + example/src/router/zh-CN.ts | 4 + example/src/view/component.vue | 6 + src/module/backTop/index.less | 21 +++ src/module/backTop/index.vue | 117 +++++++++++++++- 6 files changed, 317 insertions(+), 6 deletions(-) create mode 100644 example/docs/zh-CN/components/backtop.md create mode 100644 src/module/backTop/index.less diff --git a/example/docs/zh-CN/components/backtop.md b/example/docs/zh-CN/components/backtop.md new file mode 100644 index 00000000..5c6bcbab --- /dev/null +++ b/example/docs/zh-CN/components/backtop.md @@ -0,0 +1,170 @@ +::: title 基础使用 + +###### 回到顶部组件的默认样式,通过滑动来查看页面右下角的按钮。 + +::: +::: demo + + + + + +::: + +:::title 自定义 + +###### 回到顶部组件可自定义样式,限制宽高:50px \* 50px,showHeight = 0 将始终显示,disabled 属性可以禁用返回顶部效果。查看页面右下角的圆形按钮。 + +::: demo + + + + + +::: + +::: title 滚动容器 + +###### 通过设置 targetposition="absolute"参数 ,可对特定容器进行返回顶部操作 + +::: +::: demo + + + + + +::: + +::: title 结合 tooltip 组件使用 + +###### 可以和 lay-tooltip 组件搭配使用 + +::: +::: demo + + + + + +::: + +::: title 属性 +::: + +::: table + +| 属性 | 说明 | 类型 | 可选值 | +| ---------- | --------------------------------------------------- | ------- | ------------------------ | +| target | 可选,触发滚动的对象 | string | 元素选择器 \| window(默认) | +| showHeight | 可选,滚动高度达到该值后显示回到顶部按钮 | number | 200(默认) | +| position | 可选,定位方式,特定容器内部需设置为 absolute | string | absolute \| fixed(默认) | +| right | 可选,按钮距离页面右边距,单位 px | number | 30(默认) | +| bottom | 可选,按钮距离页面底部位置,单位 px | number | 40(默认) | +| bgcolor | 可选,背景颜色 | string | \#9F9F9F(默认) | +| disabled | 可选,禁用点击返回顶部 | boolean | true \| false(默认) | + +::: + +::: title 事件 +::: + +::: table + +| 事件名 | 说明 | 回调参数 | +| ------ | ------------------------- | -------- | +| click | 点击回到顶部按钮的回调函数 | event | + +::: + +::: title 插槽 +::: + +::: table + +| 插槽名 | 说明 | +| ------ | ------------------ | +| — | 自定义内容 | + +::: diff --git a/example/src/layouts/Layout.vue b/example/src/layouts/Layout.vue index ccaf01ce..5b6c9e52 100644 --- a/example/src/layouts/Layout.vue +++ b/example/src/layouts/Layout.vue @@ -327,6 +327,11 @@ export default { title: '文字提示', subTitle: 'tooltip', path: '/zh-CN/components/tooltip', + }, { + id: 42, + title: '返回顶部', + subTitle: 'backtop', + path: '/zh-CN/components/backtop', }, ] diff --git a/example/src/router/zh-CN.ts b/example/src/router/zh-CN.ts index 8a546306..4b6b15a8 100644 --- a/example/src/router/zh-CN.ts +++ b/example/src/router/zh-CN.ts @@ -299,6 +299,10 @@ const zhCN = [ path: '/zh-CN/components/msg', component: () => import('../../docs/zh-CN/components/msg.md'), meta: { title: '信息' }, + },{ + path: '/zh-CN/components/backtop', + component: () => import('../../docs/zh-CN/components/backtop.md'), + meta: { title: '返回顶部' }, }, ], }, diff --git a/example/src/view/component.vue b/example/src/view/component.vue index 497332c6..b3f63311 100644 --- a/example/src/view/component.vue +++ b/example/src/view/component.vue @@ -158,6 +158,12 @@ export default { subTitle: 'dropdown', path: '/zh-CN/components/dropdown', }, + { + id: 42, + title: '返回顶部', + subTitle: 'backtop', + path: '/zh-CN/components/backtop', + }, ], }, { diff --git a/src/module/backTop/index.less b/src/module/backTop/index.less new file mode 100644 index 00000000..f6439693 --- /dev/null +++ b/src/module/backTop/index.less @@ -0,0 +1,21 @@ +/** backtop **/ +.lay-backtop { + position: fixed; + right: 15px; + bottom: 15px; + z-index: 999999; + width: 50px; + height: 50px; + line-height: 50px; + margin-bottom: 1px; + text-align: center; + cursor: pointer; + font-size: 40px; + background-color: #9f9f9f; + color: #fff; + border-radius: 2px; + opacity: 0.95; + :hover { + opacity: 0.85; + } +} diff --git a/src/module/backTop/index.vue b/src/module/backTop/index.vue index 1b70744b..c23fc1e8 100644 --- a/src/module/backTop/index.vue +++ b/src/module/backTop/index.vue @@ -1,14 +1,119 @@ \ No newline at end of file +import { defineProps, defineEmits, ref, shallowRef, withDefaults, computed, onMounted, } from 'vue'; +import layIcon from '../icon/index'; +import './index.less'; + +export interface LayBacktopProps { + target?: string; // 触发滚动的对象 + showHeight?: number; + position?: 'fixed' | 'absolute'; // 定位方式,显示在特定容器内部需要设置为 absolute + right?: number; + bottom?: number; + bgcolor?: string; + disabled?: boolean; // 禁用返回顶部 +} + +const props = withDefaults(defineProps(), { + target: 'window', + showHeight: 200, + position: 'fixed', + right: 30, + bottom: 40, + bgcolor: '#9F9F9F', + disabled: false, +}); + +const emit = defineEmits(['click']); + +let visible = ref(props.showHeight === 0); +const backtopRef = ref(null); +const scrollTarget = shallowRef(undefined); + +const scrollToTop = () => { + if (!scrollTarget.value) return; + if (scrollTarget.value instanceof Window) { + window.scrollTo({ + top: 0, + left: 0, + behavior: 'smooth' //smooth(平滑滚动),instant(瞬间滚动),默认instant + }); + } else { + // FIXME 初版待改进 + let step = scrollTarget.value.scrollTop / 4; + if (scrollTarget.value.scrollTop > 0) { + scrollTarget.value.scrollTop -= Math.max(step, 10); + setTimeout(() => { + scrollToTop(); + }, 1000 / 60); + } + } +}; + +const handleScroll = () => { + if (!scrollTarget.value) return; + const scrollTop = scrollTarget.value instanceof Window ? window.pageYOffset : scrollTarget.value.scrollTop; + visible.value = scrollTop >= props.showHeight; +}; + +const handleClick = (event: MouseEvent) => { + if (!props.disabled) { + scrollToTop() + }; + emit('click', event); +}; + +// 获取滚动目标元素 +const getScrollTarget = () => { + if (props.target === 'window') { + return window || document.documentElement || document.body; + } else { + const targetElement = document.querySelector(props.target); + if (!targetElement) throw new Error(`target is not existed: ${props.target}`); + // 特定容器内部显示 + if (props.position === 'absolute') { + if (!targetElement.parentElement) throw new Error(`target parent element is not existed: ${props.target}`); + targetElement.parentElement.style.position = 'relative'; + console.log(backtopRef.value); + if (!backtopRef.value) throw new Error(`target ref is null: ${props.target}`); + backtopRef.value.style.position = props.position; + } + return targetElement; + } +}; + +onMounted(() => { + let timer: any = undefined; + scrollTarget.value = getScrollTarget(); + // FIXME 节流待改进 + scrollTarget.value.addEventListener('scroll', () => { + clearTimeout(timer); + timer = setTimeout(() => { + handleScroll(); + }, 100); + }); +}); +