Skip to content

获取鼠标坐标来实现样式

登录/注册前端样式:

(后端样式见学习笔记:flask框架的应用 )

效果?

​ 受b站up主cksndn悬浮鼠标样式的启发,我希望能做到鼠标移动进某个地方后,颜色像是墨水滴入水池一样在背景晕开。这个动效其实可以做得很好看的(虽然我做不好看)。

​ 其实思路不难:在鼠标移入div中时,记录下坐标,然后再这个坐标点加上一个往四周放大的圆即可。但是由于某些情况(比如快速滑动)导致动画失效、过度添加导致的性能问题等,其实有必要自己上手完成一个这样的任务,来提高自己对动画、事件获取的理解。

​ 下面简单聊聊基本实现过程和bug修复:

​ #html:

html
<body>
    <div class="login">
        <h1>登录</h1>
    <form action="/login" method="post">
        <input type="text" name="username" placeholder="用户名:">
        <input type="password" name="password" placeholder="密码:">
        <input type="submit" value="登录" class="button">
        <a href="register">还没有账号?点我注册</a>
    </form>
    </div>

</body>

​ #css

css
*{
    margin: 0;
    padding: 0;
    color:#eee;
}
body{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: rgb(139, 162, 162);
}
.login{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    width: 300px;
    height: 450px;
    background-color: rgb(41, 91, 148);
    border-radius: 10px;
    box-shadow: 10px 10px 20px rgba(97, 125, 158 ,0.5);
    position: relative;
}
h1{
    font-size: 45px;
    font-weight: 500;
}
form{
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
    width: 100%;
    height: 230px;
    z-index:1;
    
}
input{
    width: 200px;
    height: 40px;
    background-color: transparent;
    border: none;
    border-bottom: 2px solid #eee;
    font-size: 16px;
    outline: none;
}
input::placeholder{
    font-size: 12px;
    color: #eee;
}
.button{
    width: 100px;
    height: 36px;
    border: 1px solid #eee;
    border-radius: 18px;
    cursor: pointer;
}
form a{
    bottom: 5px;
    position: absolute;
}
/*动画部分,随便采用一个元素,总之要实现放大的效果*/
span{
    position: absolute;
    top: 0;
    left: 0;
    width: 0;
    height: 0;
    background-color: rgb(212, 75, 75);
    transform: translate(-50%,-50%);/*鼠标居中*/
    border-radius: 50%;
    opacity: 0.5;
}
@keyframes in {
    0%
    {
        width: 0;
        height: 0;
    }
    100%
    {
        width: 1000px;
        height: 1000px;
    }
}
@keyframes out {
    0%
    {
        width: 1000px;
        height: 1000px;
    }
    100%
    {
        width: 0;
        height: 0;
    }
}

​ 添加一点交互,在js中搭建出基本的实现思路:

javascript

let login =document.querySelector('.login')
//鼠标进出
login.addEventListener('mouseenter',()=>{
	let span = document.createElement('span')
	
	login.appendChild(span)
	//实现动画过程
	span.style.animation = 'in 0.5s ease-out forwards'
})
login.addEventListener('mouseleave',()=>{
            
})

​ 接下来我们需要获取鼠标位置:

js
let span;//在外部声明
let login =document.querySelector('.login')
login.addEventListener('mouseenter',function(e){//改用e传参
    span = document.createElement('span')	
	login.appendChild(span)
	span.style.animation = 'in 0.5s ease-out forwards'
    
    //计算top/left
    let top = e.clientY - e.target.offsetTop
    let left = e.clientX - e.target.offsetLeft
    span.style.top = top+'px'
    span.style.left = left+'px'
})
login.addEventListener('mouseleave',function(e){
	span.style.animation = 'out 0.5s ease-out forwards'   
	let top = e.clientY - e.target.offsetTop
	let left = e.clientX - e.target.offsetLeft
	span.style.top = top+'px'
	span.style.left = left+'px'        
})

​ 效果已经出现了!接下来就是重新修改span标签的样式了,建议自己发挥想象力!记得给form等文字加点高度,否则会被覆盖。

缺陷?

​ 有一个问题是,如果鼠标反复进入,会造成span标签的反复添加,在我们打算添加一些动画、增加某些视觉显示时,这个问题是致命的。

加上计时器来消灭已经生成的span:

js
login.addEventListener('mouseleave', function(e) {
   if (span) { 
        span.style.animation = 'out 0.5s ease-out forwards';
        let top = e.clientY - e.target.offsetTop;
        let left = e.clientX - e.target.offsetLeft;
        span.style.top = top + 'px';
        span.style.left = left + 'px'
// 使用 setTimeout 删除 span,以便动画完成后移除
        setTimeout(() => {
            if (span) {
                login.removeChild(span);
                span = null; 
            }
        }, 500); //动画时间
    }
});

​ 还有一个问题鼠标快速划过(<0.5)时,由于后一个动画提前执行,所以span会被瞬间放大到1000px。

​ 因此我们需要计算一下鼠标进入的时间、离开的时间:

js
if(passTime < 500){
	setTimeout(mouseleave, 500 - passTime) 
}
else{
	mouseleave()
}

​ 此时out动画将在剩余的时间里被调用。

完善一下!将有关动画结束删去span的部分进行一些调整:

js
let login = document.querySelector('.login')
let span
let inTime, outTime
let isIn = true
let isOut

login.addEventListener('mouseenter', function(e){
	isOut = false //进入后初始要把out设置成关闭的
	if(isIn){
		inTime = new Date().getTime()

		span = document.createElement('span')
		login.appendChild(span)
		span.style.animation = 'in .5s ease-out forwards'
		let top = e.clientY - e.target.offsetTop
		let left = e.clientX - e.target.offsetLeft
		span.style.top = top + 'px'
		span.style.left = left + 'px'
        
		isIn = false 
		isOut = true 
	}
})
//鼠标离开事件
login.addEventListener('mouseleave', function(e){
	if(isOut){
		outTime = new Date().getTime()
        let passTime = outTime - inTime

        if(passTime < 500){
        	setTimeout(mouseleave, 500 - passTime)}
        else{
        	mouseleave()}
	}

    function mouseleave(){
    	span.style.animation = 'out .5s ease-out forwards'
		let top = e.clientY - e.target.offsetTop
		let left = e.clientX - e.target.offsetLeft

		span.style.top = top + 'px'
		span.style.left = left + 'px'

		setTimeout(function(){
        	login.removeChild(span)
			isIn = true }, 500)
    }
})

小知识点:为什么要进行传参e?

​ e通常指event,简单示例:

​ 主要是为了获取关于当前事件的信息:

js
document.addEventListener('click', function(e) {
    console.log('点击位置:', e.clientX, e.clientY);
    console.log('事件目标:', e.target);
});