在CSS中,碰撞回弹通常指的是两个相互碰撞的元素之间产生的反弹效果。这种效果可以通过CSS的动画或者变换属性来实现。
带你更好的理解requestAnimationFrame相较于定时器setInterval的优势,更加丝滑顺畅!
1.通过定位属性叠加动画的方式, 让小球在屏幕中进行运动,通过设置animation的alternate属性来设置回弹。最后,只需要设置两个运动的持续时间不同就能完成多方向的边界碰撞事件了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
div {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: orange;
border-radius: 50%;
animation: w_move 2.5s linear infinite alternate,
h_move 3.2s linear infinite alternate;
}
@keyframes w_move {
to {
left: calc(100vw - 100px);
}
}
@keyframes h_move {
to {
top: calc(100vh - 100px);
}
}
</style>
</head>
<body>
<div></div>
</body>
</html>
2.定时器方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
background-color: #e9e9e9;
}
#box {
position: absolute;
width: 120px;
height: 120px;
background-color: #baf;
line-height: 120px;
text-align: center;
}
</style>
</head>
<body>
<div id="box">
<a href="https://www.weibo.com/u/2908172597" target="_blank">公告!速点!</a>
</div>
<script>
let box = document.getElementById('box');
let xSpeed = 4; // x轴方向移动的速度
let ySpeed = 2.4; // y轴方向移动的速度(类似于向量)
let timer;
// 兼容性的浏览器视口高度和宽度
let width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
let height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
// 定时器运行函数
function run() {
let rect = box.getBoundingClientRect(); // 返回盒子的位置对象信息
// 下一瞬/帧 要修改盒子的left或top的值,等于当前位置加速度
let newX = rect.left + xSpeed;
let newY = rect.top + ySpeed;
// 位置判断,到达边界,碰撞更改
if (newX + box.offsetWidth >= width) {
newX = width - box.offsetWidth;
xSpeed = -xSpeed;
} else if (newX <= 0) {
newX = 0;
xSpeed = -xSpeed;
}
if (newY + box.offsetHeight >= height) {
newY = height - box.offsetHeight;
ySpeed = -ySpeed;
} else if (newY <= 0) {
newY = 0;
ySpeed = -ySpeed;
}
// 更改位置即为移动dom元素
box.style.left = `${newX}px`;
box.style.top = `${newY}px`;
}
// 开始动画
timer = setInterval(run, 16.7); // 大约每秒60帧
// 添加事件监听器
box.addEventListener('mouseenter', () => {
clearInterval(timer); // 鼠标悬停时,清除定时器
});
box.addEventListener('mouseleave', () => {
timer = setInterval(run, 16.7); // 鼠标离开时,重新设置定时器
});
</script>
</body>
</html>
3.requestAnimationFrame方式-实现网站站点首页出现的漂浮公告,回弹边界漂浮,下面看代码效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
background-color: #e9e9e9;
}
#box {
position: absolute;
width: 120px;
height: 120px;
background-color: #baf;
line-height: 120px;
text-align: center;
}
</style>
</head>
<body>
<div id="box">
<a href="https://www.weibo.com/u/2908172597" target="_blank">公告!微博@当时我就没憋住!</a>
</div>
<script>
let box = document.getElementById('box');
let xSpeed = 4; // x轴方向移动的速度
let ySpeed = 2.4; // y轴方向移动的速度(类似于向量)
let animationFrameId;
// 兼容性的浏览器视口高度和宽度
let width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
let height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
// 动画运行函数
function run() {
let rect = box.getBoundingClientRect(); // 返回盒子的位置对象信息
// 下一瞬/帧 要修改盒子的left或top的值,等于当前位置加速度
let newX = rect.left + xSpeed;
let newY = rect.top + ySpeed;
// 位置判断,到达边界,碰撞更改
if (newX + box.offsetWidth >= width) {
console.log('到达右边界');
newX = width - box.offsetWidth; // 右边界位置是视口宽度去除自身宽度
xSpeed = -xSpeed; // 移动方向颠倒过来
} else if (newX <= 0) {
console.log('到达左边界');
newX = 0; // 左边界位置是起始位置是0
xSpeed = -xSpeed; // 移动方向颠倒过来
}
// Y轴同理不赘述
if (newY + box.offsetHeight >= height) {
console.log('到达下边界');
newY = height - box.offsetHeight;
ySpeed = -ySpeed;
} else if (newY <= 0) {
console.log('到达上边界');
newY = 0;
ySpeed = -ySpeed;
}
// 更改位置即为移动dom元素
box.style.left = `${newX}px`;
box.style.top = `${newY}px`;
// 再次run
animationFrameId = requestAnimationFrame(run);
}
// 开始动画
run();
/**
* 添加事件监听器,使用mouseenter和mouseleave
* 即为鼠标hover效果
* */
box.addEventListener('mouseenter', () => {
console.log('移入暂停');
cancelAnimationFrame(animationFrameId); // 取消动画帧
});
box.addEventListener('mouseleave', () => {
console.log('移出继续');
run();
});
</script>
</body>
</html>
温馨提示:想要 hover 效果,就用mouseenter 和 mouseleave
/**
* 不要使用mouseover和mouseout,因为其内部子元素也会触发这个事件
* 把上面的两个事件绑定注释掉,把下面的解开,当鼠标在box的子元素a上
* 轻轻来回划过(要在box内操作)会出现动画抖动现象,即为触发了事件的暂停和启动
* */
// box.addEventListener('mouseover', () => {
// console.log('移入暂停');
// cancelAnimationFrame(animationFrameId); // 取消当前等待执行的动画帧
// });
// box.addEventListener('mouseout', () => {
// console.log('移出继续');
// run();
// });
requestAnimationFrame相关概述
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行...
官方文档对应截图
大致看了以后,我们可以知道:
requestAnimationFrame这个api主要是用来做动画的。
requestAnimationFrame这个api主要是用来做动画的。
requestAnimationFrame这个api主要是用来做动画的。
其实顾名思义,我们翻译这个英文单词,也能大致明白。request(请求)Animation(动画)Frame(帧)
官方 https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame