前端初学-动画函数封装(一)

本人所有文章均为学习过程中的总结,不同程度地参考了互联网上其他的文章或书籍,如果不正恳请指出,共同进步

好了,让我们开始吧。

  • 函数最终代码(初步)

      //elem:要移动的元素 fianl_x final_y 目的地坐标(相对于父盒子)
      function animate(elem,final_x,final_y){
          clearInterval(elem.timer);
          elem.timer = setInterval(function(){
              var x = parseInt(elem.style.left)||0;
              var y = parseInt(elem.style.top)||0;
              if(x==final_x&&y==final_y){
                  clearInterval(elem.timer);
                  return true;
              }
              var distx=(final_x-x)/10;
              distx=distx>0?Math.ceil(distx):Math.floor(distx);
              x=x+distx;
              var disty=(final_y-y)/10;
              disty=disty>0?Math.ceil(disty):Math.floor(disty);
              y=y+disty;
              elem.style.left=x+"px";
              elem.style.top=y+"px";
          },25);
      }
    
  1. 准备工作
    首先,要移动的元素必须是绝对定位的,前面也说了,我们是不断地在一个极小的时间不断的更新元素的横竖位置(相对于父盒子),而符合我们要求的便是绝对定位了,通过更改left,right,top,bottom等属性的值来更改元素的位置。参数里有元素的最终位置,我们也需要获取元素的初始位置。
    注意:此处要转为整型,因为ele.style.left获取的是一个字符串,带单位"px".
    function animate(elem,final_x,fianl_y){
    var x,y;
    if(elem.style.left){
    x = parseInt(elem.style.left);
    }else{
    x = 0;
    }
    if(elem.style.top){
    y = parseInt(elem.style.top);
    }else{
    y = 0;
    }
    }

但是可能这个元素并没有设置left以及top的值,于是我们需要进行检测,如果没有这两个值则将它们设置为0(实际上一个绝对定位的元素如果没有设置值的话默认为0,0);

好啦,现在我们得到了元素的起始位置,那么什么时候这个函数才算完成了任务呢?没错,就是起始位置与终止位置相等时。于是我们加入函数结束的条件。

if(x==final_x&&y==final_y){//如果起始位置与目标位置一致
    return true;
}

接下来我们的问题就是该怎么移动这个元素了。

不断的移动一个微小的距离,造成动画的效果。

敲黑板,划重点。

“不断”。没错就是我们的setInterval(a,b)定时器。这个函数有两个参数,第一个参数a是要执行的代码块,第二个参数b是执行的间隔时间,这里我们让它每25ms执行一次吧。

我们的逻辑就是:每隔一段时间,获取一次元素当前位置,让元素移动一个微小的距离,如果当前位置等于目标位置,我们就退出setInterval函数,如果不等于,则重复执行代码块a。

现在的代码如下:

setInterval(function(){
    var x,y;
    if(elem.style.left){
         x = parseInt(elem.style.left);
    }else{
         x = 0;
    }
    if(elem.style.top){
         y = parseInt(elem.style.top);
    }else{
         y = 0;
    }
    if(x==final_x&&y==final_y){
        return true;
    }
},25);

"一个微小的距离"。即我们每个25ms让其移动的距离,这里我们取10px;

setInterval(function(){
    var x,y;
    if(elem.style.left){
         x = parseInt(elem.style.left);
    }else{
         x = 0;
    }
    if(elem.style.top){
         y = parseInt(elem.style.top);
    }else{
         y = 0;
    }
    if(x==final_x&&y==final_y){
        return true;
    }
    var dist = 10;//我们移动的距离
    if(x<fianl_x){//如果横向在目标位置左侧,则+10向目标位置前进
        x=x+10;
    }
    if(x>final_x){//如果横向在目标位置右侧,则-10向目标位置后退
        x=x-10;
    }
    if(y<fianl_y){//同上
        y=y+10;
    }
    if(y>fianl_y){
        y=y-10;
    }
},25);

然后再将增加或减少了的x,y重新赋值给元素的left以及top属性

elem.style.left=x+"px";
elem.style.top=y+"px";

这样,我们就初步完成了动画函数,每隔25ms将元素移动10px(x轴与y轴),如果当前位置等于了目标位置,则退出setInterval函数,animate执行完毕。此时代码清单如下:

function animate(elem,final_x,fianl_y){
    setInterval(function(){
        var x,y;
        if(elem.style.left){
             x = parseInt(elem.style.left);
        }else{
             x = 0;
        }
        if(elem.style.top){
             y = parseInt(elem.style.top);
        }else{
             y = 0;
        }
        if(x==final_x&&y==final_y){
            return true;
        }
        var dist = 10;//我们移动的距离
        if(x<fianl_x){//如果横向在目标位置左侧,则+10向目标位置前进
            x=x+10;
        }
        if(x>final_x){//如果横向在目标位置右侧,则-10向目标位置后退
            x=x-10;
        }
        if(y<fianl_y){//同上
            y=y+10;
        }
        if(y>fianl_y){
            y=y-10;
        }
    },25);
    elem.style.left=x+"px";
    elem.style.top=y+"px";
}

但是这串代码既不健壮也不简洁,还可以优化。

我们可以先简化一下代码:

function animate(elem,final_x,final_y){
    setInterval(function(){
        var x = parseInt(elem.style.left)||0;
        var y = parseInt(elem.style.top)||0;
        if(x==final_x&&y==final_y){
            return true;
        }
        var dist=10;
        if(x<fianl_x){//如果横向在目标位置左侧,则+10向目标位置前进
            x+=10;
        }
        if(x>final_x){//如果横向在目标位置右侧,则-10向目标位置后退
            x-=10;
        }
        if(y<fianl_y){//同上
            y+=10;
        }
        if(y>fianl_y){
            y-=10;
        }
        elem.style.left=x+"px";
        elem.style.top=y+"px";
    },25);
}

问题1. 移动距离每次都是固定的(10px),是匀速移动。我们想逼真一点,当距离目标位置越近时速度变得越慢,产生渐变的效果;

问题2. 定时器没有及时清除。

假如你先想通过调用animate函数将elem移动到目标位置1,在其还未到达的时候,又改变主意让它移动到目标位置2,再调用animate函数,传入了新的final_x,final_y,此时就会造成两个定时器同时运作,产生混乱。

定时器A想让它到目标位置1,定时器B想让它到目标位置2,获取的元素当前位置却是同一个,就会造成elem摇摆不定,所以要以后一次的调用为准,清除前一次的定时器。对了,到达目标位置后也要清除,总不能把人家白白搁在那。

要养成好习惯:要用定时器,先清定时器。用完定时器,清除定时器。

首先解决问题1。

var distx=(final_x-x)/10;
var disty=(final_y-y)/10;

此时,我们的移动的距离不再是相同的10px,而是根据目标位置与当前位置的差(距离差)来进行动态计算,当前位置距离目标位置越近我们移动的距离越小,每次移动距离差的十分之一,这样我们就不再是匀速运动了,距离差在不断减小,我们移动距离dist也在不断减小。
那些if也让人看着生厌,这时我们的distx,disty已经带有正负号,可以直接加在x,y上了。
/10要有取整处理,正负数的向上取整不同。代码如下:

var distx=(final_x-x)/10;
distx=distx>0?Math.ceil(distx):Math.floor(distx);
x=x+distx;
var disty=(final_y-y)/10;
disty=disty>0?Math.ceil(disty):Math.floor(disty);
y=y+disty;

问题1结束。

其次解决问题2。

我们可以知道,多个定时器都是作用在同一个元素上面,我们可以将每次进行的定时器的ID绑定在元素上,下一次执行新的定时器时将元素本身的定时器clear(也就是上一个定时器),再将此次定时器ID绑定即可。

最终代码:

function animate(elem,final_x,final_y){
    clearInterval(elem.timer);//要用定时器,先清定时器
    elem.timer = setInterval(function(){
        var x = parseInt(elem.style.left)||0;
        var y = parseInt(elem.style.top)||0;
        if(x==final_x&&y==final_y){
            clearInterval(elem.timer);//用完定时器,清除定时器
            return true;
        }
        var distx=(final_x-x)/10;
        distx=distx>0?Math.ceil(distx):Math.floor(distx);
        x=x+distx;
        var disty=(final_y-y)/10;
        disty=disty>0?Math.ceil(disty):Math.floor(disty);
        y=y+disty;
        elem.style.left=x+"px";
        elem.style.top=y+"px";
    },25);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,650评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,052评论 25 707
  • 1. 组件简介 webuploader:是一个以HTML5为主, Flash为辅的文件上传组件,采用大文件分片/并...
    Xiangdong_She阅读 15,445评论 5 22
  • 悠悠,黄昏欲消,夜色为忧,月下飞愁,伫立庭中,望月思亲,寻觅寻觅,当过去演变成往事,未来成为希望时,我愿乘风破浪,...
    蔡籽阅读 163评论 0 0
  • // 列举 enum 类型 // 假设我们有这样一个需求,通过对于一副扑克牌的花色和牌面大小的 enum 类型,凑...
    fordring2008阅读 904评论 0 0