feat:初版实现基础功能,返回顶部,固定按钮,自定义内容,特定容器内部显示
This commit is contained in:
parent
eb296766f8
commit
91ccfc58bb
170
example/docs/zh-CN/components/backtop.md
Normal file
170
example/docs/zh-CN/components/backtop.md
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
::: title 基础使用
|
||||||
|
|
||||||
|
###### 回到顶部组件的默认样式,通过滑动来查看页面右下角的按钮。
|
||||||
|
|
||||||
|
:::
|
||||||
|
::: demo
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Lay-backtop target=".layui-body"></Lay-backtop>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::title 自定义
|
||||||
|
|
||||||
|
###### 回到顶部组件可自定义样式,限制宽高:50px \* 50px,<code>showHeight = 0</code> 将始终显示,<code>disabled</code> 属性可以禁用返回顶部效果。查看页面右下角的圆形按钮。
|
||||||
|
|
||||||
|
::: demo
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Lay-backtop target=".layui-body" :showHeight="0" :bottom="100" @click="handlerClick" disabled style="background-color:#FFFFFF">
|
||||||
|
<div style="
|
||||||
|
width:50px;
|
||||||
|
height:50px;
|
||||||
|
background-color: #5FB878;
|
||||||
|
color: #FFFFFF;
|
||||||
|
text-align: center;
|
||||||
|
border-radius:50%">
|
||||||
|
<lay-icon type="layui-icon-dialogue" size="30px"></lay-icon>
|
||||||
|
</div>
|
||||||
|
</Lay-backtop>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { layer } from "../../../../src/index.ts"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const handlerClick = () => {
|
||||||
|
layer.confirm("layui-vue 1.0.0 已经发布");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
handlerClick,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: title 滚动容器
|
||||||
|
|
||||||
|
###### 通过设置 <code>target</code> 和 <code>position="absolute"</code>参数 ,可对特定容器进行返回顶部操作
|
||||||
|
|
||||||
|
:::
|
||||||
|
::: demo
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 需要用一个 div 包裹滚动容器和 Lay-backtop 组件 -->
|
||||||
|
<div style="width:700px; height:300px;">
|
||||||
|
<div id="scrollContainer" style="overflow-y:auto; overflow-x:auto; width:700px; height:300px;">
|
||||||
|
<p v-for="(n,index) in 50" :key="n" style="height:40px;border-bottom:0.5px solid #5FB878;margin-bottom:10px;line-height:40px">
|
||||||
|
{{index + ". layui-vue , 基 于 vue 3.0 的 桌 面 端 组 件 库 , layui 的 另 一 种 呈 现 方 式"}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Lay-backtop target="#scrollContainer" :showHeight="100" :bottom="30" position="absolute"></Lay-backtop>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: title 结合 tooltip 组件使用
|
||||||
|
|
||||||
|
###### 可以和 lay-tooltip 组件搭配使用
|
||||||
|
|
||||||
|
:::
|
||||||
|
::: demo
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 需要用一个 div 包裹滚动容器和 Lay-backtop 组件 -->
|
||||||
|
<div style="width:700px; height:300px;">
|
||||||
|
<div id="scrollContainer2" style="overflow-y:auto; overflow-x:auto; width:700px; height:300px;">
|
||||||
|
<p v-for="(n,index) in 50" :key="n" style="height:40px;border-bottom:0.5px solid #5FB878;margin-bottom:10px;line-height:40px">
|
||||||
|
{{index + ". layui-vue , 基 于 vue 3.0 的 桌 面 端 组 件 库 , layui 的 另 一 种 呈 现 方 式"}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<lay-tooltip content="backtop" position="left">
|
||||||
|
<Lay-backtop target="#scrollContainer2" :showHeight="100" :bottom="30" position="absolute" style="border-radius: 50%;background-color: #5FB878;"></Lay-backtop>
|
||||||
|
</lay-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: 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
|
||||||
|
|
||||||
|
| 插槽名 | 说明 |
|
||||||
|
| ------ | ------------------ |
|
||||||
|
| — | 自定义内容 |
|
||||||
|
|
||||||
|
:::
|
@ -327,6 +327,11 @@ export default {
|
|||||||
title: '文字提示',
|
title: '文字提示',
|
||||||
subTitle: 'tooltip',
|
subTitle: 'tooltip',
|
||||||
path: '/zh-CN/components/tooltip',
|
path: '/zh-CN/components/tooltip',
|
||||||
|
}, {
|
||||||
|
id: 42,
|
||||||
|
title: '返回顶部',
|
||||||
|
subTitle: 'backtop',
|
||||||
|
path: '/zh-CN/components/backtop',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -299,6 +299,10 @@ const zhCN = [
|
|||||||
path: '/zh-CN/components/msg',
|
path: '/zh-CN/components/msg',
|
||||||
component: () => import('../../docs/zh-CN/components/msg.md'),
|
component: () => import('../../docs/zh-CN/components/msg.md'),
|
||||||
meta: { title: '信息' },
|
meta: { title: '信息' },
|
||||||
|
},{
|
||||||
|
path: '/zh-CN/components/backtop',
|
||||||
|
component: () => import('../../docs/zh-CN/components/backtop.md'),
|
||||||
|
meta: { title: '返回顶部' },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -158,6 +158,12 @@ export default {
|
|||||||
subTitle: 'dropdown',
|
subTitle: 'dropdown',
|
||||||
path: '/zh-CN/components/dropdown',
|
path: '/zh-CN/components/dropdown',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 42,
|
||||||
|
title: '返回顶部',
|
||||||
|
subTitle: 'backtop',
|
||||||
|
path: '/zh-CN/components/backtop',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
21
src/module/backTop/index.less
Normal file
21
src/module/backTop/index.less
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,119 @@
|
|||||||
<template>
|
<template>
|
||||||
<ul class="layui-fixbar">
|
<div
|
||||||
<li class="layui-icon layui-fixbar-top" style="display: list-item"></li>
|
v-show="visible"
|
||||||
</ul>
|
ref="backtopRef"
|
||||||
|
class="lay-backtop"
|
||||||
|
:style="{
|
||||||
|
right: `${props.right}px`,
|
||||||
|
bottom: `${props.bottom}px`,
|
||||||
|
backgroundColor: `${props.bgcolor}`
|
||||||
|
}"
|
||||||
|
@click.stop="handleClick"
|
||||||
|
>
|
||||||
|
<slot>
|
||||||
|
<lay-icon type="layui-icon-top" size="40px"></lay-icon>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: "LayBacktop"
|
name: 'LayBacktop',
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
</script>
|
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<LayBacktopProps>(), {
|
||||||
|
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<HTMLElement | null>(null);
|
||||||
|
const scrollTarget = shallowRef<Window | HTMLElement | undefined>(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<HTMLElement>(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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user