原生javascript写自己的运动库(匀速运动篇)
网上有很多JavaScript的运动库,这里和大家分享一下用原生JavaScript一步一步写一个运动函数的过程,如读者有更好的建议欢迎联系作者帮助优化完善代码。这个运动函数完成后,就可以用这个运动函数写轮播、选项卡、滚动文字的特效。运动的模式有很多,如匀速运动、匀加速运动、变加速运动等等。这里我们以匀速运动为例。
现在我们先用HTML和CSS将我们的运动对象建好
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>move</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
<style type="text/css">
.box1{width:100px; height: 100px; background: red; position: absolute; left: 0px; top:50px;opacity:1; filter: alpha(opacity=100);}
span{display: block; width:1px; height:300px; background:black; position: absolute; left: 500px; top: 0;}
</style>
</head>
<body>
<div class="box1"></div>
<span></span>
</body>
</html>
打开浏览器会看到一个红色的块,和一条黑色的线,现在我们要让红色的左边线移动到黑线处。那么怎么移动过去呢?如果用老式电影播放机看过电影,应该知道我们看的电影实际上是有一张张的图片组成的,这些图片播放的速度很多也就成了一组连续的动作。我们这里的运动函数也是基于这种原理。
我们先获取当前left的位置,然后设定一个定时器,定时器执行一次left加10个像素,这样红色部分就可以向右运动了。这里我们设置定时器16毫秒执行一次,也就相当于1秒钟执行62.5次,大概就是60帧。在执行left加10个像素前我们还要进行一个判断用来判断何时关定时器。判断条件是如果当前left到目标点的距离小于10就关闭定时器,并让left的值等于目标值。
现在开始写JavaScript运动函数
window.onload = function(){
var oDiv = document.getElementsByTagName('div')[0];
oDiv.onclick = function(){
startMove();
}
}
var timer = null;
function startMove(target){
//获取运动目标
var oDiv = document.getElementsByTagName('div')[0];
timer = setInterval(function(){
//如果当前left到目标点的距离(500)小于10就关闭定时器;
if(500-oDiv.offsetLeft<10){
clearInterval(timer)
oDiv.style.left = 500 + 'px';
}else{
oDiv.style.left = oDiv.offsetLeft + 10 + 'px';
}
}, 16);
}
这样我们就可以完成了一个向右运动的过程,但是不完美,只能向右运动(这里的10如果是-10就可以向左运动),只能运动到函数内指定的位置(即目标值),我们用speed来替换10,用target替换500,target由函数参数传入。speed=(目标值-当前值)/60,这里的60相当于是每秒60帧。目标值>当前值,speed就是一个正数,当前值向目标值靠近;目标值<当前值,speed就是一个负数,当前值也是向目标值靠近。我们用于判断的条件换成绝对值比较。
优化后的代码
window.onload = function(){
var oDiv = document.getElementsByTagName('div')[0];
oDiv.onclick = function(){
startMove(500);
}
}
var timer = null;
function startMove(target){
//获取运动目标
var oDiv = document.getElementsByTagName('div')[0];
var speed = (target-oDiv.offsetLeft)/60;
timer = setInterval(function(){
//如果当前left到目标点的距离(target)小于speed就关闭定时器;
if(Math.abs(target-oDiv.offsetLeft)<Math.abs(speed)){
clearInterval(timer)
oDiv.style.left = target + 'px';
}else{
oDiv.style.left = oDiv.offsetLeft + speed + 'px';
}
}, 16);
}
测试div的left为0或者1000的时候,都运动到了目标位置500,但是还是不完美,只能移动left,如果要移动top还得改函数内部的left,这里我们将移动属性也作为参数传入函数。offsetLeft也换个形式,我们引入一个获取对象属性值的函数
window.onload = function(){
var oDiv = document.getElementsByTagName('div')[0];
oDiv.onclick = function(){
startMove('top', 500); }
} //获取对象属性值
function getStyle(obj, attr){
if(window.getComputedStyle){
return window.getComputedStyle(obj, false)[attr];
}else{
return obj.currentStyle[attr];
}
}
var timer = null; //运动函数
function startMove(attr, target){
//获取运动目标
var oDiv = document.getElementsByTagName('div')[0];
var speed = (target-parseFloat(getStyle(oDiv, attr)))/60; timer = setInterval(function(){
var fCur = parseFloat(getStyle(oDiv, attr));
//如果当前位置到目标点的距离,小于speed就关闭定时器;
if(Math.abs(target-fCur)<Math.abs(speed)){
clearInterval(timer)
oDiv.style[attr] = target + 'px';
}else{
oDiv.style[attr] = fCur + speed + 'px';
} }, 16);
}
现在我们已经可以做多个属性值的运动了,但是还是有bug,运行下列代码试试
window.onload = function(){
var oDiv = document.getElementsByTagName('div')[0];
oDiv.onmouseover = function(){
startMove('width', 500);
}
oDiv.onmouseout = function(){
startMove('width', 100);
}
}
你会发现有时不受控制,这是因为我们对同一个对象开了多个定时运动导致的,我们再加一行代码clearInterval(timer);加在timer = setInterval()之前,在每个运动的定时器开启前去除其他的定时器,这样问题就解决了。但是还是不完美,只能一个对象运动,现在我要让多个对象运动,就不行,那是因为运动对象在我们的函数里,现在我们将运动对象也作为参数传进函数。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>move</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
<style type="text/css">
.box1{width:100px; height: 50px; background: red; position: absolute; left: 0px; top: 0px;opacity:1; filter: alpha(opacity=100);/* border: 1px solid black;*/}
.box2{width:100px; height: 50px; background: red; position: absolute; left: 0; top: 80px;}
.box3{width:100px; height: 50px; background: red; position: absolute; left: 0; top: 160px;}
span{display: block; width:1px; height:300px; background:black; position: absolute; left: 500px; top: 0;}
</style>
</head>
<body>
<div class="box1"></div>
<div class="box2"></div>
<div class="box3"></div>
<span></span>
</body>
</html>
html修改测试多物体运动
window.onload = function(){
var oDiv = document.getElementsByTagName('div');
oDiv[0].onmouseover = function(){
startMove(this, 'width', 500);
}
oDiv[0].onmouseout = function(){
startMove(this, 'width', 100);
}
oDiv[1].onmouseover = function(){
startMove(this, 'width', 500);
}
oDiv[1].onmouseout = function(){
startMove(this, 'width', 100);
}
oDiv[2].onmouseover = function(){
startMove(this, 'width', 500);
}
oDiv[2].onmouseout = function(){
startMove(this, 'width', 100);
}
} //获取对象属性值
function getStyle(obj, attr){
if(window.getComputedStyle){
return window.getComputedStyle(obj, false)[attr];
}else{
return obj.currentStyle[attr];
}
} //运动函数
function startMove(obj, attr, target){
var speed = (target-parseFloat(getStyle(obj, attr)))/60;
clearInterval(obj.timer); obj.timer = setInterval(function(){
var fCur = parseFloat(getStyle(obj, attr));
//如果当前位置到目标点的距离,小于speed就关闭定时器;
if(Math.abs(target-fCur)<Math.abs(speed)){
clearInterval(obj.timer)
obj.style[attr] = target + 'px';
}else{
obj.style[attr] = fCur + speed + 'px';
} }, 16);
}
现在可以进行多物体的运动了,但是一个物体运动时其他物体就停止了运动,那是因为多个物体公用一个定时器导致的,我们再对代码进行改进,给每个对象添加属于自己的定时器。
即把startMove函数里的所有timer改成obj.timer就可以进行多个物体同时运动了。但是还是不完美,一个对象一次只能运动一个属性,不能同时改变多个属性,现在我们将startMove的参数attr和target用json数据对象传入。代码修改如下:
window.onload = function(){
var oDiv = document.getElementsByTagName('div');
//测试一个对象同时运动对个属性
oDiv[0].onclick = function(){
startMove(this, {'width':50, 'left':500, 'height':200, 'top':100});
}
} //获取对象属性值
function getStyle(obj, attr){
if(window.getComputedStyle){
return window.getComputedStyle(obj, false)[attr];
}else{
return obj.currentStyle[attr];
}
} //运动函数
function startMove(obj, json){
var speed = {};
//给每个属性设一个速度
for (var attr in json){
speed[attr] = (json[attr]-parseFloat(getStyle(obj, attr)))/60;
}
clearInterval(obj.timer);attr obj.timer = setInterval(function(){
var fCur = 0;
var btn = true;
for(var attr in json){
fCur = parseFloat(getStyle(obj, attr));
//判断运动的完成条件,
if(Math.abs(fCur-json[attr])<Math.abs(speed[attr])){
btn = false;
}
obj.style[attr] = fCur + speed[attr] + 'px';
}
//因为速度是根据(目标值-当前值)/60来确定的,所以每个属性的完成时间是一样的
//在运动完成时给所以属性值设置为目标值,并清楚定时器。
if(btn == false){
for(var attr in json){
obj.style[attr] = json[attr] + 'px';
}
clearInterval(obj.timer);
} }, 16);
}
到现在,一个运动函数基本完成了,但还有优化的地方。可以给函数增加一个参数,参数是一个函数,即要求运动完成后执行一个函数。另外我们的运动函数不能修改透明度,我们对透明度单独处理,实现透明度的修改就可以做淡入谈出了。如果还想控制运动的快慢可以在参数json中增加一个时间参数,为了框架的通用性,稍微有一点点复杂,这也是为了框架可以应对更多的需求。现在来修改我们的运动函数,使它可以完成上述三个功能。
window.onload = function(){
var oDiv = document.getElementsByTagName('div');
//测试一个对象同时运动对个属性
oDiv[0].onclick = function(){
startMove(this, {'width':50, 'left':500,'time':3000, 'opacity':0.3, 'height':200, 'top':100}, function(){
startMove(oDiv[0], {'width':200, 'left':1000, 'opacity':1, 'height':50, 'top':0})
});
}
} //获取对象属性值
function getStyle(obj, attr){
if(window.getComputedStyle){
return window.getComputedStyle(obj, false)[attr];
}else{
return obj.currentStyle[attr];
}
} //运动函数
function startMove(obj, json, fun){
//设置默认的运动频率
var iFrame = 60;
var velocity = {};
//判断时候有时间属性,有就设置用户iFrame
if (json['time']){
iFrame = parseInt(json['time'])/16;
}
//删除时间属性
delete json['time']
//给每个属性设一个速度
for (var attr in json){
velocity[attr] = (json[attr]-parseFloat(getStyle(obj, attr)))/iFrame;
} clearInterval(obj.timer);attr
//开启定时器,开始运动
obj.timer = setInterval(function(){
var fCur = 0;
var btn = true;
for(var attr in json){
//获取当前属性值
fCur = parseFloat(getStyle(obj, attr));
//判断运动的完成条件,
if(Math.abs(fCur-json[attr])<Math.abs(velocity[attr])){
btn = false;
}
//判断attr如果是opacity则执行透明度变化的代码,否则执行其他属性值变化代码
if(attr=='opacity'){
obj.style[attr] = fCur + velocity[attr];
obj.style.filter = 'alpha(opacity=' + (fCur + velocity[attr])*100 + ')';
}else{
obj.style[attr] = fCur + velocity[attr] + 'px';
}
}
//因为速度是根据(目标值-当前值)/60来确定的,所以每个属性的完成时间是一样的
//在运动完成时给所以属性值设置为目标值,并清楚定时器。
if(btn == false){
for(var attr in json){
if(attr=='opacity'){
obj.style[attr] = json[attr];
obj.style.filter = 'alpha(opacity=' + (json[attr])*100 + ')';
}else{
obj.style[attr] = json[attr] + 'px';
}
}
//清楚定时器,并判断是否执行函数
clearInterval(obj.timer);
if(fun){
fun();
}
}
}, 16);
}
这样我们的匀速运动函数就完成了,有了它就可以写轮播、文字滚动等运动特效了。后期会增加变速运动部分,读者也可自行修改代码,使其可以做变速运动。现将我们最终版的匀速运动函数提取出来放在一个单独的js文件中,这样以后直接在html文件中导入就行了。将函数名startMove改成uniformMotion(匀速运动),js文件名为motion.js
//获取对象属性值
function getStyle(obj, attr){
if(window.getComputedStyle){
return window.getComputedStyle(obj, false)[attr];
}else{
return obj.currentStyle[attr];
}
} //运动函数
function uniformMotion(obj, json, fun){
//设置默认的运动频率
var iFrame = 60;
var velocity = {};
//判断时候有时间属性,有就设置用户iFrame
if (json['time']){
iFrame = parseInt(json['time'])/16;
}
//删除时间属性
delete json['time']
//给每个属性设一个速度
for (var attr in json){
velocity[attr] = (json[attr]-parseFloat(getStyle(obj, attr)))/iFrame;
} clearInterval(obj.timer);attr
//开启定时器,开始运动
obj.timer = setInterval(function(){
var fCur = 0;
var btn = true;
for(var attr in json){
//获取当前属性值
fCur = parseFloat(getStyle(obj, attr));
//判断运动的完成条件,
if(Math.abs(fCur-json[attr])<Math.abs(velocity[attr])){
btn = false;
}
//判断attr如果是opacity则执行透明度变化的代码,否则执行其他属性值变化代码
if(attr=='opacity'){
obj.style[attr] = fCur + velocity[attr];
obj.style.filter = 'alpha(opacity=' + (fCur + velocity[attr])*100 + ')';
}else{
obj.style[attr] = fCur + velocity[attr] + 'px';
}
}
//因为速度是根据(目标值-当前值)/60来确定的,所以每个属性的完成时间是一样的
//在运动完成时给所以属性值设置为目标值,并清楚定时器。
if(btn == false){
for(var attr in json){
if(attr=='opacity'){
obj.style[attr] = json[attr];
obj.style.filter = 'alpha(opacity=' + (json[attr])*100 + ')';
}else{
obj.style[attr] = json[attr] + 'px';
}
}
//清楚定时器,并判断是否执行函数
clearInterval(obj.timer);
if(fun){
fun();
}
}
}, 16);
}
成功的背后离不开辛勤的付出。
原生javascript写自己的运动库(匀速运动篇)的更多相关文章
- 原生JavaScript写AJAX
前端JavaScript: function ajaxGet(url, obj) { var request; if(window.XMLHttpRequest) { request = new XM ...
- 原生javascript写的侧栏跟随效果
浏览网站时经常看到有的网站上,当一个页面很长的时候,设定侧栏内容会跟随滚动条滚动,我们把这种效果叫做“侧栏跟随滚动”.这种特效对提高网站浏览量.文章点击率.广告点击量都有一定效果. 侧栏跟随滚动的实现 ...
- javascript学习-原生javascript的小特效(原生javascript实现链式运动)
以下代码就不详细解析了,在我之前的多个运动效果中已经解析好多次了,重复的地方这里就不说明了,有兴趣的童鞋可以去看看之前的文章<原生javascript的小特效> <!DOCTYPE ...
- 用原生JavaScript写AJAX
//原生js写ajax就像打电话 //打电话分下面4步//1.拿出手机//2.拨号//3.说话//4.听对方说话 //ajax也分下面4步//1.创建ajax对象//2.连接到服务器//3.发送请求( ...
- 原生JavaScript写select下拉选择后跳转页面
<select name="molsel_oprate" onchange="javascript:var obj = event.target; var inde ...
- 用原生javascript写出jquery中slideUp和slideDown效果
设置块级元素的CSS属性overflow为hidden,然后动态改变height即可 var header=document.getElementsByTagName('header')[0]; he ...
- 自己用原生JS写的轮播图,支持移动端触摸滑动,分页器圆点可以支持mouseover鼠标移入和click点击,高手看了勿喷哈
自己用原生JavaScript写的轮播图,分页器圆点按钮可支持click点击,也可支持mouseover鼠标悬浮触发,同时支持移动端触摸滑动,有兴趣的友友可以试试哈,菜鸟一枚,高手看了勿喷,请多多指正 ...
- 自己用原生JS写的轮播图,支持移动端触屏滑动,面向对象思路。分页器圆点支持click和mouseover。
自己用原生javascript写的轮播图,面向对象思路,支持移动端手指触屏滑动.分页器圆点可以选择click点击或mouseover鼠标移入时触发.图片滚动用的setInterval,感觉setInt ...
- 原生JavaScript运动功能系列(五):定时定点运动
原生JavaScript运动功能系列(一):运动功能剖析与匀速运动实现 原生JavaScript运动功能系列(二):缓冲运动 原生JavaScript运动功能系列(三):多物体多值运动 原生JavaS ...
随机推荐
- java 如何自定义异常 用代码展示 真心靠谱
先建两个自定义的异常类 ChushufuException类 class ChushufuException extends Exception { public ChushufuException( ...
- [案例]某体育用品公司在零售领域BI的产品应用解决方案
随着某体育用品公司集团经营规模的不断扩大,信息化的建设也在不断的深入,从POS系统到ERP系统,从MAIL系统到OA系统,整个集团的每项工作都与信息系统密不可分,可以说是行业内信息化建设的先导者.但是 ...
- [Ext.Net]客户关系管理系统
本人在企业中非专业人士,交流学习. 1.登录 2.系统主界面 3.用户与角色 3.菜单管理 4.角色与授权 5.登陆日志 6.简易工作流 7.客户分类 8.客户管理 9.报价管理 业务员反馈 报价明细 ...
- my project 中git使用过程(基本操作流程)
1.g it clone git@name:server/BM/APPS.git 则BM_APPS.git项目被下载到当前目录下了,这时git@name:server/BM/APPS.git就是自己 ...
- 将studio项目 转换为eclipse项目
总会有些奇怪的事情,比如,有的人就有将studio项目 转换为eclipse项目的需求 首先,不要因为编译原因而放弃.studio项目是完全可以转换成eclipse的 本站的开源代码板块有很多项目都是 ...
- 说说struts2中拦截器的请求流程一(模拟大致流程)
本文可作为北京尚学堂struts2课程的学习笔记. 首先 什么是拦截器?拦截器能干什么? 拦截器,顾名思义就是拦截对象然后做操作的东西,至于是拦截谁?那自然是拦截action了.能做什么操作呢?你想让 ...
- 解决winform窗体闪烁问题
如果你在Form中绘图的话,不论是不是采用的双缓存,都会看到图片在更新的时候都会不断地闪烁,解决方法就是在这个窗体的构造函数中增加以下三行代码: 请在构造函数里面底下加上如下几行: SetStyle( ...
- windows linux—unix 跨平台通信集成控制系统
首先,我们可以用到这个开源的开发包: mdk(Micro-Development-Kit)微量级软件开发包,提供几个常用类,主要实现了一个高性能的并发服务器引擎 使用c++开发,是一个跨平台的开发包, ...
- 如何使用VS2013本地C++单元测试框架
在VS2013中,可以使用VS自带的C++单元测试框架. 在使用该框架前,需要先安装Unit Test Generator(可以通过菜单“工具->扩展和更新”搜索安装). 下边,就阐述一下利用该 ...
- Linux - grep的一些进阶选项
[root@www ~]# grep [-A] [-B] [--color=auto] '搜寻字串' filename 选项与参数: -A :后面可加数字,为 after 的意思,除了列出该行外,后续 ...