写一个带追踪特效的渐变按钮

写一个带追踪特效的渐变按钮

开头先观看这张GIF图:

是否被它的出色动画效果所吸引?

这是从一个完美竞技平台中录制出来的

我脑海中萌生了用CSS来模仿这一效果的念头

如果您不想看实现过程,可以直接跳转到文末查看完整实现代码 🚀

绘画元素

我们先记录下这个按钮浮动的颜色(#868BFF),还有按钮的背景的渐变色(#39D5FF->#868bff

外部使用一个div元素表示一个自定义按钮元素,内部包含一个div元素(follow),用于实现一个尾随鼠标的效果。

当鼠标移动到按钮上时,follow元素将跟随鼠标的位置,创造出一个视觉上的尾随效果,以增强用户界面的交互性

html 复制代码
<div class="custom-button">
  <div class="follow"></div>
  按钮
</div>

接下来,结合我们之前记录下来的色值,给按钮添加上渐变色和浮动的元素

css 复制代码
.custom-button {
  height: 80px;
  width: 400px;
  background: linear-gradient(to right, #39D5FF, #868bff);
  color: #fff;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
}

.follow {
  width: 180px;
  height: 180px;
  background: radial-gradient(circle, #868bff 0%, transparent 70%, transparent 100%);
  transform-origin: center;
  position: absolute;
  left: 0;
  top: 0;
}

设置完毕后,你将会得到以下这个效果图

绑定事件

在我们录制的GIF图中,我们可以看到当我们鼠标移动到按钮上面的时候,会出现一个动画效果的软化边缘圆形,以增强元素的视觉吸引力

你是不是也和我一样第一时间想到了利用mousemove事件来监听鼠标移动

我们给外部的div绑定上mousemove事件

html 复制代码
<div class="custom-button" @mousemove="move">
  <span class="follow"></span>
  按钮
</div>
js 复制代码
const move=()=>{}

通过mouseEvent事件,我们可以拿到很多关于鼠标的各种信息,例如鼠标的位置,按钮状态,事件类型...

如果您对mouseEvent事件不是很熟悉,可以看看这里 MouseEvent

接下来,我们通过mouseEvent事件拿到offsetXoffsetY属性

这两个属性表示的是鼠标相对于目标元素内部左上角的偏移量

typescript 复制代码
const move = (e: MouseEvent) => {
  const { offsetX, offsetY } = e
  loc.X = offsetX - 90;
  loc.Y = offsetY - 90;
}

现在当我们鼠标进入到按钮内移动时,控制台就会持续打印出当前鼠标基于元素的坐标位置

我们新建一个reactive属性,用来存放当前鼠标坐标系,方便后续将值绑定到元素中

这里要注意的是,我们的鼠标指针是在圆形元素的中间,所以我们要将offsetXoffsetY各自减去90px,也就是width/2height/2

typescript 复制代码
// 存放坐标信息
const loc = reactive({
  X: 0,
  Y: 0
})

// 鼠标事件
const move = (e: MouseEvent) => {
  const { offsetX, offsetY } = e
  loc.X = offsetX - 90;
  loc.Y = offsetY - 90;
}

loc对象绑定给follow元素作为样式

vue 复制代码
<span class="follow" ref="followRef" :style="{ transform: `translate(${loc.X}px, ${loc.Y}px)` }"></span>

小小知识点:使用transform进行移动可以优化性能,因为它不会触发页面的reflowreflow是比较消耗性能的操作

我们试一下效果,跟着鼠标移动的效果貌似是实现了,但是你有没有发现每次移动的时候follow元素总会闪到坐上角去,而且offsetXoffsetY输出了{offsetX: 171, offsetY: 63}

我们给follow元素添加上pointer-events: none;即可解决问题

在多个元素重叠在一起时,event.target会发生变动,可以打印一下看看

事件目标元素一直在spandiv标签中来回切换,导致我们出现抖动的效果

来看看调整过的效果!

实现动画效果

这里可以使用CSS中的过渡动画来实现我们的需求,这里使用scale函数用来改变follow元素的缩放效果,当鼠标进入到元素中,scale值会从01,反之,重置为0

我们给外部的div绑定多两个事件,分别是mouseentermouseleave,用来记录当前鼠标是否进入到元素中

定义两个ref属性,用来记录transitionDuration值(元素CSS属性的过渡时间)和isShowCircle是否显示follow元素

html 复制代码
<!-- 绑定鼠标事件和CSS属性值 -->
<div class="custom-button" @mousemove="move" @mouseenter="enter" @mouseleave="leave">
  <span class="follow" ref="followRef"
    :style="{ transform: `translate(${loc.X}px, ${loc.Y}px) scale(${isShowCircle ? 1 : 0})`, transitionDuration: tds }"></span>
  按钮
</div>
typescript 复制代码
// 是否显示圆形
const isShowCircle = ref(false)
// 过渡时间
const tds = ref('0.2s')
// 当鼠标进入到元素后触发
const enter = () => {
  isShowCircle.value = true
  // 这里要注意下
  // 需要延迟设置tds值,否则会出现进入时动画丢失效果
  setTimeout(() => {
    tds.value = '0s';
  }, 200);
}
// 当鼠标离开元素后触发
const leave = () => {
  isShowCircle.value = false
  tds.value = '0.2s';
}

现在就可以来看看我们效果怎么样

解决文字层级问题

现在我们的特效会将按钮中的文字覆盖掉,是因为当兄弟元素脱离了文档流之后,元素的层级可能会比兄弟元素略高一些,我们只需要在按钮里的文字用span标签包裹着,并给他指定一个适当的z-index即可解决这个问题

别忘记了,也要设置pointer-events: none;哦,否则会出现抖动问题

css 复制代码
.text {
    z-index: 1;
    pointer-events: none;
}

扩展

Vue项目开发中,我们可能会遇到一份代码重复使用的情况,这个时候我们可以使用Vue组件的方式来封装这个按钮

这里我就直接把代码贴在这里啦,有需要的同学可以从这里Copy~~ 🥰🥰

vue 复制代码
<template>
    <div class="custom-button" @mousemove="move" @mouseenter="enter" @mouseleave="leave">
        <span class="follow" ref="followRef"
            :style="{ transform: `translate(${loc.X}px, ${loc.Y}px) scale(${isShowCircle ? 1 : 0})`, transitionDuration: tds }"></span>
        <span class="text">
            <slot></slot>
        </span>
    </div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';

const isShowCircle = ref(false)
const followRef = ref()
const loc = reactive({
    X: 0,
    Y: 0
})
const tds = ref('0.2s')
const enter = () => {
    isShowCircle.value = true
    setTimeout(() => {
        tds.value = '0s';
    }, 200);
}
const leave = () => {
    isShowCircle.value = false
    tds.value = '0.2s';
}
const move = (e: MouseEvent) => {
    const { offsetX, offsetY } = e
    loc.X = offsetX - 90;
    loc.Y = offsetY - 90;
}

</script>
<style scoped>
.custom-button {
    height: 80px;
    width: 400px;
    background: linear-gradient(to right, #39D5FF, #868bff);
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    overflow: hidden;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
}

.follow {
    width: 180px;
    height: 180px;
    background: radial-gradient(circle, #868bff 0%, transparent 70%, transparent 100%);
    transform-origin: center;
    pointer-events: none;
    position: absolute;
    left: 0;
    top: 0;
}

.text {
    z-index: 1;
    pointer-events: none;
}
</style>

项目地址:track-gradient-button

相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax