毛玻璃实现交友软件心动嘉宾模糊效果

线上效果图

思路分析

图片我们使用背景图片的方式插入

如果我们的图片大于容器的话

我们使用background-size: cover 进行比例缩放,覆盖整个容器

毛玻璃效果使用 backdrop-filter 来进行处理,值越大模糊程度越大

使用毛玻璃实现图片模糊效果

复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>使用毛玻璃实现图片模糊效果</title>
    <style>
      *{
        padding: 0;
        margin: 0;
      }
      /* 设置圆角的大小 */
      :root {
        --radius-value: 8px
      }
      body{
       margin: 400px;
      }
      .box{
        width: 200px;
        height: 280px;
        /* 引入图片 */
        background-image: url("./person1.png");
        /* 图片不平铺 */
        background-repeat: no-repeat;
        /* 图片按比例缩放,覆盖整个容器 */
        background-size: cover; 
        position: relative;
        border-radius: var(--radius-value);
      }
      .set-img-blur{
        width: 100%;
        height: 100%;
        /* 值越大模糊程度越大 */
        backdrop-filter: blur(10px);
        border-radius: 3%;
        border-radius: var(--radius-value);
      }
      .text-info{
        color: #fff;
        position: absolute;
        left: 10px;
        bottom: 10px;
        font-size: 13px;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="set-img-blur">
        <div class="text-info">
          <div>成都 | 28岁</div>
          <div> 研究生</div>
          <div>Ta看过你10次</div>
        </div>
      </div>
    </div>
  </body>
</html>

发现问题:控制台去掉backdrop-filter属性

没有想到吧,这么简单就实现了这个效果

但是机智的小伙伴沉思片刻就发现了一个问题

若是用户操作控制台去掉backdrop-filter属性

那不是就可以看见真实的图像了?

这样肯定是不得行的,产品肯定喊你小子改

好嘛,我只有老老实实的去解决这个问题

解决用户在控制台去掉backdrop-filter属性问题

方法1:禁用使用操作控制台,通过去监听用户的按键来进行处理

方法2: 使用 MutationObserver 方法来解决

MutationObserver 的简单介绍

用于监测DOM树的变化,它提供了一种异步的方式来监听DOM元素的增加、删除、属性变化、文本节点等变化。

通过MutationObserver,我们可以实时地知道到DOM的变化,并做出相应的操作。

首先我们要选择一个观察的元素对象。

然后通过 new MutationObserver 创建观察者对象,观察发生变化后触发回调函数 。

配置观察选项,这个非常重要,我们等会要详细去了解

最后通过 observer.observe传入需要观察的元素和配置选项

复制代码
// 第一步:选择目标节点
var targetElement = document.getElementById('domID');

// 回调函数
function callBackFun(mutationsList, observer){
 // mutationsList:一个数组,包含所有检测到的变化。
 // observer:此回调函数的 MutationObserver 实例。
}

// 第二步: 创建观察者对象,观察发生变化后触发回调函数 
let observer = new MutationObserver(callBackFun);

// 第三步:配置观察选项:
let configObj = { attributes: true, childList: true, characterData: true }

//第四步: 传入目标节点和观察选项
observer.observe(targetElement, configObj);

// 适当的时机,停止观察的方法
observer.disconnect();

配置观察选项

复制代码
const config = {
  attributes: true,        // 监听属性变化,等会详细介绍一下这个属性,场景:防样式篡改行为
  childList: true,         // 监听某个元素下的子节点变化(不包含目标节点),如:用户在控制台删除(新增)节点,场景:动态内容加载监控
  subtree: true,           // 监听目标节点自身 + 所有后代节点,单独配置{subtree:true}不会产生任何效果,
  attributeFilter: ['id'], // 只监听特定属性(此处为 id),  attributeFilter: ['data-*']监听 data-* 属性的变化
  characterData: true      // 监听目标节点及其所有后代节点的文本内容变化,场景:富文本编辑器内容追踪
};

attributes: true 的详细介绍

1,通过控制台去除了css行内样式的属性

2,通过js给元素新增了某个属性

如:ele.setAttribute()、ele.removeAttribute()、ele.id = 'newId'、ele.className = 'newClass'

3,通过 style 属性修改:如 element.style.color = 'red'。

特别提醒:内部样式表,如:类名{ }这种,在控制台去除样式属性是监听不到的。

subtree: true 的详细介绍

监听所有后代节点,单独配置 { subtree:true }不会产生任何效果。

因为 subtree 本身只是一个范围扩展标记,而不是独立的监听类型。

通常需要配合其他选项,如:attributes: true, 监听属性变化。

childList: true, 监听某个元素下的子节点变化(删除,新增)

关于 MutationObserver的回调

如果在同一时间,某个被监听的元素属性发生了1000次变化。

MutationObserver的回调函数会执行1000次嘛?

答:不会。只会执行一次。

MutationObserver的回调是异步执行的,并且会将多个变化合并到一个回调函数中去处理。

也就是说,如果在同一时间(或者同一事件循环中)发生了多次变化,这些变化会被收集起来。

然后一次性传递给回调函数,而不是每次变化都立即触发回调。

MutationObserver的使用场景

总体来讲:只要DOM发生变化,就可以使用MutationObserver。

具体的场景:echarts 容器大小发生变化。用户通过控制台更改了元素的属性。响应式网页。窗口大小发生变化的场景。

使用MutationObserver监听元素的属性是否发生变化

复制代码
<style>
...其他css不变...
.set-img-blur{
  border-radius: var(--radius-value);
}
...其他css不变...
</style>
<body>
  <div class="box" id="imgbox">
    <div class="set-img-blur" id="watchElementNode" style="backdrop-filter: blur(10px)">
      <div class="text-info">
        <div>成都 | 28岁</div>
        <div> 研究生</div>
        <div>Ta看过你10次</div>
      </div>
    </div>
  </div>
</body>
<script>
  // 获取目标元素
  const watchElementNode = document.getElementById('watchElementNode');
  // 声明观察对象
  let observer;
  // 设置配置
  const setConfig = { attributes: true }
  // 修改DOM重新调用
  function watchCallBack(element) {
    console.log('修改DOM重新调用' )
    // 先停止监听避免循环,造成内存异常
    observer.disconnect();
    //重新添加上这个属性
    watchElementNode.style.backdropFilter = 'blur(10px)';
    watchElementNode.style.width = '100%';
    watchElementNode.style.height = '100%';
    // 重新监听(使用异步避免错过中间修改)
    setTimeout(() => observer.observe(watchElementNode, setConfig), 0);
  }
  // 观察者
  function watchElement(){
    observer = new MutationObserver(watchCallBack)
    observer.observe(watchElementNode, setConfig)
  }
  watchElement()
</script>

又发现问题

我们的背景图是插入在 <div class="box" id="imgbox"> 这个元素上的

毛玻璃的效果是用在
<div class="set-img-blur" id="watchElementNode"> 这个节点上的。

如果用户删除watchElementNode这个节点的话,一样可以看到真实的图像。

怎么来解决这个问题呢?

监听 <div class="box" id="imgbox">这个元素的子节点是否有变化。

如果监听到发生变化了,给这个节点隐藏起来同时不影响文档布局

使用MutationObserver监听子节点是否发生变化

复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>使用毛玻璃实现图片模糊效果</title>
    <style>
      *{
        padding: 0;
        margin: 0;
      }
      /* 设置圆角的大小 */
      :root {
        --radius-value: 8px
      }
      body{
       margin: 400px;
      }
      .box{
        width: 200px;
        height: 280px;
        /* 引入图片 */
        background-image: url("./person1.png");
        /* 图片不平铺 */
        background-repeat: no-repeat;
        /* 图片按比例缩放,覆盖整个容器 */
        background-size: cover; 
        position: relative;
        border-radius: var(--radius-value);
      }
      .set-img-blur{
        border-radius: var(--radius-value);
      }
      .text-info{
        color: #fff;
        position: absolute;
        left: 10px;
        bottom: 10px;
        font-size: 13px;
      }
    </style>
  </head>
  <body>
    <div class="box" id="imgbox">
      <div class="set-img-blur" id="watchElementNode" style=" backdrop-filter: blur(10px); width: 100%;height: 100%;">
        <div class="text-info">
          <div>成都 | 28岁</div>
          <div> 研究生</div>
          <div>Ta看过你10次</div>
        </div>
      </div>
    </div>
  </body>

</html>
<script>
  // 获取目标元素
  const watchElementNode = document.getElementById('watchElementNode');
  // 声明观察对象
  let observer;
  // 设置配置
  const setConfig = { attributes: true }
  // 修改DOM重新调用
  function watchCallBack(element) {
    console.log('修改DOM重新调用' )
    // 先停止监听避免循环
    observer.disconnect();
    //重新添加上这个属性
    watchElementNode.style.backdropFilter = 'blur(10px)';
    watchElementNode.style.width = '100%';
    watchElementNode.style.height = '100%';
    // 重新监听(使用异步避免错过中间修改)
    setTimeout(() => observer.observe(watchElementNode, setConfig), 0);
  }
  // 观察者
  function watchElement(){
    observer = new MutationObserver(watchCallBack)
    observer.observe(watchElementNode, setConfig)
  }
  watchElement()

  // 监听 imgbox 以元素的子节点是否发生变化
  const imgBox = document.getElementById('imgbox');
  // 声明观察对象
  let observerImgBox;
  // 设置监听子节点变化以及属性
  const setConfig2 ={ childList: true, attributes: true }
  // 修改DOM后的回调函数
  function imgBoxNodeCallback(element) {
    console.log('imgBoxNode', element )
    // 先停止监听避免循环,造成内存异常
    observerImgBox.disconnect();
    // 隐藏这个元素,同时不影响布局
    imgBox.style.visibility = 'hidden';
    // 重新监听(使用异步避免错过中间修改)
    setTimeout(() => observerImgBox.observe(imgBox, setConfig2), 0);
  }
  // 观察者
  function watchImgBoxElement2(){
    observerImgBox = new MutationObserver(imgBoxNodeCallback)
    observerImgBox.observe(imgBox, setConfig2)
  }
  watchImgBoxElement2()
  </script>

可以优化一下吗?

有机制的小伙伴说:我们可以直接去监听 id = imgbox这会元素。

如果这个元素的子节点和子节点的属性发生变化了。

我们直接将这个 id = imgbox 隐藏起来。

这样就不需要写2个监听器了,大佬果然有点东西。

那我们就按照这个思路再来优化一下

监听子节点和子节点的属性是否发生变化
复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>使用毛玻璃实现图片模糊效果</title>
    <style>
      *{
        padding: 0;
        margin: 0;
      }
      /* 设置圆角的大小 */
      :root {
        --radius-value: 8px
      }
      body{
       margin: 400px;
      }
      .box{
        width: 200px;
        height: 280px;
        /* 引入图片 */
        background-image: url("./person1.png");
        /* 图片不平铺 */
        background-repeat: no-repeat;
        /* 图片按比例缩放,覆盖整个容器 */
        background-size: cover; 
        position: relative;
        border-radius: var(--radius-value);
      }
      .set-img-blur{
        border-radius: var(--radius-value);
      }
      .text-info{
        color: #fff;
        position: absolute;
        left: 10px;
        bottom: 10px;
        font-size: 13px;
      }
    </style>
  </head>
  <body>
    <div class="box" id="imgbox">
      <div class="set-img-blur" id="watchElementNode" style=" backdrop-filter: blur(10px); width: 100%;height: 100%;">
        <div class="text-info">
          <div>成都 | 28岁</div>
          <div> 研究生</div>
          <div>Ta看过你10次</div>
        </div>
      </div>
    </div>
  </body>
</html>
<script>
  // 获取需要监听 imgbox 节点
  const imgBox = document.getElementById('imgbox');
  // 声明观察对象
  let observerImgBox;
  // 监听子节点变化,监听属性变化, 监听所有后代节点的变化以及后代节点的属性
  const setConfig2 ={ childList: true, attributes: true, subtree: true,  }
  // 修改DOM后的回调函数
  function imgBoxNodeCallback(element) {
    console.log('imgBoxNode', element )
    // 先停止监听避免循环,造成内存异常
    observerImgBox.disconnect();
    // 隐藏这个元素,同时不影响布局
    imgBox.style.visibility = 'hidden';
    // 重新监听(使用异步避免错过中间修改)
    setTimeout(() => observerImgBox.observe(imgBox, setConfig2), 0);
  }
  // 观察者
  function watchImgBoxElement(){
    observerImgBox = new MutationObserver(imgBoxNodeCallback)
    observerImgBox.observe(imgBox, setConfig2)
  }
  watchImgBoxElement()
  </script>

尾声

如果你觉得我写的不错的话

请给我点个赞,收藏等于学会哦

对了,求大佬们推荐一个好用的gif录制软件