z自定义指令

This commit is contained in:
李志鹏
2026-05-14 14:23:32 +08:00
parent 2ab2466f3d
commit 25919e5b1b
8 changed files with 155 additions and 92 deletions

View File

@@ -1,25 +1,25 @@
<template>
<Header />
<MainHeader />
<RouterView />
<Footer />
<MainFooter />
<BackTop />
</template>
<script setup lang="ts">
import { RouterView } from 'vue-router'
import Header from './components/header.vue'
import Footer from './components/footer.vue'
import BackTop from "./components/back-top.vue";
import { RouterView } from "vue-router";
import MainHeader from "./components/main-header.vue";
import MainFooter from "./components/main-footer.vue";
import BackTop from "./components/back-top.vue";
</script>
<style scoped lang="less">
// .main {
// height: 100vh;
// width: 100vw;
// overflow-y: auto;
// overflow-x: hidden;
// position: relative;
// > .content{
// height: auto;
// }
// }
// .main {
// height: 100vh;
// width: 100vw;
// overflow-y: auto;
// overflow-x: hidden;
// position: relative;
// > .content{
// height: auto;
// }
// }
</style>

View File

@@ -1,9 +1,5 @@
<template>
<div
class="back-top"
@click="handleClick"
:class="[progress > 0 ? 'active' : 'hidden']"
>
<div class="back-top" @click="handleClick" v-scroll-progress="handleScroll">
<svg width="100%" height="100%" viewBox="-1 -1 102 102">
<path
d="M50,1 a49,49 0 0,1 0,98 a49,49 0 0,1 0,-98"
@@ -20,26 +16,17 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, h } from "vue";
import { ref, onMounted, onUnmounted } from "vue";
const max = ref(98 * Math.PI);
const progress = ref(0);
const handleScroll = () => {
const el = document.documentElement;
const num = el.scrollTop / (el.scrollHeight - el.clientHeight);
progress.value = max.value * num;
const handleScroll = (e: number) => {
progress.value = (e / 100) * max.value;
};
const handleClick = () => {
document.documentElement.scrollTo({ top: 0, behavior: "smooth" });
};
onMounted(() => {
handleScroll();
document.addEventListener("scroll", handleScroll);
});
onUnmounted(() => {
document.removeEventListener("scroll", handleScroll);
});
</script>
<style scoped>
<style scoped lang="less">
.back-top {
position: fixed;
right: 30px;
@@ -53,40 +40,43 @@
-webkit-box-shadow: inset 0 0 0 1px #e1e1e1;
box-shadow: inset 0 0 0 1px #e1e1e1;
z-index: 999;
}
.back-top svg {
width: 100%;
height: 100%;
}
.back-top path {
transition: stroke-dashoffset 10ms linear;
}
.back-top.active {
animation: active 0.2s linear both;
}
.back-top.hidden {
animation: hidden 0.2s linear both;
}
@keyframes active {
0% {
display: none;
opacity: 0;
transform: translateY(50px);
display: flex;
align-items: center;
justify-content: center;
> svg {
position: absolute;
width: 100%;
height: 100%;
> path {
transition: stroke-dashoffset 10ms linear;
}
}
100% {
opacity: 1;
transform: translateY(0);
animation: back-top-hidden 0.2s linear both;
&.active {
animation: back-top-active 0.2s linear both;
}
}
@keyframes hidden {
0% {
opacity: 1;
transform: translateY(0);
@keyframes back-top-active {
0% {
display: none;
opacity: 0;
transform: translateY(15px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
100% {
opacity: 0;
transform: translateY(50px);
display: none;
@keyframes back-top-hidden {
0% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(15px);
display: none;
}
}
}
</style>

View File

@@ -1,25 +0,0 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
//const props = defineProps({
//})
//const emit = defineEmits([
//])
let data = reactive({
})
onMounted(()=>{
})
onUnmounted(()=>{
})
defineExpose({})
const {} = toRefs(data);
</script>
<template>
<header class="header">
</header>
</template>
<style lang="less" scoped>
.header{
position: relative;
}
</style>

View File

@@ -0,0 +1,20 @@
<template>
<header class="main-header" v-scroll-progress></header>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";
</script>
<style lang="less" scoped>
.main-header {
position: fixed;
width: 100%;
height: 85px;
top: 0;
left: 0;
z-index: 10000;
transition: background-color 0.2s linear;
&.active {
background-color: #0a0a0a;
}
}
</style>

10
src/directives/index.ts Normal file
View File

@@ -0,0 +1,10 @@
import type { App } from 'vue'
export default {
install(app: App) {
const directivesList = import.meta.glob('./*.ts', { eager: true }) as any;
Object.keys(directivesList).forEach(key => {
app.directive(directivesList[key].default.name, directivesList[key].default);
});
}
};

View File

@@ -0,0 +1,63 @@
/**
* 监听滚动条 滚动进度变化
* v-scroll-progress.self="(p) => console.log(p)"
* @modifiers self 监听当前元素的滚动进度变化
* @param params 滚动进度回调函数
* @param p 滚动进度
*/
const rootMap = new Map()
export default {
name: 'scroll-progress',
mounted(el: HTMLElement, binding: any) {
const params = binding.value
const { self } = binding.modifiers
const paramsType = typeof params
const obj = {
onscroll: () => { },
el,
root: self ? el : document,
activeNum: 0,
};
if (paramsType === 'function') {
obj.onscroll = params
} else if (paramsType === 'object') {
if (params.onscroll) obj.onscroll = params.onscroll
if (params.GetRoot) obj.root = params.GetRoot()
if (params.hasOwnProperty('activeNum')) obj.activeNum = params.activeNum
}
requestAnimationFrame(() => handleScroll({ target: obj.root }))
if (rootMap.has(obj.root)) {
rootMap.get(obj.root).push(obj)
return
}
rootMap.set(obj.root, [obj])
obj.root.addEventListener('scroll', handleScroll)
},
beforeUnmount(el: HTMLElement, binding: any) {
rootMap.forEach((objs, root) => {
if (objs.some((v: any) => v.el === el)) {
objs = objs.filter((v_: any) => v_.el !== el)
rootMap.set(root, objs)
}
if (objs.length === 0) {
root.removeEventListener('scroll', handleScroll)
rootMap.delete(root)
}
})
},
};
function handleScroll(e: any) {
const target = e.target
const objs = rootMap.get(target)
if (!objs || objs.length === 0) return
const el = target === document ? document.documentElement : target as HTMLElement
const num = el.scrollTop / (el.scrollHeight - el.clientHeight);
const progress = Math.round(num * 100)
objs.forEach((obj: any) => {
obj?.onscroll(progress)
if (obj.el) {
let isActive = progress > obj.activeNum
obj.el.classList.toggle('active', isActive)
}
})
};

View File

@@ -2,8 +2,13 @@ import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
import { routes } from './routes'
import './style.css'
import directives from './directives/index'
// 注册指令
export const createApp = ViteSSG(App, {
routes,
base: import.meta.env.BASE_URL,
routes,
base: import.meta.env.BASE_URL,
}, ({ app }) => {
app.use(directives)
})