box-shadow 巧解横向滑动列表的遮罩层穿透问题

779 阅读4分钟

最近小组遇到一个设计实现的一个问题,大概就是一个横向滑动列表,在最右侧实现一个遮罩层然后让元素从底部溜入的场景,然后点击这个遮罩层穿透到对应的列表元素上去。

其实一般思路就是借助一个元素来实现遮罩效果或者稍微优化一些,使用伪类,但是他们都会拦截鼠标的交互,无法将交互应用到遮罩层下方的列表元素上,这也是这个问题的难点所在。

这个项目我没有参与,但是在开会的时候我们组长将问题抛出了,他们也是花了一些时间来解决,但最终解决的方法使用了css的 touch-action、 cursor-event 等不常用的属性(大家可以自行尝试),组长貌似也是觉得这个解决方案不够普遍、优雅,所以也就抛出来向大家征求有没有更好的方案,但是当时大家似乎也没啥好的建议。

回到家后,刚好我手头上有一本《css揭秘》,我就想看看有没有这个场景的解决方法,还真让我找到了,而且还非常简单,核心就是一句话, ** box-shadow 只是能引起视觉上的效果 无法阻止鼠标交互 **

所以问题就豁然开朗了,只要借助box-shadow来实现视觉上的遮罩就ok了。

下面我们来实际操作一下:

首先将场景UI生成:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <style>
    ul {
      width: 200px;
      height: 50px;
      list-style: none;
      overflow-x: auto;
      white-space: nowrap;
      background-color: #ccc;
    }
    
    ul::-webkit-scrollbar {
      display: none;
    }


    ul>li {
      margin-left: 10px;
      display: inline-block;
      border-bottom: 5px solid red;
    }
  </style>
</head>

<body>
  <ul id="list">
    <li>aaaa</li>
    <li>bbbb</li>
    <li>cccc</li>
    <li>dddd</li>
    <li>eeee</li>
    <li>ffff</li>
    <li>gggg</li>
    <li>hhhh</li>
    <li>iiii</li>
    <li>jjjj</li>
  </ul>
  <script>
    const ul = document.querySelector("#list")
    ul.addEventListener('click', (e) => {
      const item = e.target;
      console.log(item);
    }, true)
  </script>
</body>

</html>

效果如下:

并且我们点击对应元素,就会在控制台里面打印出来。

然后我们借助伪元素来添加遮罩层,其实这里我也踩了一些坑,我把我踩过的坑先记录一下,然后过渡到最终的结果

首先大方向是借助box-shadow 而且是伪元素的box-shadow,所以第一次我是这么写的,我们暂且称之为1.0


// 1.0

    ul::before {
      position: fixed;
      width: 50px;
      height: 30px;
      left: 198px;
      content: '';
      box-shadow: inset 50px 30px red;
    }

效果如下,我们用红色来标识伪元素

我们采用了inset box-shadow,看上去基本没啥问题,可是当我们点击上去之后,发现控制台打印的是

是当前伪元素ul,而并非伪元素下面的li,所以虽然我们给伪元素着色的方式是采用了box-shadow,但是其实还是在伪元素这个元素本身上面做文章,所以我们点击的还是元素,并非box-shadow,所以我们再次明确一下我们的目标: 借助伪元素来实现遮罩,并且使用box-shadow来实现遮罩效果,而且点击一定要点击到box-shadow上,

所以我们就不能使用inset,就正常让box-shadow脱离元素就好了,所以2.0应运而生,为了让大家看的更清楚,先给出2.0.1:

我们用蓝色来标注伪元素,用红色来标注阴影

// 2.0.1
ul::before {
      position: fixed;
      width: 50px;
      height: 30px;
      left: 0;
      content: '';
      background-color: blue;
      box-shadow: 50px 0 red;
    }

然后我们接下来要做的其实就是让伪元素本身“消失”,只留下红色的阴影部分,

切记不能使用display:none visibility:hidden opacity:0这三种方法,因为阴影是相对于他的元素的,元素消失不见,阴影也就随之而去了

其实到这一步解决方法也就多种多样了,我们可以分别设置位移:将蓝色移出视口外,将红色阴影移到最右侧


// 2.0.2

ul::before {
      position: fixed;
      width: 50px;
      height: 30px;
      left: -50px;
      content: '';
      background-color: blue;
      box-shadow: 248px 0 red;
    }

效果如下:

我们点击红色阴影后 成功穿透到了下面的自元素

也可以不用移动伪元素,将伪元素本身设置为透明

 ul::before {
      position: fixed;
      width: 50px;
      height: 30px;
      content: '';
      background-color: transparent;
      box-shadow: 150px 0 red;
    }

到这里基本上可以解决问题了,而且也不复杂,但其实还可以优化,最后,给出我的终极解决方案:利用box-shadow可以设置多个阴影,结合我们1.0没有成功的inset来实现

ul::before {
      position: fixed;
      width: 50px;
      height: 30px;
      content: '';
      box-shadow: inset 50px 30px transparent,
        150px 0 2px red;
    }

写在后面,前端技术日新月异,更新换代,日积月累,量变质变。