今天分享一个很有特色的按钮交互效果,如封面图所示,保证让你停不下来,原作者是Adam Kuhn
,有兴趣的可以去codepen体验,地址:codepen,本文将核心功能逐一讲解。
基于这个动图可以将主要实现的几个功能点拆分为以下几点:
- 按钮的径向渐变背景色可以随着鼠标的移动变化
- 按钮的背景区域会随着鼠标的移动产生弹性变化效果
- 按钮的文字阴影会随着鼠标的变化而变化
鼠标位置获取
在正式开始前做一些准备工作,分析主要的这几个功能点可以发现每个功能都和鼠标的移动有关,都需要借助于鼠标移动的坐标,所以我们首先获取鼠标的位置并传递到css中,代码如下:
document.querySelectorAll(".inner").forEach((button) => { button.onmousemove = (e) => { const target = e.target; const rect = target.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; button.style.setProperty("--x", `${x}px`); button.style.setProperty("--y", `${y}px`); button.style.setProperty("--height", `${rect.height}px`); button.style.setProperty("--width", `${rect.width}px`); }; });
这里除开传递鼠标的位置,还传递了当前按钮的宽高用于后续按钮文案阴影的依赖。
径向渐变背景动起来
背景色默认是纯色,随着鼠标的产生变化,所以这里和两个关键点有关,鼠标移入hover,移动过程中的坐标变化。实现过程核心是通过background定义两个背景色,默认的显示部分background-size
是100%,渐变部分的background-size
是0,待hover时设置为100%,这时就会显示渐变背景色内容了。
background: // 渐变背景色 radial-gradient( circle at center, var(--lightest), var(--light) 5%, var(--dark) 30%, var(--darkest) 50% ), // 默认显示背景色 var(--darkest); background-size: 0px 0px, 100%; :hover { background-size: 100%, 100%; }
显示之后要动起来,基于js传入的坐标值应用到transform
的translate
平移,这里注意移动是要基于当前元素的中心点位所以x和y都要减去自身的50%。
transform: translate(calc(var(--x) - 50%), calc(var(--y) - 50%));
如图所示,绿色区域是按钮部分,整个背景的中心点要和鼠标移动的坐标一致,所以要减去自身宽高的各一半。还有一点需要注意的是不能在移动的过程中让背景色漏出,所以背景区域是整个按钮的2倍。
这时整个背景区域很大,这里使用了CSS3的混合模式mix-blend-mode: lighten
,最终只会应用亮色部分也就是中间的绿色区域。这里的混合模式给下一步中的弹性伸缩效果起到重要的作用。
此时的效果就是这样的,原代码在此基础上还增加了transition和filter体验让效果更佳,因涉及篇幅较长这里就不一一说明了,
背景区域弹性变化交互效果
背景弹性交互效果需要增加一个元素,与当前按钮同级别。此时的html如下:
<div class="inner"> <button type="button">南城FE</button> <div class="blob"></div> </div>
blob
元素和button
都使用了绝对定位,因为按钮上面有文字,所以层级上button
更高。blob
元素增加了两个伪元素,先看after
:
&:after { width: calc(100% - 4rem); height: calc(100% - 4rem); top: 2rem; left: 2rem; border-radius: 5rem; box-shadow: 0 0 0 8rem #fff; }
基于当前界面减少实际按钮的区域,并通过定位居中,再通过box-shadow
填充白色背景,还增加了圆角,此时按钮的背景变成如下所示,按钮的雏形已经有了。
然后before
主要也是通过box-shadow
来增加额外的元素显示,分为三个部分,中间部分跟随鼠标移动,上下两个部分为鼠标移动到边界的反向效果区域。核心代码如下:
box-shadow: 0 0 0 0.75rem #fff, 0 -8rem 0 2rem #fff, 0 8rem 0 2rem #fff;
再配合基于js传入的坐标值应用到translate
平移,box-shadow
部分的内容即可跟随鼠标动起来了。这里用到了一个css3的函数clamp
,它可以用来限制一个值的范围。clamp
函数接受三个参数,分别表示最小值、推荐值和最大值。函数的返回值为推荐值,但是它会被限制在最小值和最大值之间。所以这里超出按钮的显示区域会有临界点,不会完全脱离,核心代码如下:
transform: translate( clamp(5%, calc(var(--x) - 50%), 550%), clamp(1rem, calc(var(--y) - 50%), 5rem) );
此时按钮的效果如下,圆形部分即是上面的0 0 0 0.75rem #fff
,下面的半圆即是0 8rem 0 2rem #fff
,因为增加了圆角border-radius: 100%
所以都是圆形。为什么下面的是半圆白色,因为after
中的box-shadow
白色背景遮挡了,所以不会完全显示,又因为是白色阴影加上混合模式所以这块区域以亮色白色显示。
是不是和目标效果有些接近了,加上一行关键代码即可。
filter: blur(12px) contrast(50);
这里使用filter属性处理,首先对元素进行模糊处理,如果只是增加模糊的效果如下,可以看到增加的伪元素圆形都被磨平了,完美的融入到了按钮本身的背景色中。
再加上contrast
调整元素的对比度即可达到最终的效果,这里切记执行的顺序不能写反。在CSS中 filter
属性中的函数是按照从左到右的顺序执行的。如果你在 filter
属性中使用了多个函数,那么它们会按照从左到右的顺序依次执行。
按钮的文字阴影变化
文字的阴影变化主要是改变其水平和垂直的偏移量,以及模糊半径,这里就要用到最开始传入的按钮宽高的数据了,因为偏移量的计算会基于整个按钮的面积,这样才会显得更逼真。
先看水平和垂直的偏移量,核心还是基于clamp
函数,设置最小值,最大值,中间的推荐值则会随着鼠标的坐标值变化而变化,具体的数值有兴趣的可以调整体验,以下是文字阴影的水平和垂直的偏移量计算的代码:
clamp(-6px, calc((var(--width) / 2 - var(--x)) / 12), 6px) clamp(-4px, calc((var(--height) / 2 - var(--y)) / 16), 4px)
然后是模糊半径的计算,这里用到了max
函数,最大取5px,其他情况基于坐标值和宽高计算得出。
max( calc((var(--width) / 2 - var(--x)) / 8 + ((var(--height) / 2 - var(--y)) / 3)), calc(( ((var(--width) / 2 - var(--x)) / 8) + ((var(--height) / 2 - var(--y)) / 3) ) * -1 ), 5px )
最终的效果如下:
最后
到此整个核心的实现过程就结束了,整个代码中我们使用了box-shadow
,text-shadow
,mix-blend-mode
,filter
等属性,还有CSS3函数max
,clamp
,calc
。还有transition
动画相关没有说明,涉及的知识点比较多,有兴趣的同学可以看源码了解。
在线代码预览:
到此本文就结束了,看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~