纯css鼠标移入移出效果

1,938 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

前言

今天写一个纯css鼠标移入移出效果,从div四个不同方向移入元素时,一块蒙板可以从对应方向滑入元素中,从div移出元素时,蒙板可以从对应方向滑出屏幕。

效果图如下:

Kapture 2022-12-08 at 14.52.59.gif

解题思路

需要思考的最核心问题是如何判断鼠标从哪个方向移入?如果是用js的话我们很容易就可以通过监听mouseMove事件获取到移入位置,那么用css应该如何判断?

最容易的方法是把div元素分成上下左右四个区块,根据移入的所在区块,获得到鼠标的移入方向:

image.png

一开始我的想法是将元素的宽度和高度为0时,设置元素的边框宽度就会形成四个三角形,但是四个元素堆叠在一起会遮挡住其他的元素,所以后面使用clip-path裁剪元素,保证不会互相覆盖。

<div class="left block"></div>
<div class="right block"></div>
<div class="top block"></div>
<div class="bottom block"></div>
.block {
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    z-index: 1;
    clip-path: var(--clip-path);
}

.top {
    --clip-path: polygon(0 0, 100% 0, 50% 50%);
    background-color: red;

}

.bottom {
    --clip-path: polygon(0 100%, 100% 100%, 50% 50%);
    background-color: yellow;
}

.left {
    --clip-path: polygon(0 0, 0 100%, 50% 50%);
    background-color: green;
}

.right {
    --clip-path: polygon(100% 0, 100% 100%, 50% 50%);
    background-color: blue;
}

当鼠标移入四个block区域时,我们只需要给蒙板元素分别加一个对应方向的移入动画即可。

.mask {
    width: 100%;
    height: 100%;
    position: absolute;
    left: -100%;
    top: 0;
    background-color: rgba(17, 89, 156, 0.8);
    --startX: 0;
    --startY: 0;
    --x: 0;
    --y: 0;
    animation: var(--animation-name) 0.3s linear;
    animation-fill-mode: forwards;
    color: white;
    font-size: 44px;
    display: flex;
    justify-content: center;
    align-items: center;
}
.top:not(.big):hover~.mask {
    --startX: 100%;
    --startY: -100%;
    --x: 100%;
    --y: 0;
    --animation-name: runner;
}

.bottom:not(.big):hover~.mask {
    --startX: 100%;
    --startY: 100%;
    --x: 100%;
    --y: 0;
    --animation-name: runner;
}

.left:not(.big):hover~.mask {
    --startX: 0;
    --startY: 0;
    --x: 100%;
    --y: 0;
    --animation-name: runner;
}

.right:not(.big):hover~.mask {
    --startX: 200%;
    --startY: 0;
    --x: 100%;
    --y: 0;
    --animation-name: runner;
}

@keyframes runner {
    from {
        transform: translateX(var(--startX)) translateY(var(--startY));
    }

    to {
        transform: translateX(var(--x)) translateY(var(--y));
    }
}

如果只是简单这样处理的话,还是会有问题,当鼠标快速的在元素内部移动时,会出现错乱,蒙板可能一会从上面下来,一会从左边出来,一会从右边出来,所以当移入某一个方向的块中之后,要挡住其他方向的块,让鼠标无法选中其他方向。

.block:not(.big):hover {
    clip-path: unset;
    z-index: 2;
}

当某一个方向块在hover状态下,取消裁剪属性,并且把该方向块的层级提高,挡住其他的元素块。

Kapture 2022-12-08 at 15.39.57.gif

到这里,移入效果基本上已经完成了,接下来是解决另外一个问题:移出的时候方向该如何判断?

同理,我们可以做一个溢出层,用来判断超出元素块之后的方向。

鼠标移出效果

在元素外画一个更大的方向块,元素类名为big

image.png

当鼠标移入对应的溢出方向块时,设置对应移出动画。

源代码可查看:

优化展示效果

完成上面的移入和移出效果后,我发现还是有一些动画不够丝滑的地方:

  • 溢出块太小,动画效果会卡顿;
  • 快速移动鼠标到页面底部时,动画效果不明显;

优化方案:

  • 当鼠标移动到某一个溢出块时,增加该溢出块大小,刚好覆盖住相邻元素的溢出块,此时还没进入到隔壁块,不会影响下一个块的动画,而且会延长移出动画时间。
  • 当鼠标移入底部空时添加一个蒙板下移的动画效果,蒙板不会突然消失,使动画更丝滑。
  • 适当缩短动画时间,这样比较容易欺骗眼睛,动画也会更加丝滑一些。

就是这么简单——Ending——