老板让我实现模块锚点定位还要闪烁突出!

6,911 阅读4分钟

需求简介

自从公司裁员后,每个人手上的活而越来越多,老板给了我个需求,让我尽快实现,不然立马滚蛋

我大脑飞速思索,1秒后终于反应过来,不就是个滚动条滚动css吗,还想辞退我,做梦!不能惯着!

技术方案

技术选型

首先,很快啊,我就实现了静态样式

然后要考虑的就是如何实现点击锚点界面能滚动到对应的模块,最简单的就是scrollIntoView方法。

Element 接口的 scrollIntoView() 方法会滚动元素的父容器,使被调用 scrollIntoView() 的元素对用户可见。

至于闪烁突出,那就当元素被选中时,给它动态添加一个类名,写写css动画就行。

以vue为例,我具体展示下实现过程。

实现滚动

点击导航栏的得锚点时,我们可以使用wacth监听到是哪个模块被点击了,然后执行滚动逻辑

<template>
    <div class="card-content" >
        <!-- 模块内容 -->
    </div>
</template>

<script setup lang="ts">
    // 监听被点击的模块编码,执行滚动逻辑
    watch(() => scrollModuleCode, (v) => {
        // 滚动逻辑
    })
</script>

我们先看看scrollIntoView的简单用法

Element.scrollIntoView()

Element 接口的 scrollIntoView() 方法会滚动元素的父容器,使被调用 scrollIntoView() 的元素对用户可见。

语法

scrollIntoView(scrollIntoViewOptions)

使用示例

const element = document.getElementById("box");

element.scrollIntoView();
element.scrollIntoView({ block: "end" });
element.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });

属性参数释义
behavior (可选)

定义滚动是立即的还是平滑的动画。该选项是一个字符串,必须采用以下值之一:

  • smooth:滚动应该是平滑的动画。
  • instant:滚动应该通过一次跳跃立刻发生。
  • auto:滚动行为由 scroll-behavior 的计算值决定。

滚动实现

结合scrollIntoView的用法,要实现滚动就非常容易了

<template>
    <div class="card-content" ref="scrollRef">
        <!-- 模块内容 -->
    </div>
</template>

<script setup lang="ts">
    const scrollRef = ref()

    // 监听被点击的模块编码,执行滚动逻辑
    watch(() => scrollModuleCode, (v) => {
        // 滚动逻辑
        nextTick(() => {
            scrollRef.value.scrollIntoView({ behavior: "smooth", block: "start" });
        })
    })
</script>

上述代码中,我们使用scrollRef绑定了元素的dom,当元素被点击时,实现了滚动。看看效果

不错,基本功能实现了,现在我们增加下样式。

闪烁突出实现

要实现闪烁动画,我们可以再模块被点击时,动态添加一个类名,然后给这个类写样式即可。

<template>
    <div class="card-content" ref="scrollRef" :class="{ 'stroke-flash': flashActive }">
        <!-- 模块内容 -->
    </div>
</template>

<script setup lang="ts">
    const scrollRef = ref()
    const flashActive = ref(false)
    // 监听被点击的模块编码,执行滚动逻辑
    watch(() => scrollModuleCode, (v) => {
        // 开启动画
        flashActive.value = true
        // 滚动逻辑
        nextTick(() => {
            scrollRef.value.scrollIntoView({ behavior: "smooth", block: "start" });
        })
        // 关闭动画
        setTimeout(() => {
            flashActive.value = false
        }, 2200)
    })
</script>
<style>
.stroke-flash {

}
</style>

上述代码中,当模块被点击时,flashActive值变为true,组件的stroke-flash类名被激活,2.2s后,flashActive值变为fasle,类名消失,动画结束。

那么样式如何实现呢?突出闪烁,其实就是给组件加个蓝色的边框,然后使用动画属性改变其透明度。

@keyframes strokeFlash {

    0%,
    100% {
        border-color: transparent;
    }

    50% {
        border: 3px solid #246FE5;
    }
}

.stroke-flash {
    /* 动画名称、动画时长、缓入缓出效果、循环次数 */
    border-radius: 12px;
    animation: strokeFlash 1100ms ease-in-out 2;
}

我们看看效果

nice!

仔细观察界面,其实有个非常难受的地方,每次滚动,元素总是滚动到了浏览器视窗的顶部,导致有一部分被导航栏遮住了,非常难受!这要是给老板交付,搞不好直接被裁了!

滚动位置优化

一个很难受的地方就是scrollIntoView()不支持传元素滚动到浏览器顶部的距离,这导致每次滚动元素都定位到了浏览器顶部。比如,我们更希望点击【控制失调空间】时,界面是这样

而不是直接滚动到浏览器顶部

要解决这个问题,其实很简单!注意看,我给每个模块顶部一定高度写了个专门用于定位的div。

<template>
    <div class="card-content":class="{ 'stroke-flash': flashActive }">
        <div class="anchor" ref="scrollRef"></div>
        <!-- 模块内容 -->
    </div>
</template>

<script setup lang="ts">
    const scrollRef = ref()
    const flashActive = ref(false)
    // 监听被点击的模块编码,执行滚动逻辑
    watch(() => scrollModuleCode, (v) => {
        // 开启动画
        flashActive.value = true
        // 滚动逻辑
        nextTick(() => {
            scrollRef.value.scrollIntoView({ behavior: "smooth", block: "start" });
        })
        // 关闭动画
        setTimeout(() => {
            flashActive.value = false
        }, 2200)
    })
</script>
<style>
.card-content {
    position: relative;
    .anchor {
        top: -177px;
        left: 0;
        width: 20px;
        height: 5px;
        position: absolute;
        background: red;
    }
}
</style>

由于【控制失调空间】模块的定位元素在浏览器的顶部,滚动时,位置就如同我们想象的那样了!

非常丝滑!现在我么把这个红色的锚点隐藏起来就行!

<style>
.card-content {
    position: relative;
    overflow: hidden;
    .anchor {
        top: -177px;
        left: 0;
        width: 10px;
        height: 1px;
        position: absolute;
        background: red;
        opacity: 0;
    }
}
</style>

再试试

这么丝滑,老板肯定不会辞退我了!不说了,去交差了!

写在最后

这个锚点定位+闪烁突出实现的方案比较简单和粗糙,时间紧,任务重,我也没想到很好的方案。。。如果有更好的实现方式,请大家评论区告诉我!我去和老板对线!