前面的话

  运动除了直线运动和曲线运动两种运动形式外,还有一种运动形式是鼠标跟随运动,而这种跟随运动需要用到三角函数的相关内容或者需要进行比例运算。本文将以几个小实例来介绍鼠标跟随运动的相关内容

眼球转动

  在很多网页中,都存在着跟随运动,比如眼球转动。鼠标在网页中移动时,眼球也会跟着朝相应方向转动

  上面是眼球转动的示意图,(x0,y0)是眼球的位置,而(x,y)是鼠标的位置。设直线与垂直方向的夹角为a,假设圆心点坐标为(0,0),可以得到以下公式

    tan(a) = x/y = x0/y0x0 = r*sin(a) y0 = r*cos(a)

  在mousemove事件中,可以很容易的得到鼠标位置(x,y),由此求出夹角a,进而可以求出眼球的位置

  设左眼为ball1,右眼为ball2。左眼的圆心坐标是(39,72),右眼的圆心坐标是(106,68),眼球可以移动的半径是12px

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#test{position: absolute;top: 100px;left: 200px;}
#ball1{position: absolute;top: 62px;left: 28px;}
#ball2{position: absolute;top: 58px;left: 96px;}
</style>
</head>
<body>
<div id="test"><img src="head.png" alt="body">   <img id="ball1" src="ball.png" alt="ball"><img id="ball2" src="ball.png" alt="ball">
</div>
<script>
//声明脑袋的默认偏移
var offsetLeft = test.offsetLeft;
var offsetTop = test.offsetTop;
//声明左眼夹角a1、右眼夹角a2
var a1,a2;
//声明左眼圆心(X1,Y1)、右眼圆心(X2,Y2)
var X1 = 38,Y1 = 72,X2 = 106,Y2 = 68;
//声明半径
var R = 12;
document.onmousemove = function(e){e = e || event;//获取鼠标坐标var x = e.clientX;var y = e.clientY;//更新夹角a1、a2
    a1 = Math.atan2(x-X1-offsetLeft,y-Y1-offsetTop);a2 = Math.atan2(x-X2-offsetLeft,y-Y2-offsetTop);//更新左眼、右眼的left、top值
    ball1.style.left = R*Math.sin(a1) + X1 -10 + 'px';ball1.style.top = R*Math.cos(a1) + Y1 -10+ 'px';ball2.style.left = R*Math.sin(a2) + X2 -10 + 'px';ball2.style.top = R*Math.cos(a2) + Y2 -10 + 'px';
}
</script>
</body>
</html>

苹果菜单

  苹果菜单中也存在着鼠标跟随运动,与鼠标距离越近的菜单项的宽高越大,越远则宽高越小

  鼠标坐标可以通过mousemove事件中的clientX和clientY获得。菜单项的坐标其实是已知项。而鼠标坐标与菜单项的距离就是要求的距离,而距离与菜单项的宽高成反比

  [注意]不能够将元素的自定义属性命名为x,因为x已经被浏览器使用

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
body{margin: 0;}
#test{position: absolute;bottom:10px;width: 100%;text-align: center;}
img{width: 64px;height: 64px;}
</style>
</head>
<body>
<div id="test"><img id="img1" src="img/1.png"><img src="img/2.png"><img src="img/3.png"><img src="img/4.png"><img src="img/5.png">
</div>
<script>
//声明菜单项的宽高值
var offsetWidth = img1.offsetWidth;
var offsetHeight = img1.offsetHeight;
//声明外层盒子的left、top值
var offsetLeft = test.offsetLeft;
var offsetTop = test.offsetTop;
//获取菜单项
var imgs = test.getElementsByTagName('img');
document.onmousemove = function(e){e = e || event;//更新鼠标位置var x = e.clientX;var y = e.clientY;for(var i = 0; i < imgs.length; i++){//获取菜单项的坐标
        imgs[i].x0= imgs[i].offsetLeft+offsetLeft+imgs[i].offsetWidth/2;
        imgs[i].y0 = imgs[i].offsetTop + offsetTop + imgs[i].offsetHeight/2;//更新鼠标与菜单项的距离
        imgs[i].len =Math.sqrt((x-imgs[i].x0)*(x-imgs[i].x0) + (y-imgs[i].y0)*(y-imgs[i].y0));//限制范围if(imgs[i].len > 150){imgs[i].len = 150;}//更新菜单项的宽高
        imgs[i].style.width = (1-imgs[i].len/300)*2*offsetWidth + 'px';
        imgs[i].style.height = (1-imgs[i].len/300)*2*offsetHeight + 'px';
        }
}
</script>
</body>
</html>

方向跟随

  有许多网页都有方向跟随的效果。鼠标从哪个方向移入,元素就跟着从哪个方向移入。鼠标从哪个方向移出,类似地,元素也跟着从哪个方向移出

  移入移出的运动效果使用匀速直线运动即可,这里主要需要判断方向

  由示意图中所示,可以把一个正方形的元素分成(上-右、上-左、左-上、左-下、下-右、下-左、右-上、右-下)这8个部分,每个部分是一个等腰直角三角形,当元素进入某个区域时,横线前面的方向就表示元素的方向

  假设正方形的中心坐标为(x0,y0),动态元素(move)进入时的坐标为(x,y),以这两个坐标组成的直线与水平正方向的直线的夹角作为基准角,假设为a,则通过确定夹角a的范围,可以确定动态元素(move)进入的方向

  -45<a<45时,进入方向为右

  45<a<135时,进入方向为上

  a>135或a<-135时,进入方向为左

  -135<a<-45时,进入方向为下

  确定好动态元素(move)进入的方向后,需要根据方向,将动态元素(move)瞬间变换到对应的位置。然后,动态元素(move)进行匀速直线运动,最终停止在与静态元素(test)重合的位置

  动态元素(move)移出静态元素(test)的范围时,要注意的是,并不会触发静态元素(test)的mouseout事件。因为,此时鼠标一直处于动态元素(move)上。所以,触发的是动态元素(move)的mouseout事件

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#box{overflow: hidden;position: relative;left: 100px;top: 100px;height: 100px;width: 300px;}
.test{width: 100px;height: 100px;position: absolute;font:20px/100px '宋体';text-align: center;}
</style>
</head>
<body>
<div id="box"><div class="test" style="top: 0px;left: 0px;background-color: pink;">1</div><div class="test" style="top: 0px;left: 100px;background-color: lightcoral;">2</div><div class="test" style="top: 0px;left: 200px;background-color: lightgreen;">3</div>  <div id="move" style="width: 100px;height: 100px;background-color: lightblue;position: absolute;top: -100px;left: -100px;"></div>
</div>
<script>
var tests = box.getElementsByTagName('div');
for(var i = 0; i < tests.length; i++){tests[i].onmouseover = fn;
}
//鼠标移出动态元素(move)时,再将fn()函数置于所有静态元素上
move.onmouseout = fn;
function fn(e){e = e || event;//阻止冒泡if(e.stopPropagation){e.stopPropagation();}else{e.cancelBubble = true;}for(var i = 0; i < tests.length; i++){tests[i].onmouseover = fn;}var _this = this;//鼠标移入动态元素(move)时,将静态元素上的mouseover事件置空
    move.onmouseover = function(){move.innerHTML = _this.innerHTML;_this.onmouseover = null;}//声明坐标var x = e.clientX;var y = e.clientY;//声明静态元素(test)左上角坐标(相对于父级)var x11 = this.offsetLeft;var y11 = this.offsetTop;    //声明静态元素(test)中心点坐标(相对于父级)var x10 = x11 + this.offsetWidth/2;var y10 = y11 + this.offsetHeight/2;   //声明静态元素(test)左上角坐标(相对于文档)var x21 = this.parentNode.offsetLeft + x11;var y21 = this.parentNode.offsetTop + y11;//声明静态元素(test)中心点坐标(相对于文档)var x20 = x21 + this.offsetWidth/2;var y20 = y21 + this.offsetHeight/2;//声明静态元素宽高var height = this.offsetHeight;var width = this.offsetWidth;//声明并计算夹角var a = Math.atan2(y20-y,x-x20)*180/Math.PI;//声明并计算方向var dir;if(a > -45 && a < 45){dir = 'right';}else if(a > 45 && a < 135){dir = 'top';}else if(a > -135 && a < 45){dir = 'bottom';}else{dir = 'left';}//鼠标移入时if(e.type == 'mouseover'){//更新动态元素(move)的初始位置//移动动态元素(move)直到完全覆盖静态元素(test)if(dir == 'right'){move.style.left = x10 + width/2 + 'px';
            move.style.top = y10 - height/2 + 'px';
            fnMove(move,'left',x11)}else if(dir == 'top'){move.style.left = x10 - width/2 + 'px';
            move.style.top = y10 - height/2 - height + 'px';
            fnMove(move,'top',y11)}else if(dir == 'left'){move.style.left = x10 - width/2 - width + 'px';
            move.style.top = y10 - height/2 + 'px';
            fnMove(move,'left',x11)         }else{move.style.left = x10 - width/2 + 'px';
            move.style.top = y10 - height/2 + height + 'px';
            fnMove(move,'top',y11)        }          }if(e.type == 'mouseout'){//鼠标移出时if(dir == 'right'){fnMove(move,'left',x11 + width);}else if(dir == 'top'){fnMove(move,'top',y11 - height);}else if(dir == 'left'){fnMove(move,'left',x11 - width);}else{fnMove(move,'top',y11 + height);}          }
}
function getCSS(obj,style){if(window.getComputedStyle){return getComputedStyle(obj)[style];}return obj.currentStyle[style];
}
function fnMove(obj,attr,target){var H = obj.offsetHeight;if(obj.timer) return;var cur = parseFloat(getCSS(obj,attr));if(target > cur){var step = H/4;
    }else{var step = -H/4;
    }obj.timer = setInterval(function(){cur = parseFloat(getCSS(obj,attr));cur = cur + step;if((cur -target) * step >= 0){cur = target;  }obj.style[attr] = cur + 'px'; if(cur == target){clearInterval(obj.timer);obj.timer = 0;}},20);
}
</script>
</body>
</html>

3Dhover

  下面是一个3Dhover效果

<style>
.outer{perspective: 1000px;}
.inner{height: 200px;width: 200px;background-color: tan;box-shadow: 3px 3px 6px -3px black;}
</style>
<div class="outer"><div class="inner"></div>
</div>
<script>
var oDiv = document.getElementsByClassName('inner')[0];
oDiv.onmousemove = function(e){e = e || event;var x = e.clientX - this.offsetLeft;var y = e.clientY - this.offsetTop;var x0 = this.offsetWidth/2;var y0 = this.offsetHeight/2;var percentX = (x - x0)/x0;var percentY = (y - y0)/y0;this.style.transform = 'rotateX('+ (5*-percentY) +'deg)' + 'rotateY('+ (5*percentX) +'deg)';
}
oDiv.onmouseleave = function(e){this.style.transform = 'none';
}
</script>

javascript运动系列第七篇——鼠标跟随运动相关推荐

  1. javascript运动系列第八篇——碰壁运动

    前面的话 碰撞运动可能是运动系列里面比较复杂的运动了.碰撞可以分为碰壁和互碰两种形式,而碰撞前后的运动形式也可以分为变速和匀速两种,如果再涉及到阻力,具有速度损耗的话,就更加复杂了.本文先介绍碰壁运动 ...

  2. 深入理解javascript作用域系列第四篇——块作用域

    前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀.简洁的 ...

  3. 深入理解javascript作用域系列第三篇

    前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javascript作用域系列第三篇--声明提升(hois ...

  4. javascript面向对象系列第三篇——实现继承的3种形式

    前面的话 学习如何创建对象是理解面向对象编程的第一步,第二步是理解继承.开宗明义,继承是指在原有对象的基础上,略作修改,得到一个新的对象.javascript主要包括类式继承.原型继承和拷贝继承这三种 ...

  5. 【JavaScript 教程系列第 10 篇】判断一个数是整数还是小数

    这是[JavaScript 教程系列第 10 篇],如果觉得有用的话,欢迎关注专栏. 思路 如果一个数是整数,那么 parseInt() 函数和 parseFloat() 函数的返回值是相同的,反之返 ...

  6. 【JavaScript 教程系列第 8 篇】什么是闰年?判断某一年是不是闰年

    这是[JavaScript 教程系列第 8 篇],如果觉得有用的话,欢迎关注专栏. 闰年分为 普通年:能被 4 整除且不能被 100 整除的是闰年. 世纪年:能被 400 整除的是闰年. 依据这两个判 ...

  7. 转载 解密蓝牙mesh系列 | 第七篇【低功耗蓝牙】【承载层(bearer layer)】【节点与特性】【代理节点(Proxy Node)】【蓝牙mesh代理服务】【发现代理节点】【代理协议】

    SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机.32位ARM Cortex-M0处理器.128kB Flash存储器.以及丰富的数字接口.SYD8801片上集成了 ...

  8. 奋斗的小孩系列 FPGA学习altera系列: 第七篇 添加激励及功能仿真操作

    奋斗的小孩系列 FPGA学习altera系列: 第七篇 添加激励及功能仿真操作 作者:奋斗的小孩 郝旭帅(转载请注明出处) 大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分.大侠可以关注FPG ...

  9. javascript运动系列第九篇——碰撞运动

    前面的话 碰撞可以分为碰壁和互碰两种形式,上篇介绍了碰壁运动,本文将从浅入深地介绍碰撞运动的互碰形式 碰撞检测 对于互碰形式的碰撞运动来说,首先要解决的是碰撞检测.对于矩形元素的碰撞检测前面的博文已经 ...

最新文章

  1. 开源社区的危机:拒绝被“白嫖”?2大著名项目遭作者破坏
  2. 自动驾驶感知中的深度学习
  3. nhibernate入门系列: 使用Criteria载入对象
  4. jdk1.8hashmap为什么对hash进行了一次扰动处理
  5. CSP认证201512-4 送货[C++题解]:无向图欧拉路径、并查集、dfs
  6. Android 开机自动启动服务
  7. AutoLayout(自动布局)详细教程
  8. 对比损失的PyTorch实现详解
  9. 【语义分割】论文代码资料汇总
  10. 农产品管理系统-毕设
  11. 令人啼笑皆非的不靠谱产品是如何诞生的?
  12. Cuba Platform Introduce (Cuba平台介绍)
  13. 记《浪潮之巅》-第一版-6.IT业的罗马帝国--微软,Microsoft
  14. Tesseract文字训练,以及样本生成
  15. 开放式激光振镜运动控制器:C++振镜矫正方法与实现
  16. 【REACT-受控组件和非受控组件】
  17. 三维实景地图智慧园区3D可视化物联商迪3D网平台制作
  18. latex中文小标题_LaTeX标题怎样使用中文?
  19. 基于java的城市公交查询管理系统
  20. js 截取指定字符后面/前面的所有字符串

热门文章

  1. java 中random类使用_Java中的天使和魔鬼:Unsafe类
  2. 卸载mongodb_【数据库】mongodb数据库安装
  3. 服务器不重启磁盘修复,重启后数据盘不见了?别担心,只是磁盘脱机
  4. python常用方法总结-Python3常用函数、方法总结(持续更新…)
  5. android intent.putextras,关于android:如何使用putExtra()和getExtra()来表示字符串数据
  6. java孙膑和庞涓问题_庞涓与孙膑的故事
  7. android support design library eclipse,Eclipse下使用Android Design Support Library中的控件
  8. ssm整合2 增删改
  9. python和c语言哪个效率高_C语言和Python哪个更容易学?
  10. Python3--unitest框架的使用