首页 > 技术文章 > js动画练习

solaris-wwf 2019-10-07 13:14 原文

js动画原理
    1.匀速
    2.碰撞
        .box{height: 100px;width: 100px;background: #333;position: absolute;left: 0;right: 0;}
        .box1{left: 500px;top: 500px;}
    <input type="text" id="te">
    <div class="box"></div><div class="box1"></div>

设定一些基础变量

    var oText = document.getElementById("te");
    var oDiv = document.querySelector(".box");
    var oDiv2 = document.querySelector(".box1");
    var speedX = 30;//设置一个速度变量,
    var speedY = 30;
    oDiv.speedX = 15;//设置div横向速度
    oDiv.speedY = 3;//设置div纵向速度
    oDiv2.speedX = 18;
    oDiv2.speedY = 2;

 给一个元素加个速度移动元素的方法

    function move(el){
        var currentLeft = parseInt(window.getComputedStyle(el).left);//取得元素当前距离父元素左边的距离
        var currentTop = parseInt(window.getComputedStyle(el).top);//取得元素当前距离父元素上边的距离
        check_border_conllision(el);//引用下边的构造函数判断元素是否到达了页面边缘
        var left = currentLeft + el.speedX;//设置一个速度让div距离页面左边的位置
        var top = currentTop + el.speedY;
        el.style.left = left + 'px';//将取得速度后的距离父元素的左边距离赋值给元素
        el.style.top = top + 'px';
    }
    盒子碰撞到了页面边缘检测核心构造函数
    function check_border_conllision(el){
        var elStyle = window.getComputedStyle(el);
        var left = parseInt(elStyle.left);
        var top = parseInt(elStyle.top);
        var w = parseInt(elStyle.width);//div的宽度
        var h = parseInt(elStyle.height);
        if(left < 0){//如果左侧位置是负值,则将它设为0,平移值转换
            left = 0;
            el.speedX *= -1;
        }
        if(left > window.innerWidth - w){//如果现在的左侧距离大于了页面宽度减去div的宽度,表示右边已经撞上了。
            left = window.innerWidth -w;
            el.speedX *= -1;
        }
        if(top < 0){//如果距离父元素上边的距离小于0,表示碰到了上边的边缘,将元素的速度取一个负值
            top = 0;
            el.speedY *= -1;
        }
        if(top > window.innerHeight - h){//如果距离大于父元素底部的距离,将元素的速度取一个负值
            top = window.innerHeight - h;
            el.speedY *= -1;
        }
        el.style.left = left + 'px';//重新设定元素距离父元素左边的距离
        el.style.top = top + 'px';
    }
    检测两个盒子相撞核心构造函数
    function check_conllision(el,el2){
        var style1 = window.getComputedStyle(el);
        var style2 = window.getComputedStyle(el2);
        var left1 = parseInt(style1.left);
        var left2 = parseInt(style2.left);
        var top1 = parseInt(style1.top);
        var top2 = parseInt(style2.top);
        var w1 = parseInt(style1.width);
        var w2 = parseInt(style2.width);
        var h1 = parseInt(style1.height);
        var h2 = parseInt(style2.height);
        var style1Zx = {x:left1 + w1 / 2,y:top1 + h1 / 2};//箱子1的中心点位置
        var style2Zx = {x:left2 + w2 / 2,y:top2 + h2 / 2};//箱子2的中心点位置
        var leftc = Math.abs(style1Zx.x - style2Zx.x);//将两个箱子中心点横向距离相减取绝对值
        var topc = Math.abs(style1Zx.y - style2Zx.y);
        if(leftc <= (w1 + w2)/2 && topc <= (h1 + h2)/2){//如果箱子的中心点小于他们的高度和宽度一半之和,我们判断为相撞
            return true;
        }
        return false;
    }

 加入了碰撞构造函数的移动元素的构造函数

    function move2(el){
        var currentLeft = parseInt(window.getComputedStyle(el).left);
        var currentTop = parseInt(window.getComputedStyle(el).top);
        check_border_conllision(el);
        var tempX, tempY;
        if(check_conllision(oDiv,oDiv2)){//判断是否相撞,将箱子的速度互换
            tempX = oDiv.speedX;
            tempY = oDiv.speedY;
            oDiv.speedX = oDiv2.speedX;
            oDiv.speedY = oDiv2.speedY;
            oDiv2.speedX = tempX;
            oDiv2.speedY = tempY;
        }
        var left = currentLeft + el.speedX;//设置div距离页面左边的位置
        var top = currentTop + el.speedY;
        el.style.left = left + 'px';
        el.style.top = top + 'px';
    }
    setInterval(() => {//设定一个调用函数,时间为20毫秒
        move2(oDiv);move2(oDiv2);
    }, 20);

 

动画函数的封装
    1.getStyle
    2.减速
    3.透明度
    4.多属性
      var oDiv = document.querySelector("div");
        function getStyle(el,property){//ie8兼容性,构造函数取得元素的style
            return el.currentStyle ? el.currentStyle[property] :window.getComputedStyle(el)[property];
        }
        function animateEl(el,properties,fn){
            clearInterval(el.timer);//先将调用函数清除
            el.timer = setInterval(function() {//设定周期调用函数
            var stop = true;//设定一个值,是否运算得到的属性都匹配然后终止定时器。
       for(var property in properties){//遍历对象中的键名 var curpro;//设定一个当前属性的值 var target = properties[property];//将对象中的目标值取出来 if(property == "opacity"){//如果属性是opacity curpro = Math.round(parseFloat(getStyle(el,property)) * 100);//将当前属性值乘以100取整,方便计算speed target = parseFloat(target)*100;//将目标也乘以100 }else{ curpro = parseFloat(getStyle(el,property));//width如果使用parseInt,所以用了parseFloat,不会得到指定值,其他属性可以 } var speed = (target - curpro) / 30;//目标距离减去当前距离除以30作为速度。 speed = speed > 0 ? Math.ceil(speed): Math.floor(speed);//设定速度,如果速度大于0,则向上取整,至少为1,相反为-1; if(properties[property] != curpro){//如果第一次运算的属性没有达到指定值,则返回stop为false
            stop = false;
          }
          if(property == "opacity"){ el.style[property] =(curpro + speed)/100 ;//设定元素的属性值,因为之前已经成了100,所以除以100 }else{ el.style[property] =curpro + speed + "px";//如果不是透明属性,加上px, } }
        if(stop){//如果第一次运算得到的值匹配了指定值,让定时器停止
                  clearInterval(el.timer);
            fn && fn();//因为停止了第一个定时器,需要开启下一次运算,用回调函数来决定。这个意思是你返回一个函数,则调用这个函数。
              }
    }, 20); } 
    animateEl(oDiv,{
"top":300,"width":300,"opacity":0.5});

 

    自己练习写的
        var speedCa =  (opacity1 - targetFL)/30 ;
        speedCa = speedCa / 100;
        speedCa.toFixed(8);//如果指定值和现值很接近,则速度将会很小,所以先取小数点后面8个数,
        el.style[property] = opacity1/100 - speedCa;
        if(parseFloat(el.style[property]).toFixed(2) == parseFloat(target).toFixed(2)){//如果已经很接近,则直接将指定值赋值给现值
            el.style[property] = target;
            clearInterval(timer1);
        } 

 

    
轮播
    1.透明度变化轮播
        .slideimg{height: 200px;width: 500px;position: relative;border: 5px solid #333;overflow: hidden;}
        .slideimg .ul1 .item{position: absolute;list-style: none;opacity: 0;}
        .slideimg .ul1 .item img{height: 200px;width: 500px;display: block;}
        .slideimg .ul1 .item:first-of-type{opacity: 1;}
        .slideimg .pre,.slideimg .next{height: 60px;width: 30px;opacity: .5;background: #333;position: absolute;top:50%;margin-top: -30px;cursor: pointer;}
        .slideimg .pre{left: 0;}
        .slideimg .next{right: 0;}
        .slideimg .ul2{position: absolute;bottom: 5px;left:50%;margin-left: -50px;width: 100px;}
        .slideimg .ul2 .li{height: 20px;width: 20px;border-radius: 20px;background: rgba(000, 000, 000, .5);list-style: none; cursor: pointer;float: left;margin-left: 5px}
        .slideimg .ul2 .li.focus{background:rgba(255, 000, 000, .5)}
    <div class="slideimg">
        <ul class="ul1">
            <li class="item"><img src="../img/1.jpg" alt="1"></li>
            <li class="item"><img src="../img/2.jpg" alt="2"></li>
            <li class="item"><img src="../img/3.jpg" alt="3"></li>
            <li class="item"><img src="../img/4.jpg" alt="4"></li>
        </ul>
        <ul class="ul2">
            <li class="li focus"></li><li class="li"></li><li class="li"></li><li class="li"></li>
        </ul>
        <div class="pre"></div><div class="next"></div>
    </div>
        var preIndex,nextIndex,len,id;//当前页赋值给这个数,当前显示页面,图片总数,
        var ul2li = document.querySelectorAll(".ul2 li");//全局对象圆点跳转图片
        init();
        function init(){//初始化
            preIndex = nextIndex  = 0;//初始化参数,将前一页后一页的下标清零
            len = document.querySelectorAll(".ul1 .item").length;
            document.querySelector(".next").onclick = function(){//点击右侧按钮显示下一张图片
                slideNext();
            }
            document.querySelector(".pre").onclick = function(){
                slidePre();
            }
            var slideImg = document.querySelector(".slideimg");
            slideImg.onmouseover = function(){//鼠标悬停
                stop();
            }
            slideImg.onmouseout = function(){
                sildeAuto();
            }
            sildeAuto();//自动轮播
       点击下面的圆点跳转图片
            for(i = 0; i < ul2li.length ; i++){//遍历所有的圆点
                ul2li[i].index = i;//将当前的下标赋值给index
                ul2li[i].onclick = function(){
                    preIndex = nextIndex;
                    nextIndex = this.index;
                    slideTo(preIndex,nextIndex);
                }
            }
        }
        function sildeAuto(){//3秒自动轮播
            clearInterval(id);
            id = setInterval(function(){
                slideNext();
            },3000);
        }
        function slidePre(){//向前封装函数
            preIndex = nextIndex;
            nextIndex--;
            if(nextIndex === - 1){
                nextIndex = len - 1;
            }
            slideTo(preIndex,nextIndex);
        } 
        function slideNext(){//向后封装函数
            preIndex = nextIndex;
            nextIndex++;
            if(nextIndex === len){
                nextIndex = 0;
            }
            slideTo(preIndex,nextIndex);
        }  
        function stop(){//停止周期调用函数
            clearInterval(id);
        }
        function slideTo(pre,next){//透明度函数封装
            var item = document.querySelectorAll(".ul1 .item");
            ul2li[pre].className = "li";
            ul2li[next].className = "li focus";
            animateEl(item[pre],{opacity:0});//调用上面的封装函数
            animateEl(item[next],{opacity:1});
        }
 
  2.水平轮播
        .slideimg2{height: 200px;width: 400px;position: relative;border: 5px solid #333;overflow: hidden;}
        .slideimg2 .list{position: absolute;list-style: none;}
        .slideimg2 .list .item{float: left;width: 400px;height: 200px;}
        .slideimg2 .list .item img{height: 200px;width: 400px;display: block;}

            .slideimg2 .preimg,.slideimg2 .nextimg{height: 60px;width: 30px;opacity: .5;background: #333;position: absolute;top:50%;margin-top: -30px;cursor: pointer;}
            .slideimg2 .preimg{left: 0;}
            .slideimg2 .nextimg{right: 0;}

        .slideimg2 .list2{position: absolute;bottom: 5px;left:50%;margin-left: -50px;width: 100px;display: flex;justify-content:space-around}
            .slideimg2 .list2 .dot{height: 20px;width: 20px;border-radius: 20px;background: rgba(000, 000, 000, .5);list-style: none; cursor: pointer;}
            .slideimg2 .list2 .dot.focus{background: rgba(255, 000, 000, .5)}
    <div class="slideimg2">
        <ul class="list">
            <li class="item"><img src="../img/1.jpg" alt="1"></li>
            <li class="item"><img src="../img/2.jpg" alt="2"></li>
            <li class="item"><img src="../img/3.jpg" alt="3"></li>
            <li class="item"><img src="../img/4.jpg" alt="4"></li>
        </ul>
        <ul class="list2">
            <li class="dot focus"></li><li class="dot"></li><li class="dot"></li><li class="dot"></li>
        </ul>
        <div class="preimg"></div><div class="nextimg"></div>
    </div>
    var sIndex,len,liWidth,liItem,idin;
    var list = document.querySelector(".slideimg2 .list");
    var dot = document.querySelectorAll(".slideimg2 .list2 .dot");
    init()
    function init(){
        sIndex = 1;//设定默认显示第一张图片
        var newLi1 = document.querySelector(".slideimg2 .list .item:first-of-type");
        var copy1 = newLi1.cloneNode(true);//复制第一张图片
        var newLi4 = document.querySelector(".slideimg2 .list .item:last-of-type").cloneNode(true);
        var copyLast = newLi4.cloneNode(true);//复制最后一张图片
        list.appendChild(copy1);//将第一张图片添加到最后一个
        list.insertBefore(copyLast,newLi1);//将最后一张图片添加到第一个
 

        liItem = document.querySelectorAll(".list .item");
        var liStyle = window.getComputedStyle(liItem[0]);
        liWidth = liStyle.width;//取到一个li的宽度
        list.style.left = -parseInt(liWidth) + "px";//让图片显示初始值第一个
        len = liItem.length;//取到总li的个数
        list.style.width = parseInt(liWidth) * len +"px";//list的宽度=li个数乘以li的宽度,让所有图片横排
        //左按钮播放一下张图片
        document.querySelector(".slideimg2 .preimg").onclick = function(){
            preImg();
        }
        //右按钮播放一下张图片
        document.querySelector(".slideimg2 .nextimg").onclick = function(){
            nextImg();
        }
        //鼠标悬停
        document.querySelector(".slideimg2").onmouseover = function(){
            stop();
        }
        //鼠标离开自动轮播
        document.querySelector(".slideimg2").onmouseout = function(){
            auto();
        }
 
        auto();//自动轮播函数
        //小圆点逻辑
        for(var i = 0; i < dot.length ; i++){
            dot[i].index = i;//将下标占时存入index
            dot[i].onclick = function(){
                sIndex = this.index + 1;//因为按钮的下一页前后都多了一张图片,所以小圆点的下标要加1,
                sliTo(sIndex);
                }
            }
        }
    function auto(){//调用函数自动轮播
        clearInterval(idin);
        idin = setInterval(function(){
            nextImg();
        },3000);
    }
    function stop(){
        clearInterval(idin);//停止周期调用函数
    }
    function preImg(){//前一张图片的构造函数
        sIndex--;
        if(sIndex === -1){//如果上一张图片到了第一张,则直接跳转到后面相同的图片
            sIndex = len -3;//因为设定了轮播图片是4张,加上前后两张一共6张。
            list.style.left = -parseInt(liWidth) * 4 + "px";//立即移动图片,
        }
        sliTo(sIndex);//调用滚动核心构造函数
    }
    function nextImg(){
        sIndex++;
        if(sIndex === len){//如果下一张图片到了最后一张,则直接跳转到前面相同的图片,
            sIndex = 2;
            list.style.left = -parseInt(liWidth) + "px";
        }
        sliTo(sIndex);
    }
    function sliTo(index){//滚动核心构造函数
    var foucusDot;//平衡小圆点和左右按钮的逻辑
    if(index === 0){
       foucusDot = dot.length -1;//当按钮最左面一张图片对应小圆点的最后一张图片的下标
    }else if(index === 5){//当按钮最右面一张图片对应小圆点的第一张图片的下标
        foucusDot = 0;
    }else{
        foucusDot = index - 1;//按钮的下标比小圆点的下标多1.
    }
    document.querySelector(".focus").className = "dot";//检索小圆点取消有focus的样式
    dot[foucusDot].className = "dot focus";//添加小圆点当前页的突出显示
    animateEl(list,{left:-parseInt(liWidth)*index}) ;//封装动画函数,整个list进行left的位移
}

 

推荐阅读