vue--实现组件吸顶交互效果

1,026 阅读2分钟

吸顶效果是web开发中一种常见的交互方式,常见的应用场景有导航、搜索框等等。

吸顶元素的初始位置一般靠近页面顶部,但与顶部有一定距离,这块区域放的是最醒目的元素,页面向下滚动超过吸顶元素初始位置时,把吸顶元素固定在顶部

要求吸顶的元素一般是二级导航栏、搜索框、文章标题栏、表头、tab条等等,共同特点是在内容或功能上比较重要,但又不是最重要的元素

这篇文章以vue3顶部导航栏做分析,vue2、jquery、纯js实现需自行变通一下哈,但关键部分已写出

实现方法: 通过滚动事件的触发,判断当前滚动距离是否到达吸顶距离,如果大于则添加吸顶类名,否则移除类名

准备

如果吸顶导航栏与顶部导航栏不一致,需要准备两个导航栏组件,如果一致,则只需准备一个导航栏组件则可

两个导航栏一致

导航栏组件代码,注意有坑!!!!,在代码中已标出,已有解决方案

<script setup>
    import {ref} from 'vue';
    // vueUse 实现 第三方插件,自行下载
    import { useScroll } from '@vueuse/core'
    const { y } = useScroll(window);

    // //纯js获取滚动距离
    // const y = ref(null);
    // //注册滚动事件
    // window.onscroll = function(){
    //     y.value = window.scrollY;
    // };

    // // jquery实现
    // $(function() {
    //     $(window).scroll(function() {
    //         console.info($(window).scrollTop());
    //     });
    // });


</script>

<template>
<!-- //坑2:滚动到临界位置的时候,页面抖了一下,向上缩了一截。因为`fixed`定位元素脱离文档流,下面的元素上来,所以页面抖了一下,解决方法是添加一个占位元素 -->
    <div style="height:78px;width:100px;" v-if="y>78"></div>
    <div class="app-header-sticky" :class="{ show: y > 78 }">
        <div class="container">
            <font-icon :class="'icon-xingxiansg'" :color="'color'" :size="'size'"></font-icon>
        </div>
    </div>
</template>


<style scoped lang='scss'>
    .app-header-sticky {
        width: 100%;
        height: 80px;
        //超出滚动距离,添加定位
        //&表示继承父级样式,
        //坑1:这里的父级样式是在style上,不是元素标签哈,比如,这里&表示的是.app-header-sticky
        &.show {
            position: fixed;
            left: 0;
            top: 0;
            z-index: 999;
        }

        .container {
            display: flex;
            align-items: center;
            .color {color:red;}
            .size {font-size:60px;}
        }
    }
</style>

页面index.vue码:

<script setup>
    import nav from './components/nav.vue'
</script>

<template>
    <!-- 导航栏  -->
    <nav  />
</template>

两个导航栏不一致

原有导航栏不做任何操作,以下是吸顶导航栏操作

<script setup>
    import {ref} from 'vue';
    // vueUse 实现 vueuse是第三方工具,需自行下载哈
    import { useScroll } from '@vueuse/core'
    const { y } = useScroll(window);

    // //纯js获取滚动距离
    // const y = ref(null);
    // //注册滚动事件
    // window.onscroll = function(){
    //     y.value = window.scrollY;
    // };

    // // jquery获取滚动距离
    // $(function() {
    //     $(window).scroll(function() {
    //         console.info($(window).scrollTop());
    //     });
    // });


</script>

<template>
    //如果滚动距离大于78,则添加新类名show
    <div class="app-header-sticky" :class="{ show: y > 78 }">
        <div class="container">
            <font-icon :class="'icon-xingxiansg'" :color="'color'" :size="'size'"></font-icon>
        </div>
    </div>
</template>


<style scoped lang='scss'>
    //先隐藏,后显现
    .app-header-sticky {
        width: 100%;
        height: 80px;
        position: fixed;
        left: 0;
        top: 0;
        z-index: 999;
        background-color: #fff;
        border-bottom: 1px solid #e4e4e4;
        // 此处为关键样式!!!
        // 状态一:往上平移自身高度 + 完全透明  ---方便实现渐变效果,更加平滑
        transform: translateY(-100%);
        opacity: 0;

        // 状态二:移除平移 + 完全不透明
        //&表示继承父级样式,
        //坑:这里的父级样式是在style上,不是元素标签哈,比如,这里&表示的是.app-header-sticky
        &.show {
            transition: all 0.3s linear;
            transform: none;
            opacity: 1;
        }

        .container {
            display: flex;
            align-items: center;
            .color {color:red;}
            .size {font-size:60px;}
        }
        
    }
</style>

页面index.vue代码:

<script setup>
    //原有导航栏
    import nav from './components/nav.vue'
    //吸顶导航栏
    import fixed from './components/fixed.vue'
</script>

<template>
    <nav />
    <fixed />
</template>

ios不完整方案

以上两种方案在移动IOS是行不通的,吸顶定位方法的改为position:sticky; 吸顶时Css写法:

    &.show {
        position: sticky;
        left: 0;
        top: 0;
        z-index: 999;
    }

虽然将定位方式做了更改,但IOS还是存在一些问题,这些问题是因为 IOS对scroll做了很大的限制:

手指划动屏幕 -> 滚动 -> 手指抬起 -> 惯性滚动 -> 停止滚动

整个过程,直到停止滚动时才会触发1次scroll事件,也就是说,IOS8以下的scroll变成了scrollend。监听滚动判断位置的方法完全失效,平滑吸顶效果变成了滚过临界位置直到停止滚动时,吸顶元素跳到目标位置,无法实时获取吸顶状态,体验非常差,position:sticky;也无法解决这个问题

目前笔者还没有找到解决方案,如有新思路、好点子,或者成熟方案,麻烦告知,感激不尽