好久没有写过原生JS了,突然没事做,写了一个跟着鼠标走的加载小动画,最终效果如下图:
这个效果实现起来非常简单,大概思路是:先用 CSS3 的 border-radius 属性将三个 div 的样式设置为圆形,然后定义一个椭圆路径,最后用定时器或帧函数使得三个 div 绕着椭圆路径旋转,同时椭圆路径的中点始终跟随者鼠标移动。
有了思路就可以开始写代码了,先把 html 和 css 部分写好:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>跟着鼠标走的加载小动画</title>
<style>
body{
padding: 0;
margin: 0;
}/*初始化body*/
#container{
width:100%;
height:100%;
margin: 0;
padding: 0;
background-color: white;
position: absolute;
}/*整个网页容器的样式*/
#ball_1{
border-radius: 100%;
position: absolute;
width:8px;
height:8px;
background-color: #999;
}/*第一个小球样式*/
#ball_2{
border-radius: 100%;
position: absolute;
width:12px;
height:12px;
background-color: #666;
}/*第二个小球样式*/
#ball_3{
border-radius: 100%;
position: absolute;
width:16px;
height:16px;
background-color: #333;
}/*第三个小球样式*/
</style>
</head>
<body>
<div id="container"></div> <!--整个网页的容器-->
</body>
</html>
写好静态页面代码后,开始写 JS 代码:
(一)用 js 的方式生成3个小球
<script>
//获得容器对象
var container = document.getElementById("container");
//定义小球数组
var ball_arr = [];
//生成3个小球
for(var i=1; i<=3; i++){
var ball = document.createElement('div'); //创建小球div
ball.id = "ball_" + i; //设置小球对应的样式
ball.style.display = "none"; //暂时不显示小球
ball_arr.push( ball ); //压入小球数组
container.appendChild( ball ); //添加小球到网页中显示
}
</script>
(二)定义椭圆路径
//定义椭圆路径
var path_length = 50; //定义路径的半长轴的长度
var path_width = 30; //定义路径的半短轴的长度
var path_angel = 0; //初始化与正半轴所成的角度角度为0(顺时针为正)
var angel_increase = 1.3; //小球运动角度增量(用于做小球旋转动画动画)
var ball_gap = 0.8; //小球间距(用于做小球旋转动画)
这里可能会有疑问,怎么定义椭圆路径?怎么在代码方面描述一个椭圆? 一般情况下,我们要用代码画一个图形的路径,就要用到图形对应的参数方程,比如椭圆的参数方程为:
其中,a为椭圆的半长轴的长度,b为半短轴的长度,α为与正半长轴所成的角度。于是,用 a, b, α 这两个量,就能确定某个构成椭圆路径的点的坐标。如果 a, b 长度不变,不断改变角度 α , 就能确定一个构成椭圆路径的几乎所有点的坐标,有了这些坐标,3个小球就能准确地围着椭圆的路径运动而不会偏离出去。
如果仍然难以理解,可以用一张图作辅助理解,如下图,红点M的轨迹是椭圆,M(x,y) = (|OA|cosa,|OB|sina) = (acosα, bsinα)。
(三)定义鼠标移动事件
由于椭圆路径的中点是跟着鼠标跑,所以我们需要定义鼠标移动事件来更新一对鼠标坐标的变量,在此前,先把一对鼠标坐标变量定义好。
//定义实时鼠标坐标
var mouseX = 0;
var mouseY = 0;
由于不同的浏览器,监听鼠标移动事件的方式不一样,所以需要用兼容性写法监听鼠标移动事件。
//鼠标移动事件(兼容性写法)
if (document.addEventListener) { //FireFox,Chrome,Opera…
container.addEventListener('mousemove',onMouseMove,false); //事件会在鼠标指针移动时发生。
}
else if (document.attachEvent) { //IE
container.attachEvent('onmousemove', onMouseMove, false); //事件会在鼠标指针移动时发生。
}
else { //Other(IE,FireFox,Chrome,Opera等,绝大部分浏览器支持方法 onclick 监听)
container.onmousemove = onMouseMove;
}
编写鼠标移动事件的回调处理函数,实时更新鼠标坐标变量。
//鼠标移动事件
function onMouseMove(event){
//更新坐标
mouseX = event.clientX + document.body.scrollLeft - document.body.clientLeft;
mouseY = event.clientY + document.body.scrollTop - document.body.clientTop;
}
(四)编写小球旋转加载动画
在 js 中编写动画,一般使用帧事件或者定时器,在这里我使用了帧事件函数 requestAnimFrame 方式来编写动画(如果有对 requestAnimFrame 不了解的童鞋,先自行查询)。由于不同浏览器对帧事件的兼容性不相同,所以先要对帧事件函数进行兼容性处理。
//定义兼容性帧事件
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
//兼容性取消帧事件
window.cancelAnimationFrame = (function () {
return window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
function (timer) {
window.clearTimeout(timer);
};
})();
接下来在帧事件中编写小球绕着椭圆路径旋转的动画。
//监听帧事件,帧渲染和帧绘制的变量
var timer = null;
//根据椭圆路径,执行小球运动
function drawFrame(){
for(var i=0; i<ball_arr.length; i++){
var ball = ball_arr[i];
//显示小球
ball.style.display = "block";
//转化角度为弧度制
var angel = ((path_angel * Math.PI / 180) + (i * ball_gap)) % 360;
//更新小球横坐标
ball.style.left = path_length * Math.cos(angel) + mouseX - ball.style.width/2 - document.body.clientLeft + "px";
//更新小球纵坐标
ball.style.top = path_width * Math.sin(angel) + mouseY - ball.style.height/2 - document.body.clientTop + "px";
//根据增量更新角度
path_angel = (path_angel + angel_increase) % 360;
}
//再次渲染
timer = requestAnimationFrame(drawFrame);
}
其中,ball.style.left = path_length * Math.cos(angel); 对应了椭圆的参数方程的 x=acosα。
同理,ball.style.top = path_width * Math.sin(angel); 对应了椭圆的参数方程的 y=bsinα。
(五)最后,调用编写好的动画即可。
timer = requestAnimationFrame(drawFrame);
最后,把以上代码整合起来,完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>跟着鼠标走的加载小动画</title>
<style>
body, #container{
padding: 0;
margin: 0;
}
#container{
width:100%;
height:100%;
background-color: white;
position: absolute;
}
#ball_1, #ball_2, #ball_3{
border-radius: 100%;
position: absolute;
}
#ball_1{
width:8px;
height:8px;
background-color: #999;
}
#ball_2{
width:12px;
height:12px;
background-color: #666;
}
#ball_3{
width:16px;
height:16px;
background-color: #333;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
//初始化3个小球
var container = document.getElementById("container");
var ball_arr = [];
for(var i=1; i<=3; i++){
var ball = document.createElement('div');
ball.id = "ball_" + i;
ball.style.display = "none";
ball_arr.push( ball ); //压入小球数组
container.appendChild( ball ); //添加小球到网页中显示
}
//定义椭圆路径
var path_length = 50; //定义路径的半长轴的长度
var path_width = 30; //定义路径的半短轴的长度
var path_angel = 0; //初始化角度为0
var angel_increase = 1.3; //小球运动角度增量
var ball_gap = 0.8; //小球间距
//鼠标移动事件(兼容性写法)
var webUI = container;
if (document.addEventListener) { //FireFox,Chrome,Opera…
// webUI.addEventListener('click', onMouseClick, false); //单击事件是在同一元素上发生了鼠标按下事件之后又发生了鼠标放开事件时才发生的。
// webUI.addEventListener('mousedown', OnMouseDown, false); //事件会在鼠标按键被按下时发生。
// webUI.addEventListener('mouseup', OnMouseUp, false); //事件会在鼠标按键被松开时发生。
webUI.addEventListener('mousemove',onMouseMove,false); //事件会在鼠标指针移动时发生。
// webUI.addEventListener('mouseover', onMouseOver, false); //事件会在鼠标指针移动到指定的对象上时发生。
// webUI.addEventListener('mouseout', onMouseOut, false); //事件会在鼠标指针移出指定的对象时发生。
//c.addEventListener('touch',onMouseClick,false);
}
else if (document.attachEvent) { //IE
// webUI.attachEvent('onclick', onMouseClick, false); //单击事件是在同一元素上发生了鼠标按下事件之后又发生了鼠标放开事件时才发生的。
// webUI.attachEvent('onmousedown', OnMouseDown, false); //事件会在鼠标按键被按下时发生。
// webUI.attachEvent('onmouseup', OnMouseUp, false); //事件会在鼠标按键被松开时发生。
webUI.attachEvent('onmousemove', onMouseMove, false); //事件会在鼠标指针移动时发生。
// webUI.attachEvent('onmouseover', onMouseOver, false); //事件会在鼠标指针移动到指定的对象上时发生。
// webUI.attachEvent('onmouseout', onMouseOut, false); //事件会在鼠标指针移出指定的对象时发生。
}
else { //Other(IE,FireFox,Chrome,Opera等,绝大部分浏览器支持方法 onclick 监听)
alert("您的当前的浏览器可能是:……");
// webUI.οnclick=onMouseClick;
// webUI.οnmοusedοwn=OnMouseDown;
// webUI.οnmοuseup=OnMouseUp;
webUI.οnmοusemοve=onMouseMove;
// webUI.οnmοuseοver=onMouseOver;
// webUI.οnmοuseοut=onMouseOut;
}
//定义实时鼠标坐标
var mouseX = 0;
var mouseY = 0;
//鼠标移动事件
function onMouseMove(event){
//先停止小球运动
if( timer != null ){
cancelAnimationFrame(timer);
}
//更新坐标
mouseX = event.clientX + document.body.scrollLeft - document.body.clientLeft;
mouseY = event.clientY + document.body.scrollTop - document.body.clientTop;
//开启小球运动
timer = requestAnimationFrame(drawFrame);
}
//定义帧事件(兼容性写法)
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
//取消帧事件(兼容性写法)
window.cancelAnimationFrame = (function () {
return window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
function (timer) {
window.clearTimeout(timer);
};
})();
//监听帧事件,帧渲染和帧绘制的变量
var timer = null;
//根据椭圆路径,执行小球运动
function drawFrame(){
for(var i=0; i<ball_arr.length; i++){
var ball = ball_arr[i];
//显示小球
ball.style.display = "block";
//转化角度为弧度制
var angel = ((path_angel * Math.PI / 180) + (i * ball_gap)) % 360;
//更新小球横坐标
ball.style.left = path_length * Math.cos(angel) + mouseX - ball.style.width/2 - document.body.clientLeft + "px";
//更新小球纵坐标
ball.style.top = path_width * Math.sin(angel) + mouseY - ball.style.height/2 - document.body.clientTop + "px";
//根据增量更新角度
path_angel = (path_angel + angel_increase) % 360;
}
//再次渲染
timer = requestAnimationFrame(drawFrame);
}
</script>
</body>
</html>