手把手讲解使用纯css实现item - background-hover随鼠标丝滑移动~
#html #css #animation
jerrywu001
创建时间:2023-09-06 11:28:52
线上效果预览
前提
基于css方案,每个item的高度必须是固定的,这确实是一个局限的点,当然文章主要分享一下新的思路。
常规hover效果
.item:hover {
background-color: #eee;
}
可以想象一下,效果很生硬,你看到的区块是hover时突然出现,并不会有丝滑的移动效果。
css实现步骤
创建html,创建items
<!DOCTYPE html>
<html lang="en">
<head>
<title>hover</title>
<style>
body {
margin: 0;
padding: 0;
}
.container {
overflow-x: hidden;
overflow-y: auto;
position: relative;
height: 100vh;
}
.item {
--y: 0;
--height: 151px;
--surface-2: #767676;
--surface-0: #181818;
cursor: pointer;
padding: 30px 16px;
border-bottom: 1px #ddd solid;
box-sizing: border-box;
}
</style>
</head>
<body>
<div class="container"></div>
</body>
<script>
const itemCount = 15;
for (var i = 0; i < itemCount; i++) {
var item = document.createElement('div');
item.className = 'item';
item.innerHTML = '<p>testtesttesttesttest</p><p>testtesttesttesttesttest</p>';
document.querySelector('.container').appendChild(item);
}
</script>
</html>
在每一项hover时,通过伪类获取最后一个item
.item:hover~.item:last-child {
// do something
}
这是打开核心思路的钥匙。
介此,我们可以通过.item:last-child::before
去创建一个等同大小absolute定位
的浅灰色区块,在hover时候去移动它来达到我们想要的交互效果~
浅灰色区块默认opacity: 0
,可以通过top
来移动浅灰色区块。
.item {
--y: 0;
--height: 151px;
--surface-2: #767676;
--surface-0: #181818;
// ...
}
.item:last-child::before {
content: '';
display: block;
position: absolute;
background: var(--surface-2);
opacity: 0;
width: 100%;
top: var(--y);
left: 0;
height: var(--height);
border-radius: .4rem;
pointer-events: none;
transition: all .5s cubic-bezier(.2,1,.2,1);
}
接下来的关键就是如何在hover每一项时改变y
变量的值,上面我们提到过“在每一项hover时,通过伪类获取最后一个item的before”,并将其透明度调高
- 透明度调高
.item:hover~.item:last-child::before {
opacity: .06;
}
此时预览,发现并没有任何效果,浅灰色区块始终在最上方,那是因为我们还没有初始化y
的值。
初始化思路也是通过伪类实现即可,如下:
.item:nth-child(1):hover~.item:last-child::before {
--y: calc(var(--height) * 0);
}
.item:nth-child(2):hover~.item:last-child::before {
--y: calc(var(--height) * 1);
}
.item:nth-child(3):hover~.item:last-child::before {
--y: calc(var(--height) * 2);
}
// ...
// ...
但是,这样写死未免有些不方便,而且显得笨拙~
我们改造一下,通过js去生成样式,并在items dom生成后初始化插入head即可。
const initHoverClasses = () => {
let styleStr = '';
for (let i = 0, len = itemCount; i < len - 1; i++) {
styleStr += `
.item:nth-child(${i + 1}):hover~.item:last-child::before {
--y: calc(var(--height) * ${i});
}
`;
}
styleStr += `.item:nth-child(${itemCount}):hover::before {
--y: calc(var(--height) * ${itemCount - 1});
opacity: .06;
}`;
// create style tag, inject styleStr
const styleTag = document.getElementById('hover-classes');
if (styleTag) styleTag.remove();
const style = document.createElement('style');
style.id = 'hover-classes';
style.innerHTML = styleStr;
document.head.appendChild(style);
};
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<title>hover</title>
<style>
body {
margin: 0;
padding: 0;
}
.container {
overflow-x: hidden;
overflow-y: auto;
position: relative;
height: 100vh;
}
.item {
--y: 0;
--height: 151px;
--surface-2: #767676;
--surface-0: #181818;
cursor: pointer;
padding: 30px 16px;
border-bottom: 1px #ddd solid;
box-sizing: border-box;
}
.item:last-child::before {
content: "";
display: block;
position: absolute;
background: var(--surface-2);
opacity: 0;
width: 100%;
top: var(--y);
left: 0;
height: var(--height);
border-radius: .4rem;
pointer-events: none;
transition: all .5s cubic-bezier(.2,1,.2,1);
}
.item:hover~.item:last-child:before {
opacity: .06;
}
</style>
</head>
<body>
<div class="container"></div>
</body>
<script>
const itemCount = 15;
const initHoverClasses = () => {
let styleStr = '';
for (let i = 0, len = itemCount; i < len - 1; i++) {
styleStr += `
.item:nth-child(${i + 1}):hover~.item:last-child::before {
--y: calc(var(--height) * ${i});
}
`;
}
styleStr += `.item:nth-child(${itemCount}):hover::before {
--y: calc(var(--height) * ${itemCount - 1});
opacity: .06;
}`;
// create style tag, inject styleStr
const styleTag = document.getElementById('hover-classes');
if (styleTag) styleTag.remove();
const style = document.createElement('style');
style.id = 'hover-classes';
style.innerHTML = styleStr;
document.head.appendChild(style);
};
for (var i = 0; i < itemCount; i++) {
var item = document.createElement('div');
item.className = 'item';
item.innerHTML = '<p>testtesttesttesttest</p><p>testtesttesttesttesttest</p>';
document.querySelector('.container').appendChild(item);
setTimeout(() => {
initHoverClasses();
});
}
</script>
</html>
好了文章到此结束,如果对您有帮助,请点个小红心,谢谢~