目录
一、业务需求
默认显示基础图片;
当鼠标移入,使用九宫格效果展示图片;
当鼠标离开,使用非九宫格效果展示图片;
【鼠标移入,九宫格展示效果】
【鼠标离开,非九宫格展示效果(默认效果)】
【视频效果】
图片九宫格与非九宫格动态切换效果
二、实现思路
- 准备一个Grid布局,添加 3 * 3 = 9 个Grid元素;
- 每个Grid元素的背景均设为该目标图片;
- 调整图片大小,设置每个背景图的宽高为单个Grid元素宽高的三倍;
- 调整图片位置,设置每个Grid元素显示对应位置的图片内容,能够拼成一个完整的图片;
- 通过对Grid元素间隙的控制,在视觉上实现九宫格和非九宫格的切换效果;
三、实现过程
1、基础页面
(1)HTML布局
分析需求可知,
- 布局内容非常简单,只需要1个Grid容器【.grid-container 】和9个Grid元素【.grid-item】;
- 可以再给Grid容器添加一个父盒子【.container】,使图片的九宫格在父盒子中居中显示;
html
<body>
<div class="container">
<div class="grid-container">
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
</div>
</div>
</body>
(2)CSS样式
- 设置【.container 】元素为Flex布局,是子元素【.grid-container】水平垂直居中显示(在.container元素中水平垂直居中显示);
- Grid容器【.grid-container 】中的9个Grid元素【.grid-item 】,呈现【3行3列】的排列方式,并设置Grid元素的间距为10px;
- 给每个Grid元素【.grid-item】设置背景为目标图片;
css
<style>
.container {
/* 设为Flex布局 */
display: flex;
/* 设置子元素水平居中 */
justify-content: center;
/* 设置子元素垂直居中 */
align-items: center;
/* 设置宽度为可视窗口宽度 */
width: 100vw;
/* 设置高度为可视窗口高度的一半 */
height: 50vh;
}
.grid-container {
/* 设为grid布局 */
display: grid;
/* 子元素分为三列,列宽为100px */
grid-template-columns: repeat(3, 100px);
/* 子元素分为两行,行高为80px */
grid-template-rows: repeat(3, 80px);
/* 子元素间隔为10px */
gap: 10px;
}
.grid-item {
background-image: url("D:\\test\\girl.png");
background-repeat: no-repeat;
/* 先设置背景图展示出来 */
background-size: cover;
}
</style>
(3)现有效果
2、图片大小调整
- 现有效果仅仅只是,给每个盒子设置了相同的图片作为背景图,显然是不符合需求的;
- 目标效果是用"一张图片",占满九个盒子;
- 那一张图片的宽高,就是整个9宫格【.grid-container】的宽高(除去间隙);
- 所以背景图的宽度和高度,是单个盒子【.grid-item】宽度和高度的三倍;
css
.grid-item {
background-image: url("D:\\test\\girl.png");
background-repeat: no-repeat;
/* 先设置背景图展示出来 */
/* background-size: cover; */
/* 修改背景图宽高为单个盒子的3倍 */
background-size: 300%;
}
3、图片位置调整
调整了图片大下后,图片的位置(除了第一个),其他的图片显示的位置并不符合预期;
但可以知道的是,一个盒子的背景图默认是从图片的(0, 0)位置开始渲染的;
所以,需要调整其余背景图片的位置;
- 第 1 张图片不需要设置位置偏移;
- 第 2 张图片需要向左偏移一个盒子的宽度(-100px);
- 第 3 张图片需要向左偏移两个盒子的宽度(-200px);
- 第 4 张图片不需要向左偏移,但需要向上偏移一个盒子的高度(-80px);
- ......(以此类推)
- 第 9 张图片需要向左偏移两个盒子的宽度(-200px),还需要向上偏移两个盒子的高度(-160px);
这里给出两种不同的解决方式,分别使用CSS和JavaScript来背景图位置的偏移量来实现效果;
(1)使用CSS实现
简单粗暴的方式,分别给每个盒子设置自己的背景位置;
css
<style>
......
.grid-item:nth-child(1){
background-position: 0 0;
}
.grid-item:nth-child(2){
background-position: -100px 0;
}
.grid-item:nth-child(3){
background-position: -200px 0;
}
.grid-item:nth-child(4){
background-position: 0 -80px;
}
.grid-item:nth-child(5){
background-position: -100px -80px;
}
.grid-item:nth-child(6){
background-position: -200px -80px;
}
.grid-item:nth-child(7){
background-position: 0 -160px;
}
.grid-item:nth-child(8){
background-position: -100px -160px;
}
.grid-item:nth-child(9){
background-position: -200px -160px;
}
</style>
(2)使用JavaScript实现
使用JavaScript也可以达到同样的效果,两者选其一即可;
- 通过querySelectorAll() 方法获取所有的【.grid-item 】元素,得到数组 itemList ,其中有item[0]~item[8];循环遍历该数组,计算每个元素的行号和列号;
- 计算每个元素的行号r :计算 i / 3 ,再向下取整,得到 0,1,2;
- 计算每个元素的列号c :计算 i % 3 ,得到 0,1,2;
- 行号( r ) * 元素高度(itemHeight),得到每一行的向上偏移量(取反);
- 列号( c ) * 元素宽度(itemWidth),得到每一行的向左偏移量(取反);
- 再将计算结果直接赋值给该元素即可(别忘了加 'px' );
注意:
- 这里需要修改一些样式,应用CSS中的变量,需要在【body】中声明变量,在【.grid-container】中使用CSS变量;
- 方便在JavaScript中获取定义好的变量值,设置每个元素的偏移位置;
【需要修改的CSS代码】
html
<style>
......
body {
/* 定义CSS变量 */
/* 定义子元素的宽度 */
--item-width: 100px;
/* 定义子元素的高度 */
--item-height: 80px;
/* 定义元素间隔 */
--item-gap: 10px;
}
......
.grid-container {
/* 设为grid布局 */
display: grid;
/* 子元素分为三列,列宽为100px */
grid-template-columns: repeat(3, var(--item-width));
/* 子元素分为两行,行高为80px */
grid-template-rows: repeat(3, var(--item-height));
/* 子元素间隔为10px */
gap: var(--item-gap);
}
......
</style>
【添加的JavaScript代码】
javascript
<script>
// 获取元素
var body = document.querySelector("body");
var container = document.querySelector(".grid-container");
var itemList = document.querySelectorAll(".grid-item");
// 获取CSS变量(元素的宽度、元素的高度)
var itemWidth = parseInt(getComputedStyle(container).getPropertyValue('--item-width'));
var itemHeight = parseInt(getComputedStyle(container).getPropertyValue('--item-height'));
// 计算偏移量
for (let i = 0; i < itemList.length; i++) {
// 计算是元素位置,
// 元素的行号(从0开始)
const r = Math.floor(i / 3);
// console.log(r);
// 元素的列号(从0开始)
const c = i % 3;
// console.log(c);
// 计算元素的偏移量
const dx = -(c * itemWidth) + 'px';
const dy = -(r * itemHeight) + 'px';
console.log((i + 1), dx, dy);
// 设置元素的偏移
itemList[i].style.backgroundPosition = `${dx} ${dy}`;
}
</script>
【实现的效果】
4、鼠标控制切换
分析需求可知:
- 图片九宫格与非九宫格的展示,现在仅仅就变成了调整元素间隙的问题;
- 九宫格展示,元素间隙【--item-gap: 10px;】
- 非九宫格展示,元素间隙【--item-gap: 0;】
- 给【.grid-container】盒子注册鼠标移入事件;当鼠标进入,设置元素间隙为10px(值可以自行定义),使用九宫格效果展示;
- 给【.grid-container】盒子注册鼠标离开事件;当鼠标离开,设置元素间隙为0,使用非九宫格效果展示;
注意:
- 这里需要修改CSS,调整默认的元素间距为0;
css
body {
/* 定义CSS变量 */
......
/* 定义元素间隔 初始为0 */
--item-gap: 0;
}
【初始效果】
【添加下列JavaScript代码】
javascript
<script>
// 获取元素
var body = document.querySelector("body");
var container = document.querySelector(".grid-container");
......
// 鼠标进入,显示九宫格
container.addEventListener('mouseenter', function () {
// 修改CSS变量
body.style.setProperty('--item-gap', '10px');
})
// 鼠标离开,不显示九宫格
container.addEventListener('mouseleave', function () {
// 修改CSS变量
body.style.setProperty('--item-gap', '0');
})
</script>
到这一步,效果基本上是可以实现了;
5、添加过渡
现有的切换效果生硬的,添加过度效果进行优化;
css
.grid-container {
......
/* 添加过渡 */
transition: 0.6s;
}
四、完整代码
【使用CSS控制图片位置】
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片九宫格切换展示效果</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
/* 定义CSS变量 */
/* 定义子元素的宽度 */
--item-width: 100px;
/* 定义子元素的高度 */
--item-height: 80px;
/* 定义元素间隔 初始值*/
--item-gap: 0;
}
.container {
/* 设为Flex布局 */
display: flex;
/* 设置子元素水平居中 */
justify-content: center;
/* 设置子元素垂直居中 */
align-items: center;
/* 设置宽度为可视窗口宽度 */
width: 100vw;
/* 设置高度为可视窗口高度的一半 */
height: 50vh;
}
.grid-container {
/* 设为grid布局 */
display: grid;
/* 子元素分为三列,列宽为100px */
grid-template-columns: repeat(3, var(--item-width));
/* 子元素分为两行,行高为80px */
grid-template-rows: repeat(3, var(--item-height));
/* 子元素间隔为10px */
gap: var(--item-gap);
/* 添加过渡 */
transition: 0.6s;
border-radius: 6px;
box-shadow: 0 0 6px 1px #999;
}
.grid-item {
background-image: url("D:\\test\\girl.png");
background-repeat: no-repeat;
/* 先设置背景图展示出来 */
/* background-size: cover; */
/* 修改背景图宽高为单个盒子的3倍 */
background-size: 300%;
}
.grid-item:nth-child(1) {
background-position: 0 0;
}
.grid-item:nth-child(2) {
background-position: calc(-1* var(--item-width)) 0;
}
.grid-item:nth-child(3) {
background-position: calc(-2* var(--item-width)) 0;
}
.grid-item:nth-child(4) {
background-position: 0 calc(-1* var(--item-height));
}
.grid-item:nth-child(5) {
background-position: calc(-1* var(--item-width)) calc(-1* var(--item-height));
}
.grid-item:nth-child(6) {
background-position: calc(-2* var(--item-width)) calc(-1* var(--item-height));
}
.grid-item:nth-child(7) {
background-position: 0 calc(-2* var(--item-height));
}
.grid-item:nth-child(8) {
background-position: calc(-1* var(--item-width)) calc(-2* var(--item-height));
}
.grid-item:nth-child(9) {
background-position: calc(-2* var(--item-width)) calc(-2* var(--item-height));
}
</style>
</head>
<body>
<div class="container">
<div class="grid-container">
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
</div>
</div>
</body>
<script>
// 获取元素
var body = document.querySelector("body");
var container = document.querySelector(".grid-container");
var itemList = document.querySelectorAll(".grid-item");
// 鼠标进入,显示九宫格
container.addEventListener('mouseenter', function () {
// 修改CSS变量
body.style.setProperty('--item-gap', '10px');
this.style.boxShadow = "none";
this.style.borderRadius = "0";
itemList.forEach(el => {
el.style.boxShadow = "0 0 6px 1px #999";
el.style.borderRadius = "6px";
});
})
// 鼠标离开,不显示九宫格
container.addEventListener('mouseleave', function () {
// 修改CSS变量
body.style.setProperty('--item-gap', '0');
this.style.boxShadow = "0 0 6px 1px #999";
this.style.borderRadius = "6px";
itemList.forEach(el => {
el.style.boxShadow = "none";
el.style.borderRadius = "0";
});
})
</script>
</html>
【使用JavaScript控制图片位置】
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片九宫格切换展示效果</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
/* 定义CSS变量 */
/* 定义子元素的宽度 */
--item-width: 100px;
/* 定义子元素的高度 */
--item-height: 80px;
/* 定义元素间隔 初始值 */
--item-gap: 0;
}
.container {
/* 设为Flex布局 */
display: flex;
/* 设置子元素水平居中 */
justify-content: center;
/* 设置子元素垂直居中 */
align-items: center;
/* 设置宽度为可视窗口宽度 */
width: 100vw;
/* 设置高度为可视窗口高度的一半 */
height: 50vh;
}
.grid-container {
/* 设为grid布局 */
display: grid;
/* 子元素分为三列,列宽为100px */
grid-template-columns: repeat(3, var(--item-width));
/* 子元素分为两行,行高为80px */
grid-template-rows: repeat(3, var(--item-height));
/* 子元素间隔为10px */
gap: var(--item-gap);
/* 添加过渡 */
transition: 0.6s;
border-radius: 6px;
box-shadow: 0 0 6px 1px #999;
}
.grid-item {
background-image: url("D:\\test\\girl.png");
background-repeat: no-repeat;
/* 先设置背景图展示出来 */
/* background-size: cover; */
/* 修改背景图宽高为单个盒子的3倍 */
background-size: 300%;
}
</style>
</head>
<body>
<div class="container">
<div class="grid-container">
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
</div>
</div>
</body>
<script>
// 获取元素
var body = document.querySelector("body");
var container = document.querySelector(".grid-container");
var itemList = document.querySelectorAll(".grid-item");
// 获取CSS变量(元素的宽度、元素的高度)
var itemWidth = parseInt(getComputedStyle(container).getPropertyValue('--item-width'));
var itemHeight = parseInt(getComputedStyle(container).getPropertyValue('--item-height'));
// 获取元素间隙
// var itemGap = parseInt(getComputedStyle(container).getPropertyValue('--item-gap'));
// 计算偏移量
for (let i = 0; i < itemList.length; i++) {
// 计算是元素位置,
// 元素的行号(从0开始)
const r = Math.floor(i / 3);
// console.log(r);
// 元素的列号(从0开始)
const c = i % 3;
// console.log(c);
// 计算元素的偏移量
const dx = -(c * itemWidth) + 'px';
const dy = -(r * itemHeight) + 'px';
console.log((i + 1), dx, dy);
// 设置元素的偏移
itemList[i].style.backgroundPosition = `${dx} ${dy}`;
}
// 鼠标进入,显示九宫格
container.addEventListener('mouseenter', function () {
// 修改CSS变量
body.style.setProperty('--item-gap', '10px');
this.style.boxShadow = "none";
this.style.borderRadius = "0";
itemList.forEach(el => {
el.style.boxShadow = "0 0 6px 1px #999";
el.style.borderRadius = "6px";
});
})
// 鼠标离开,不显示九宫格
container.addEventListener('mouseleave', function () {
// 修改CSS变量
body.style.setProperty('--item-gap', '0');
this.style.boxShadow = "0 0 6px 1px #999";
this.style.borderRadius = "6px";
itemList.forEach(el => {
el.style.boxShadow = "none";
el.style.borderRadius = "0";
});
})
</script>
</html>
=========================================================================
每天进步一点点~!
期待你更好的实现方法~~!