🔴 前言
动机
问
:为什么要写这篇?
答
:之前在一个国外网站看过这个动画,当时扪心自问了一下:"让你来做你怎么做,会做吗,要多久..."
自己想了一些方案,最后陷入深深的沉思...
这里引用罗翔老师的一句话吧:说真的,你对这个专业的知识,你可能连1%都掌握不了。承认自己的无知,乃是开启智慧的大门。
所以,有了这篇文的记录📝。
效果:
🔴 正文
⭕ 思路
简单
画背景和边
给我们来做,可能首当其冲冒出来的第一个思路
html
<div class="avatar">
<img src="./avatar.png" />
</div>
<style>
body {
margin: 0;
display: flex;
align-items: center;
justify-content: center;
}
.avatar {
width: 280px;
height: 280px;
border-radius: 50%;
background: #ecd078;
}
img {
width: 100%;
height: 100%;
border-radius: 50%;
transition: 0.5s;
}
.avatar:hover img {
transform: scale(1.2);
}
</style>
div
套图
,都设
为圆
。鼠标移入
,图放大
。
没达到我们要的效果。
再来。
径向渐变
画背景和边
1、不搞border,搞径向渐变
画背景(其中背景包括 色+边
)。
2、用outline
代替border。
区别:(1)、outline不占空间,画在周围 说明:属性可使用以下(1、2、3)任意一个值来声明,顺序不重要 -> outline: color style width offset
。
3、大
色块用黄
色,边
框用红
色。
css
--backgroundColor: #ecd078;
--borderColor: #c02942;
(1) 大色块黄色扩散,是个圆。
(2) 用closest-side
最短边做圆的半径。
(3) background: radial-gradient()
:用来创渐变背景的,我们这整个效果,能做到这个,其实中心思想还是靠渐变
去画色
的。
小课堂:
-- 本质 :从元素的背景的中心去画颜色,从内而外去渐变。
-- 参数 :各个参数的怎么定怎么设 --->
background: radial-gradient(circle at center, red 0, blue, green 100%)
说的是:在容器中心的渐变,从红开始,然后蓝,最后绿。
从红渐变到蓝色,绿色在100%的位置,蓝色的范围填充了整个渐变区域,
绿色设置在最后位置,将不会显示出来。
如果想要看到绿色,那么调整100%
为50%
,像这样background: radial-gradient(circle at center, red 0, blue, green 50%);
:
css
background: radial-gradient(
circle closest-side,
var(--backgroundColor) calc(100% - var(--borderWidth)),
var(--borderColor) calc(100% - var(--borderWidth)),
var(--borderColor) 100%,
green 100%);
closest-side
用来指定径向渐变的尺寸
。
以背景的最近的边作为渐变的尺寸。
宽度都要减去边的宽。(用calc计算属性calc(100% - var(--borderWidth))
)
也就是说,渐变从中心到最近的边去画。
通过background
径向渐变去画背景
和边
框的效果是这样的;
鼠标进去,人大,但景不得大。
设背景图,(1)、不重复;(2)、居中。
css
background: radial-gradient(
circle closest-side,
var(--backgroundColor) calc(100% - var(--borderWidth)),
var(--borderColor) calc(100% - var(--borderWidth)),
var(--borderColor) 100%,
green 100%) no-repeat center;
当scale(2),那么背景要½,才能保持原样。½ x 2 = 1
这样。
css
img {
--size: 280px;
--backgroundColor: #ecd078;
--borderColor: #c02942;
--borderWidth: 5px;
--scaleTimes: 1; /* 放大倍数 */
width: var(--size);
height: var(--size);
cursor: pointer;
border-radius: 50%;
background: radial-gradient(circle closest-side,
var(--backgroundColor) calc(100% - var(--borderWidth)),
var(--borderColor) calc(100% - var(--borderWidth)),
var(--borderColor) 100%, green 100%) no-repeat center / calc(100% / var(--scaleTimes)) 100%;
transition: 0.5s;
transform: scale(var(--scaleTimes));
}
img:hover {
// transform: scale(1.35)
--scaleTimes: 1.35
}
no-repeat center / calc(100% / var(--scaleTimes)) 100%
背景横
向尺
寸设置为calc(100% / 放大倍数)
鼠标移入,改变--scaleTimes
,背景横尺寸根据这个放大倍数改而改。
效果如下:
是不是有那味
了。
人大
,背景
保持不变
。
用变量
去搞背景的尺寸
问题,使背景保持不变。
至于那个绿色,还记得吗?是这一部分,延伸到四面八方的,设置了green
,也就是这个值:
连接红线
借助outline
画线。
css
outline: var(--borderWidth) solid var(--borderColor);
border-radius: 0 0 999px 999px;
outline-offset: calc(0px - var(--borderWidth)); // 即border的offset设置为-5px,目的是为了跟原来的渐变画的border重叠。
鼠标进,outline边的尺寸保持不变,所以outline-offset
要动态变。
offset
的值(也就是变化的差距的值)的计算,就是放大后的宽度
减去原来的宽度
除以2
.
就是(size * scaleTimes - size) / 2
。
再就是要除以一个放大倍率(size * scaleTimes - size) / 2 / scaleTimes
,得到真正被收缩的值。
(size * scaleTimes - size) / 2 / scaleTimes 最终会得到 (size - size / scaleTimes) / 2
注:
(size * scaleTimes - size) / 2 / scaleTimes
最后再去除以scaleTimes的意思是,如果宽度是200的话,咱offset最多顶天了就是-100,也就是缩到极限了,缩到中心点了。我们没有除以倍率是因为我们一开始假设默认的倍率是1的情况。放大到无限大的时候,这个offset的差距值就远远超过100,所以,正确的最终计算还要除以倍率。也就是
(size * scaleTimes - size) / 2 / scaleTimes
计算过程
:
x是size, y是scaleTimes:
上面式子简化成这样:
然后拆成这样:
也就是(size - size / scaleTimes) / 2
。 这个得到是正数。收缩的offset是个负值,那么就是反过来(size / scaleTimes - size) / 2
css
outline-offset: calc((var(--size) / var(--scaleTimes) - var(--size)) / 2 - var(--borderWidth));
防挡发
加padding-top
:
css
padding-top: 100px;
挡是不挡了, 但是味
不对了,人下移了。
那么,设置背景图就搞content的内容就好了,padding不要管它就行了。也就是说,设个content-box
给背景图叫它只管content,不管padding。
css
background: radial-gradient(circle closest-side,
var(--backgroundColor) calc(99% - var(--borderWidth)),
var(--borderColor) calc(99% - var(--borderWidth)),
var(--borderColor) 100%, green 100%) no-repeat center / calc(100% / var(--scaleTimes)) 100% content-box;
已经完成了差不多全部了。
但是圈外的东西能不能不要了
。
如果想要一个东西看不到
,
我们就一股脑要不是overflow hidden,要不就是用mask
。
多余的,不要
加mask
:
css
mask: radial-gradient(circle closest-side, #000 99%, transparent) content-box no-repeat center / calc(100% / var(--scaleTimes)) 100%;
这个mask途径的地方才显示,其他的都不显示。
所以我们拿上面那个渐变的圆来搞这个mask的圆。就可以了。
效果如下:
那圆外的头被搞掉了。
那么接着。
遮罩的区域
区域范围:
遮罩区域:上面一部分 + 下面头像那个正圆。
mask上面部分
css
img {
background: linear-gradient(#000 0 0) no-repeat center; // 全黑的渐变
}
看鼠标移入的效果,背景变大了,但是呢边outline
没动。所以,背景要下移,至于下移多少,就看outline的收缩距离的值,也就是outline-offset
这个值。
加个收缩变量值--shrink
:
css
// border收缩的变量
--shrink: calc((var(--size) / var(--scaleTimes) - var(--size)) / 2 - var(--borderWidth));
outline-offset: var(--shrink);
background: linear-gradient(#000 0 0) no-repeat center calc(0px - var(--shrink));
至于为啥0px - 收缩值
,因为这个收缩值是负数,我们背景是要下移,所以0 减去一个负数
得正数,就是下移的距离。
所以得到的效果如下:
接下来就是mask的尺寸了,你看上面效果是不是黑色区域随着鼠标进去,变大了。我们要它鼠标进去,不要大,就是跟之前圈圈一样,鼠标一进去头像大两倍,但背景保持不变,也就是乘以二分之一。
css
background: linear-gradient(#000 0 0) no-repeat center calc(0px - var(--shrink)) / calc(100% / var(--scaleTimes)) 50%;
注意这里:纵向写50%就好了。
这样,mask遮罩上面部分就完成了。
mask下面部分
下面部分是个圆。radial-gradient(circle closest-side, #000 99%, transparent) var(--backgroundOption)
css
--backgroundOption: content-box no-repeat center / calc(100% / var(--scaleTimes)) 100%;
-webkit-mask:
linear-gradient(#000 0 0) no-repeat center calc(0px - var(--shrink)) / calc(100% / var(--scaleTimes)) 50%,
radial-gradient(circle closest-side, #000 99%, transparent) var(--backgroundOption);
接下来就是要去除上面那两条竖边框。就是也是用mask
去遮
。
横向的背景就是减
去两
边的竖
线的宽度:calc(99% / var(--scaleTimes) - 2 * var(--borderWidth))
。
css
-webkit-mask: linear-gradient(#000 0 0) no-repeat center calc(0px - var(--shrink)) / calc(99% / var(--scaleTimes) - 2 * var(--borderWidth)) 50%, radial-gradient(circle closest-side, #000 99%, transparent) var(--backgroundOption);
至此遮罩写完。
头出洞🕳效果也随之完成。
🔴 总结
真的,我觉得前端把css玩
得特溜
的人绝非等闲之辈。
你说,我js可能不会的能查上一查,
css那些风骚奇淫怪技的一些效果,肚子里没点存货没点墨水,ddl延后多久,想破脑袋可能做出来都没达到完美交稿的效果。
☎️ 希望对大家有所帮助,本文有些地方可能考虑不够周到,有些纰漏,就当抛砖引玉,还望您海涵,如有错误,望不吝赐教,欢迎评论区留言互相学习。感谢阅读,祝您开发有乐趣。