Merge branch 'next' of https://gitee.com/layui/layui-vue into next
This commit is contained in:
commit
edad4c7269
5
package/component/src/component/affix/index.less
Normal file
5
package/component/src/component/affix/index.less
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.lay-affix-content{
|
||||||
|
display: block;
|
||||||
|
z-index: 999;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
5
package/component/src/component/affix/index.ts
Normal file
5
package/component/src/component/affix/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { withInstall, WithInstallType } from "../../utils";
|
||||||
|
import Component from "./index.vue";
|
||||||
|
|
||||||
|
const component: WithInstallType<typeof Component> = withInstall(Component);
|
||||||
|
export default component;
|
127
package/component/src/component/affix/index.vue
Normal file
127
package/component/src/component/affix/index.vue
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<div class="lay-affix-content" :style="getStyle" ref="dom">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "LayAffix",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import "./index.less";
|
||||||
|
import { ref, onMounted, onUnmounted, nextTick, computed } from "vue";
|
||||||
|
export interface LayAiffxProps {
|
||||||
|
top?: number;
|
||||||
|
bottom?: number;
|
||||||
|
target?: HTMLElement;
|
||||||
|
}
|
||||||
|
const props = withDefaults(defineProps<LayAiffxProps>(), {
|
||||||
|
top: 0,
|
||||||
|
target: () => {
|
||||||
|
return document.body;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const outWindow = ref(false);
|
||||||
|
const affixTop = ref(false);
|
||||||
|
const dom = ref();
|
||||||
|
|
||||||
|
let changeScrollTop = 0;
|
||||||
|
let orginOffsetLeft = 0;
|
||||||
|
let marginLeft = 0;
|
||||||
|
let marginTop = 0;
|
||||||
|
let fixedTop = 0;
|
||||||
|
let fixedBottom = 0;
|
||||||
|
const getStyle = computed(() => {
|
||||||
|
if (outWindow.value && dom.value) {
|
||||||
|
let style = {
|
||||||
|
position: "fixed !important",
|
||||||
|
top: "unset",
|
||||||
|
bottom: "unset",
|
||||||
|
left: orginOffsetLeft - marginLeft + 'px',
|
||||||
|
};
|
||||||
|
if (affixTop.value) {
|
||||||
|
style.top = fixedTop - marginTop + "px";
|
||||||
|
return style;
|
||||||
|
} else {
|
||||||
|
if (props.hasOwnProperty('bottom')) {
|
||||||
|
style.bottom = props.bottom + "px";
|
||||||
|
return style;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//检查是否在窗口内
|
||||||
|
const checkInWindow = () => {
|
||||||
|
if (dom.value) {
|
||||||
|
let offsetTop = dom.value.offsetTop;
|
||||||
|
let scrollTop = props.target?.scrollTop;
|
||||||
|
|
||||||
|
if (typeof props.bottom === 'undefined') {
|
||||||
|
//top检查 当前元素与容器顶部距离-减去滚动条的高度+容器offsetTop
|
||||||
|
let result = offsetTop - scrollTop + props.target.offsetTop;
|
||||||
|
if (result < fixedTop) {
|
||||||
|
if (outWindow.value) {
|
||||||
|
if (scrollTop <= changeScrollTop) {
|
||||||
|
outWindow.value = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
changeScrollTop = scrollTop
|
||||||
|
outWindow.value = true;
|
||||||
|
affixTop.value = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//bottom检查 可视区窗口高度 + 文档滚动高度 - 当前元素与容器顶部距离 - 当前元素高度
|
||||||
|
let viewHeight = props.target.offsetHeight > window.innerHeight ? window.innerHeight : props.target.offsetHeight
|
||||||
|
let result = viewHeight + scrollTop - offsetTop - dom.value.offsetHeight;
|
||||||
|
if (outWindow.value) {
|
||||||
|
if (scrollTop >= changeScrollTop) {
|
||||||
|
if (props.bottom == 0) {
|
||||||
|
console.log(scrollTop)
|
||||||
|
}
|
||||||
|
outWindow.value = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (result < fixedBottom) {
|
||||||
|
changeScrollTop = scrollTop - result + props.bottom
|
||||||
|
console.log(changeScrollTop)
|
||||||
|
outWindow.value = true;
|
||||||
|
affixTop.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDomStyle = (dom: any, attr: string) => {
|
||||||
|
if (dom.currentStyle) {
|
||||||
|
return dom.currentStyle[attr];
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
return document.defaultView.getComputedStyle(dom, null)[attr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
orginOffsetLeft = dom.value.getBoundingClientRect().left
|
||||||
|
fixedTop = props.top + props.target.offsetTop
|
||||||
|
if (typeof props.bottom !== 'undefined') {
|
||||||
|
fixedBottom = props.bottom + parseFloat(getDomStyle(dom.value, 'marginBottom'))
|
||||||
|
}
|
||||||
|
marginLeft = parseFloat(getDomStyle(dom.value, 'marginLeft'))
|
||||||
|
marginTop = parseFloat(getDomStyle(dom.value, 'marginTop'))
|
||||||
|
props.target.addEventListener("scroll", checkInWindow, true);
|
||||||
|
checkInWindow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
props.target.removeEventListener("scroll", checkInWindow);
|
||||||
|
});
|
||||||
|
</script>
|
@ -86,6 +86,7 @@ import LayRipple from "./component/ripple/index";
|
|||||||
import LayNoticeBar from "./component/noticeBar/index";
|
import LayNoticeBar from "./component/noticeBar/index";
|
||||||
import LayPageHeader from "./component/pageHeader/index";
|
import LayPageHeader from "./component/pageHeader/index";
|
||||||
import LayCascader from "./component/cascader/index";
|
import LayCascader from "./component/cascader/index";
|
||||||
|
import LayAffix from "./component/affix/index";
|
||||||
import LayConfigProvider from "./provider";
|
import LayConfigProvider from "./provider";
|
||||||
import { InstallOptions } from "./types";
|
import { InstallOptions } from "./types";
|
||||||
|
|
||||||
@ -170,6 +171,7 @@ const components: Record<string, Plugin> = {
|
|||||||
LayNoticeBar,
|
LayNoticeBar,
|
||||||
LayPageHeader,
|
LayPageHeader,
|
||||||
LayCascader,
|
LayCascader,
|
||||||
|
LayAffix,
|
||||||
};
|
};
|
||||||
|
|
||||||
const install = (app: App, options?: InstallOptions): void => {
|
const install = (app: App, options?: InstallOptions): void => {
|
||||||
@ -261,6 +263,7 @@ export {
|
|||||||
LayNoticeBar,
|
LayNoticeBar,
|
||||||
LayPageHeader,
|
LayPageHeader,
|
||||||
LayCascader,
|
LayCascader,
|
||||||
|
LayAffix,
|
||||||
install,
|
install,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
::: anchor
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: title 基本介绍
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: describe 使用锚点,可以将内容固定在容器内,并且不随容器的滚动而滚动,常用于侧边菜单导航等。
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: title 基础使用
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: demo 使用 `lay-affix` 标签, 创建锚点,`target` 属性用于需要监听其滚动事件的元素,默认为 `document.body` ,`top`设置距离容器顶部偏移量
|
||||||
|
<template>
|
||||||
|
<div style="width:100%;height:200px">
|
||||||
|
<lay-affix :target="target" :top="0" v-if="target">
|
||||||
|
<lay-button type="normal">固定在最顶部</lay-button>
|
||||||
|
</lay-affix>
|
||||||
|
<lay-affix :target="target" :top="38" v-if="target" style="margin-left:150px;">
|
||||||
|
<lay-button type="normal">固定在距离顶部38px</lay-button>
|
||||||
|
</lay-affix>
|
||||||
|
<lay-affix :target="target" :top="76" v-if="target" style="margin-left:350px">
|
||||||
|
<lay-button type="normal">固定在距离顶部76px</lay-button>
|
||||||
|
</lay-affix>
|
||||||
|
<lay-affix :target="target" :top="114" v-if="target" style="margin-left:550px">
|
||||||
|
<lay-button type="normal">固定在距离顶部114px</lay-button>
|
||||||
|
</lay-affix>
|
||||||
|
<lay-affix :target="target" :top="152" v-if="target" style="margin-left:750px">
|
||||||
|
<lay-button type="normal">固定在距离顶部152px</lay-button>
|
||||||
|
</lay-affix>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { nextTick,ref } from 'vue'
|
||||||
|
const target=ref()
|
||||||
|
nextTick(()=>{
|
||||||
|
target.value=document.querySelector(".layui-body");
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: title 固定在最底部
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: demo 使用 `bottom` 属性, 设置距离容器底部偏移量
|
||||||
|
<template>
|
||||||
|
<div style="width:100%;height:100px">
|
||||||
|
<lay-affix :target="target" :bottom="0" v-if="target">
|
||||||
|
<lay-button type="normal">固定在最底部</lay-button>
|
||||||
|
</lay-affix>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { nextTick,ref } from 'vue'
|
||||||
|
const target=ref()
|
||||||
|
nextTick(()=>{
|
||||||
|
target.value=document.querySelector(".layui-body");
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
::: title Aiffx 属性
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: table
|
||||||
|
|
||||||
|
| 属性 | 描述 | 可选值 |
|
||||||
|
| ------ | ---- | -------------- |
|
||||||
|
| top | 顶部偏移量 : number | 0 |
|
||||||
|
| bottom | 底部偏移量 : number,优先级大于`top` | - |
|
||||||
|
| target | 指定参考容器 : HTMLElement | 默认`document.body`,请务必确保能够正确获取到dom|
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: contributor affix
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: previousNext affix
|
||||||
|
:::
|
@ -404,6 +404,11 @@ const zhCN = [
|
|||||||
component: () => import("../document/zh-CN/components/cascader.md"),
|
component: () => import("../document/zh-CN/components/cascader.md"),
|
||||||
meta: { title: "级联选择器" },
|
meta: { title: "级联选择器" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/zh-CN/components/Affix",
|
||||||
|
component: () => import("../document/zh-CN/components/affix.md"),
|
||||||
|
meta: { title: "锚点" },
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -369,6 +369,12 @@ const menus = [
|
|||||||
subTitle: "noticeBar",
|
subTitle: "noticeBar",
|
||||||
path: "/zh-CN/components/noticeBar",
|
path: "/zh-CN/components/noticeBar",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 100,
|
||||||
|
title: "锚点",
|
||||||
|
subTitle: "Affix",
|
||||||
|
path: "/zh-CN/components/affix",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user