CSS 之 图片九宫格变幻效果

一、简介

​ 本篇博客用于讲解如何实现图片九宫格变幻的样式效果,将图片分为九块填充在3×3的的九宫格子元素中,并结合gridhovertransition等CSS属性,实现元素hover时,九宫格子元素合并为一张完整图片的动画效果。

​ 为了简化代码,demo中通过JS设置CSS变量的方法,优化了元素背景的设置过程,减少了代码的繁杂度。

​ 最后还结合js的点击事件实现了一个简易的点击拼图demo。

效果示意图:

二、具体实现

1、九宫格子元素显示部分图片,然后从外向内组合变幻成完整图片
页面效果:
实现思路:

​ ① 首先在一个父元素内,创建九个子元素,并通过display: grid;实现3×3的九宫格布局,使其聚集在父元素中间,此时子元素之间无间隙。

​ ② 给这九个子元素通过background的相关属性,分别设置各个子元素的背景图片为总图片的一部分(1/9),其聚集在一起时,正好能够组成一张完成的图片。

​ ③ 在设置子元素背景时,可以通过依次设置九个子元素的背景样式来实现,但是代码过于繁琐。为了优化代码,可以通过js获取相应元素,然后遍历获取到的元素设置CSS变量,最后再统一通过子级选择器+CSS变量的形式,统一设置元素背景。

​ ④ 设置好背景后,利用父元素grid布局的gap属性,统一设置子元素之间的间距,使九个子元素按照固定间距,形成九宫格布局。

​ ⑤ 父元素hover时,修改gap属性的值,使子元素再次聚集在一起,并通过transition实现缓动动画。

核心思路为:变化主体为父元素,子元素不改变,改变父元素控制间隙的属性。

具体代码:
html 复制代码
<style>
		.d1 {
      /* 设置grid布局 */
      display: grid;
      grid-template-columns: 100px 100px 100px;
      grid-template-rows: 100px 100px 100px;
      /* 设置子元素间隔 */
      gap: 20px;
      width: 340px;
      height: 340px;
      /* 设置子元素居中在父元素中心 */
      justify-content: center;
      align-content: center;
      cursor: pointer;
      /* 设置缓动动画 */
      transition: all 0.3s linear;
      border: 2px solid red;
    }
    /* hover时消除子元素间隔 */
    .d1:hover {
      gap: 0;
    }
    /* 统一设置子元素的背景 */
    .d1>div {
      background: url(../image/test.jpg) no-repeat;
      /* 设置背景图片大小 这个大小等于后面拼接成的完成图片的大小 */
      background-size: 300px 300px;
      /* 利用JS设置的CSS变量控制子元素的背景 */
      background-position: var(--bgx, 0) var(--bgy, 0);
    }
</style>

<div class="d1">
    <div class="d1-1"></div>
    <div class="d1-2"></div>
    <div class="d1-3"></div>
    <div class="d1-4"></div>
    <div class="d1-5"></div>
    <div class="d1-6"></div>
    <div class="d1-7"></div>
    <div class="d1-8"></div>
    <div class="d1-9"></div>
</div>

<script>
  	// 获取所有子元素
    const d1List = document.querySelectorAll('.d1 > div');
    // 遍历所有子元素
    d1List.forEach((item, index) => {
      // 计算当前子元素所在行 0~2
      const row = Math.floor(index / 3);
      // 计算当前子元素所在列 0~2
      const col = index % 3;
      console.log(row, col);
      // 根据子元素所在行和列 设置CSS变量 决定显示的背景图片的偏移量 从而决定背景图片的内容
      item.style.setProperty('--bgx', `${-col * 100}px`);
      item.style.setProperty('--bgy', `${-row * 100}px`);
    })
</script>
2、九宫格子元素显示部分图片,然后从内向外组合变幻成完整图片
页面效果:
实现思路:

​ ① 首先在一个父元素内,创建九个子元素,并通过display: grid;实现3×3的九宫格布局,初始设置子元素的宽高小于grid布局设置的单元格宽高,然后通过相关属性设置子元素在对应单元格内水平垂直居中。

​ ② 给这九个子元素通过background的相关属性,分别设置背景图片为总图片的一部分。

​ ③ 在设置子元素背景时,可以通过依次设置九个子元素的背景样式来实现,但是代码过于繁琐。为了优化代码,可以通过js获取相应元素,然后遍历获取到的元素设置CSS变量,最后再统一通过子级选择器+CSS变量的形式,统一设置元素背景,使九个子元素各自占据背景图片的一部分。但由于此时子元素的宽高小于grid布局设置的单元格宽高,所以此时子元素之间存在间隙,而且所有子元素组合拼接的背景图片并不完整。

​ ④ 设置父元素hover时,改变子元素的宽高,使其充满单元格宽高,承载的背景图片比例也相应变大,此时子元素之间紧密连接,并组合拼接为一张完整的图片。

核心思路为:变化主体为子元素,子元素改变宽高属性,父元素不变。

具体代码:
html 复制代码
<style>
    .d2 {
      /* 设置grid布局 */
      display: grid;
      grid-template-columns: 100px 100px 100px;
      grid-template-rows: 100px 100px 100px;
      width: 300px;
      height: 300px;
      cursor: pointer;
      border: 2px solid red;
    }

    .d2>div {
      /* 设置子元素的初始宽高 */
      width: 60px;
      height: 60px;
      /* 设置子元素位于grid布局网格的中心位置 */
      justify-self: center;
      align-self: center;
      /* 设置子元素缓动动画 */
      transition: all 0.3s linear;
      /* 设置子元素背景图片 */
      background: url(../image/test.jpg) no-repeat;
      /* 设置背景图片的大小 这个大小等于后面拼接成的完成图片的大小 */
      background-size: 300px 300px;
      /* 利用JS设置的CSS变量控制子元素的背景偏移 */
      background-position: var(--bgx, 0) var(--bgy, 0);
    }

    /* 父元素hover之后 修改子元素的宽高 使子元素能连接在一起 */
    .d2:hover div {
      width: 100px;
      height: 100px;
    }
</style>

<div class="d2">
    <div class="d2-1"></div>
    <div class="d2-2"></div>
    <div class="d2-3"></div>
    <div class="d2-4"></div>
    <div class="d2-5"></div>
    <div class="d2-6"></div>
    <div class="d2-7"></div>
    <div class="d2-8"></div>
    <div class="d2-9"></div>
</div>

<script>
  	// 获取所有子元素
    const d2List = document.querySelectorAll('.d2 > div');
    // 遍历所有子元素
    d2List.forEach((item, index) => {
      // 计算当前子元素所在行 0~2
      const row = Math.floor(index / 3);
      // 计算当前子元素所在列 0~2
      const col = index % 3;
      console.log(row, col);
      // 根据子元素所在行和列 设置CSS变量 决定显示的背景图片偏移量
      item.style.setProperty('--bgx', `${-col * 100}px`);
      item.style.setProperty('--bgy', `${-row * 100}px`);
    })
</script>
3、九宫格子元素显示完整小图,然后从外向内合并成一张完整大图
页面效果:
实现思路:

​ ① 首先在一个父元素内,创建九个子元素,并通过display: grid;实现3×3的九宫格布局,并且设置子元素之间存在固定间隙。

​ ② 给每一个子元素都设置背景图片,并且设置背景图片的大小等于子元素的大小,使背景图片在子元素中完整展示。

​ ③ 在父元素hover时,修改子元素之间的间隙为0,并且修改子元素的背景图片大小和背景图片的偏移量,使子元素只显示背景图片的一部分,并且所有子元素拼接起来时,能够构成一张完整的背景图片。

核心思路:同时改变父元素和子元素,改变父元素控制间隙的属性,改变子元素的背景属性。

具体代码:
html 复制代码
<style>
    .d4 {
      /* 设置grid布局 */
      display: grid;
      grid-template-columns: 100px 100px 100px;
      grid-template-rows: 100px 100px 100px;
      /* 设置子元素之间的间隙 */
      gap: 20px;
      width: 340px;
      height: 340px;
      /* 设置子元素整体内容区域相对于父元素水平垂直居中 */
      justify-content: center;
      align-content: center;
      cursor: pointer;
      /* 设置父元素缓动动画 */
      transition: all 0.4s linear;
      border: 2px solid red;
    }

    .d4>div {
      /* 设置子元素缓动动画 */
      transition: all 0.4s linear;
      /* 设置子元素的背景 背景图片大小适配元素大小 */
      background: url(../image/test.jpg) no-repeat;
      background-size: 100% 100%;
    }

    /* 父元素hover时 将子元素之间的间隙变为0  */
    .d4:hover {
      gap: 0;
    }

    /* 父元素hover时 修改子元素背景图片的大小 以及背景图片偏移量 */
    .d4:hover>div {
      /* 设置的背景图片大小 就是子元素拼接成一张图片时 图片的大小 */
      background-size: 300px 300px;
      /* 根据JS设置的CSS变量 设置背景图片的偏移量 */
      background-position: var(--bgx, 0) var(--bgy, 0);
    }
</style>

<div class="d4">
    <div class="d4-1"></div>
    <div class="d4-2"></div>
    <div class="d4-3"></div>
    <div class="d4-4"></div>
    <div class="d4-5"></div>
    <div class="d4-6"></div>
    <div class="d4-7"></div>
    <div class="d4-8"></div>
    <div class="d4-9"></div>
</div>

<script>
  		// 获取所有子元素
    const d4List = document.querySelectorAll('.d4 > div');
    // 遍历所有子元素
    d4List.forEach((item, index) => {
      // 计算当前子元素所在行 0~2
      const row = Math.floor(index / 3);
      // 计算当前子元素所在列 0~2
      const col = index % 3;
      console.log(row, col);
      // 根据子元素所在行和列 设置CSS变量 决定显示的背景图片偏移量
      item.style.setProperty('--bgx', `${-col * 100}px`);
      item.style.setProperty('--bgy', `${-row * 100}px`);
    })
</script>

三、延伸扩展

1、简易的九宫格拼图游戏

​ 上面的三个案例Demo中,都是在父元素hover时,通过控制CSS样式子元素进行整体变化,无法做到针对单个元素进行变化。如果我们结合JS的点击事件,以及子元素的定位属性,就可以实现针对单个元素的变化。比如实现简单的拼图游戏效果等等。

页面效果:
实现思路:

​ ① 首先在一个父元素内,创建九个子元素,并通过display: grid;实现3×3的九宫格布局,并且设置子元素之间无间隙,紧密结合在一起,并且在父元素中水平垂直居中。

​ ② 通过JS获取所有子元素,然后遍历获取到的子元素,根据子元素所在行和所在列,设置表示背景偏移的CSS变量,最后再统一通过子级选择器+CSS变量的形式,统一设置元素背景,使九个子元素各自占据背景图片的一部分。

​ ③ 在上一步遍历所有子元素时,根据子元素所在行和所在列,同步设置表示子元素偏移的CSS变量,然后再给子元素设置 position: relative;相对定位,最后再统一通过子级选择器+CSS变量的形式,利用CSS变量设置子元素的偏移量,使其形成有监理的九宫格布局。

​ ④ 在遍历所有子元素的同时,给所有子元素绑定点击事件,根据事件触发元素上的表示元素偏移量的CSS变量的值,决定元素该向内还是向外偏移,然后根据元素所在的行和列,决定具体的元素偏移量。

具体代码:
html 复制代码
<style>
    .d3 {
      /* 设置grid布局 */
      display: grid;
      grid-template-columns: 100px 100px 100px;
      grid-template-rows: 100px 100px 100px;
      width: 340px;
      height: 340px;
      /* 设置子元素整体内容区域相对于父元素水平垂直居中 */
      justify-content: center;
      align-content: center;
      cursor: pointer;
      border: 2px solid red;
    }

    .d3>div {
      /* 设置子元素的偏移 使子元素之间出现间隙 形成九宫格布局 */
      position: relative;
      /* 利用JS的定义的CSS变量设置元素偏移量 */
      left: var(--pleft, 0);
      top: var(--ptop, 0);
      z-index: 1;
      width: 100px;
      height: 100px;
      /* 设置子元素的背景图片 */
      background: url(../image/test.jpg) no-repeat;
      /* 设置背景图片尺寸 该尺寸等同于子元素拼接后图片的尺寸 */
      background-size: 300px 300px;
      /* 利用JS的定义的CSS变量设置背景图片的偏移量 */
      background-position: var(--bgx, 0) var(--bgy, 0);
      /* 设置子元素的缓动动画 */
      transition: all 0.3s linear;
    }
</style>

<div class="d3">
    <div class="d3-1"></div>
    <div class="d3-2"></div>
    <div class="d3-3"></div>
    <div class="d3-4"></div>
    <div class="d3-5"></div>
    <div class="d3-6"></div>
    <div class="d3-7"></div>
    <div class="d3-8"></div>
    <div class="d3-9"></div>
</div>

<script>
    // 获取所有子元素
    const d3List = document.querySelectorAll('.d3 > div');
    // 遍历所有子元素
    d3List.forEach((item, index) => {
      // 计算当前子元素所在行 0~2
      const row = Math.floor(index / 3);
      // 计算当前子元素所在列 0~2
      const col = index % 3;
      // 根据子元素所在行和列 设置CSS变量 决定显示的背景图片偏移量
      item.style.setProperty('--bgx', `${-col * 100}px`);
      item.style.setProperty('--bgy', `${-row * 100}px`);
      // 根据子元素所在行和列 设置CSS变量 决定元素的偏移量
      item.style.setProperty('--pleft', `${(col - 1) * 20}px`);
      item.style.setProperty('--ptop', `${(row - 1) * 20}px`);
      // 为当前子元素添加点击事件
      item.addEventListener('click', (e) => {
        // 获取当前子元素表示元素偏移量的CSS变量值
        const left = e.target.style.getPropertyValue('--pleft');
        const top = e.target.style.getPropertyValue('--ptop');
        // 判断当前子元素是否在原始位置
        if (left === '0px' && top === '0px') {
          // 如果在原始位置 则根据子元素所在行和列 设置CSS变量 决定使元素偏移
          e.target.style.setProperty('--pleft', `${(col - 1) * 20}px`);
          e.target.style.setProperty('--ptop', `${(row - 1) * 20}px`);
        } else {
          // 如果不在原始位置 则将CSS变量值设置为0px 将其移回原始位置
          e.target.style.setProperty('--pleft', `0px`);
          e.target.style.setProperty('--ptop', `0px`);
        }
      });
    })
</script>
相关推荐
zhougl9961 小时前
html处理Base文件流
linux·前端·html
花花鱼2 小时前
node-modules-inspector 可视化node_modules
前端·javascript·vue.js
HBR666_2 小时前
marked库(高效将 Markdown 转换为 HTML 的利器)
前端·markdown
careybobo3 小时前
海康摄像头通过Web插件进行预览播放和控制
前端
TDengine (老段)4 小时前
TDengine 中的关联查询
大数据·javascript·网络·物联网·时序数据库·tdengine·iotdb
杉之5 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
喝拿铁写前端5 小时前
字段聚类,到底有什么用?——从系统混乱到结构认知的第一步
前端
再学一点就睡5 小时前
大文件上传之切片上传以及开发全流程之前端篇
前端·javascript
木木黄木木6 小时前
html5炫酷图片悬停效果实现详解
前端·html·html5
请来次降维打击!!!6 小时前
优选算法系列(5.位运算)
java·前端·c++·算法