CSS写一个QQ登陆页的Button

其实这一期我早就想写了,应该是年初的时候,那会在家隔离,但一直没写出来。

感谢春雷的提示🎉🎉

契机

其实就是偶然需要重新登陆一下QQ,结果看到了这么一个Button。

aim

GIF的画质实在不行,可以自行QQ退出登录看一下😂

可以看到这个Button的效果是背景是渐变色且是动态的


当时最开始的想法是用CSSlinear-gradient或者radial-gradient来实现。但是没做出来

但是这次发现了两个秘密武器以后,离目标缩短了十万八千里。

别的不说,引出两个秘密武器!!😄

CSS filter: hue-rotate()

引用

How does Hue Rotation Work?

HSL (which stands for Hue Saturation Lightness) is a hue-based representation of the RGB color space of computer graphics. The HSL model is widely considered to be more intuitive than the RGB model. This is because, the HSL model allows you to select a base hue, and then adjust its saturation and lightness as desired.

大致翻译一下 😅

HSL代表的是

HSL(代表色相饱和度亮度)是计算机图形的RGB颜色空间的基于色相的表示。人们普遍认为HSL模型比RGB模型更直观。这是因为,HSL模型允许您选择基本色相,然后根据需要调整其饱和度和亮度。

hue-rotate()的参数最普遍的是接受一个角度值作为参数。

我们来看一张图就明白了。

H代表hue也就是色调。以这个色环的最上部为0 deg,也就是red为起点,顺时针旋转对应的角度,其对应DOM的颜色也会改变。如果现在div是一个红色的div,旋转120 deg这个div就会变成green 绿色

我们来先写一个小Demo来看一下

依旧是常规操作

$ npx create-react-app virtual-scroll

然后我需要把项目里不需要的代码修修改改。为了方便我就单写一个Test.jsxindex.js中引用的App换成Test即可。

我们需要一个可以拖动的drag bar来调节数值。


<div id="dragBar" className="dragBar" onMouseMove={handleMouseMove}>
  <div
    id="dragSpot"
    className="dragSpot"
    style={{ top: hueDegree }}
    onMouseDown={handleMouseEvent}
    onMouseUp={handleMouseEvent}
    onMouseLeave={() => setIsMouseDown(false)}
  />
  <span className="dragText">{`${hueDegree}deg`}</span>
</div>

我需要一个值来记录色环的旋转角度

const [hueDegree, setHueDegree] = useState(0);

然后需要一种drag操作来改变hueDegree。这里需要3个事件一起结合实现。

// 用来记录是否按下鼠标的状态标志
const [isMouseDown, setIsMouseDown] = useState(false);

const handleMouseEvent = (e) => {
  setIsMouseDown(e.type === 'mousedown')
}

然后我们需要通过onMouseMove()来获取鼠标y轴偏移距离。但是这个距离是相对于浏览器窗口的,所以我们需要记录上一次的偏移距离来和本次偏移量求出差值

const handleMouseMove = useCallback((e) => {
  if (isMouseDown) {
    // 求出差值
    const degOffset = e.clientY - preHueDegree;
    // 更新上次偏移量便于下次计算
    setPreHueDegree(e.clientY);
    // 更新hueDegree
    setHueDegree(hueDegree + degOffset);
  }
}, [preHueDegree, hueDegree, isMouseDown]);

效果是这个样子

但是可以看到他会超出(0 - 360)的范围,所以我们需要加一个限制。

const handleMouseMove = useCallback((e) => {
  if (isMouseDown) {
    const degOffset = e.clientY - preHueDegree;
    setPreHueDegree(e.clientY);
    if (hueDegree + degOffset >= 360) {
      setHueDegree(360);
      setIsMouseDown(false);
      return;
    }
    if (hueDegree + degOffset <= 0) {
      setHueDegree(0);
      setIsMouseDown(false);
      return;
    }
    setHueDegree(hueDegree + degOffset);
  }
}, [preHueDegree, hueDegree, isMouseDown]);

这样就好啦。

然后我们需要一个用来展示hue-rotate效果的图形,我就随便画个圆。

默认给这个圆的背景色red,让其从0deg开始,可以看到随着hue-rotate()的参数不断的改变,颜色也随之改变。

生活处处都用到的高斯模糊, filter: blur()

高斯模糊在生活中很多地方用到,比如ios系统就有太多太多高斯模糊了

GIF的画质又一次吐血

我们要做到高斯模糊只需要使用filter: blur()即可。接受的参数是一个模糊的值。

.test {
  filter: blur(20px);
}

进入今天主题

介绍了两个秘密武器之后,我们接下来用CSS写一下这个按钮。

其实原理非常非常简单

我们先来看一张图

1. 先来一个Button本身


// button本身
<div
  className="buttonBody"
  style={{ height, width }}
>
  ...
</div>

heightwidth作为ColorfulButton组建的props传入可以让用户自定义button的宽高,默认值都是150px

.buttonBody {
  position: relative;
  border-radius: 50%;
  background-color: #35c6ff;
}

2. 然后我们先来一个spot(也就是上图中蓝色的圆)

我们先让他的色调随时间改变。因为我默认按照red为起始色,所以我需要按照上方的色环图,可以看出蓝色主要聚集在200 - 240 deg这个范围。


<div
  className="spot"
  style={{
    top: '50%',
    left: '50%',
    transform: 'translate(-50%, -50%)',
    backgroundColor: spotColor,
  }}
></div>

.spot {
  position: absolute;
  border-radius: 50%;
  height: 75px;
  width: 75px;
  animation: spotTest 3s ease infinite;
}

@keyframes spotTest {
  0% {
    filter: hue-rotate(200deg);
  }
  50% {
    filter: hue-rotate(240deg);
  }
  100% {
    filter: hue-rotate(200deg);
  }
}

达成的效果就是这个样子

3. 接下来请出高斯模糊


@keyframes spotTest {
  0% {
    filter: blur(20px) hue-rotate(200deg);
  }
  50% {
    filter: blur(20px) hue-rotate(240deg);
  }
  100% {
    filter: blur(20px) hue-rotate(200deg);
  }
}

然后可以发现模糊程度很大,基本快和Button的背景色融合了。

4. 再多来几个这样的Spot

.spot1 {
  position: absolute;
  border-radius: 40%;
  animation: spotColorChange1 10s ease infinite;
}

.spot2 {
  position: absolute;
  border-radius: 70%;
  animation: spotColorChange2 10s ease infinite;
}

其实就是单独写了动画,为了后续animation要更改宽高,所以顺便把height和widht移到动画中。


@keyframes spotColorChange1 {
  0% {
    height: 105px;
    width: 90px;
    filter: blur(20px) hue-rotate(210deg);
  }
  60% {
    height: 130px;
    width: 130px;
    filter: blur(20px) hue-rotate(230deg);
  }
  100% {
    height: 105px;
    width: 90px;
    filter: blur(20px) hue-rotate(210deg);
  }
}

@keyframes spotColorChange3 {
  0% {
    height: 140px;
    width: 125px;
    filter: blur(20px) hue-rotate(220deg);
  }
  30% {
    height: 90px;
    width: 90px;
    filter: blur(20px) hue-rotate(210deg);
  }
  70% {
    height: 70px;
    width: 100px;
    filter: blur(20px) hue-rotate(230deg);
  }
  100% {
    height: 140px;
    width: 125px;
    filter: blur(20px) hue-rotate(220deg);
  }
}

为了让大家看的更清楚,我把先前Button写好的background-color换成了border

可以看到经过hue-rotate / blur / 宽高改变后的3个Spot和Button现在的关系是这个样子

当然我们Button以外的地方无需显示,所以我们添加一句

overflow: hidden;

再把background-color改回来看一下效果。

啊哈😁,已经有初步的样子了。

step5. 让背景色动起来

其实就是在动画中加上位移

类似这样~

@keyframes spotColorChange {
  0% {
    height: 125px;
    width: 120px;
    filter: blur(20px) hue-rotate(210deg);
    transform: translate(-70%, -70%) rotate(0deg);
  }
  40% {
    height: 135px;
    width: 130px;
    filter: blur(30px) hue-rotate(230deg);
    transform: translate(60%, -50%) rotate(-100deg);
  }
  100% {
    height: 125px;
    width: 120px;
    filter: blur(20px) hue-rotate(210deg);
    transform: translate(-70%, -70%) rotate(0deg);
  }
}

Spot有些不太显眼,我们也改进一下~👻

为了让效果更平滑舒适,让Button的背景色也在不同的蓝色之间动起来,从而突出动态的背景Spot。


.buttonBody {
  position: relative;
  border-radius: 50%;
  overflow: hidden;
  animation: bg ease 5s infinite;
}

@keyframes bg {
  0% {
    background-color: #56cfff;
  }
  30% {
    background-color: #16dcff;
  }
  70% {
    background-color: #2cb2ff;
  }
  100% {
    background-color: #56cfff;
  }
}

最后再加上 ->

import arrow from './arrow.svg'

 <img className="img" src={arrow} alt="arrow"/>

最终效果

这样我们就大功告成了!!🎉🎉

相关源码

点这里!!

相关参考