JavaScript:PC端特效--缓动动画

一、缓动效果原理

缓动动画就是让元素运动速度有所变化,最常见的就是让元素慢慢停下来

思路:

  1. 让盒子每次移动的距离慢慢变小,速度就会慢慢降下来
  2. 核心算法:(目标值-现在位置)/10作为每次移动距离的步长
  3. 停止条件:让盒子位置等于目标位置就停止计时器

案例:点击按钮后盒子缓停

css:

css 复制代码
div {
   position: absolute;
   left: 0;
   top: 40px;
   width: 200px;
   height: 200px;
   background-color: pink;
}

HTML:

html 复制代码
<div></div>
<button>点我开始走</button>

JavaScript:

javascript 复制代码
function annimate(obj, target) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function () {
          var step = (target - obj.offsetLeft) / 10;
          if (obj.offsetLeft == target) {
               clearInterval(obj.timer);
          }
          obj.style.left = obj.offsetLeft + step + 'px';
    }, 15)
}
var div = document.querySelector('div');
var button = document.querySelector('button');
button.addEventListener('click', function () {
    annimate(div, 500);
})

写完运行发现,这个案例有小bug,它并没有准准的停在我们设置的目标位置,因为它涉及到了除法,有小数点后就慢慢没那么精确了

那么我们就把step向上取整,尽量避免小数运算,向上取整是因为能延缓速度的变化,让缓动效果更加柔和

javascript 复制代码
var step = Math.ceil(target - obj.offsetLeft) / 10;

不过这样写忽略了后退时的效果,比如

html 复制代码
<div></div>
<button>点我到500</button>
<button>点我到800</button>
javascript 复制代码
function annimate(obj, target) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function () {
          var step = (target - obj.offsetLeft) / 10;
          if (obj.offsetLeft == target) {
               clearInterval(obj.timer);
          }
          obj.style.left = obj.offsetLeft + step + 'px';
    }, 15)
}
var div = document.querySelector('div');
var btn500 = document.querySelector('.btn500');
var btn800 = document.querySelector('.btn800');
btn500.addEventListener('click', function () {
    annimate(div, 500);
})
btn800.addEventListener('click', function () {
    annimate(div, 800);
})

在这个时候前进能准确到目标位置,但是后退时不能,按照上面修复bug的思路看,是取整出现问题了,和前面相似,后退时应该向下取整

那我们给step加上一个判断条件,如果大于0则向上取整,小于0则向下取整

javascript 复制代码
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);

二、动画函数添加回调函数

回调函数原理:函数可以当成一个参数,把一个函数作为参数传到另一个函数里面,那个函数执行完后再来执行传进去的参数,这个过程叫回调

回调函数写的位置:定时器结束的位置

案例:在移动完后输出你好

css:

css 复制代码
* {
   margin: 0px;
   padding: 0px;
}

div {
   position: absolute;
   left: 0;
   top: 40px;
   width: 200px;
   height: 200px;
   background-color: pink;
}

HTML:

html 复制代码
<div></div>
<button>点我到500</button>
<button>点我到800</button>

JavaScript:

javascript 复制代码
function annimate(obj, target) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function () {
          var step = (target - obj.offsetLeft) / 10;
          if (obj.offsetLeft == target) {
               clearInterval(obj.timer);
                    if (callback) {
                        callback();
                    }
          }
          obj.style.left = obj.offsetLeft + step + 'px';
    }, 15)
}
var div = document.querySelector('div');
var btn500 = document.querySelector('.btn500');
var btn800 = document.querySelector('.btn800');
btn500.addEventListener('click', function () {
    annimate(div, 800, function () {
                alert('你好');
    });
})
btn800.addEventListener('click', function () {
    annimate(div, 800, function () {
                alert('你好');
    });
})

三、动画函数封装到单独JS文件里面

因为以后要常用动画函数,所以我们把它封装到JS文件里面,下次要用直接引用就可以了

步骤:

  1. 单独新建一个JS文件
  2. 把我们写的这个函数复制进JS文件里
  3. 调用JS文件
  4. 直接调用动画函数

案例:鼠标经过它后滑出来'问题反馈'

css:

css 复制代码
.sliderbar {
    position: relative;
    left: -9px;
    height: 40px;
    width: 40px;
    text-align: center;
    line-height: 40px;
    color: #fff;
    background-color: purple;
}

.con {
    position: absolute;
    padding: 0 5px;
    left: -130px;
    height: 40px;
    width: 160px;
    background-color: purple;
    z-index: -1;
}

HTML:

记得不要忘记调用JS文件

html 复制代码
<script src="annimater.js"></script>
html 复制代码
<div class="sliderbar">
   <span>→</span>
   <div class="con">问题反馈</div>
</div>

JavaScript:

javascript 复制代码
var sliderbar = document.querySelector('.sliderbar');
var con = document.querySelector('.con');
sliderbar.addEventListener('mouseenter', function () {
    annimate(con, 40, function () {
          sliderbar.children[0].innerHTML = '←';
    })
})
sliderbar.addEventListener('mouseleave', function () {
    annimate(con, -160,, function () {
          sliderbar.children[0].innerHTML = '→';
    })
})

四、网页特效案例

**案例:**网页轮播图

css:

css 复制代码
.main {
    width: 980px;
    height: 455px;
    margin-left: 242px;
}

.focus {
    position: relative;
    width: 715px;
    height: 455px;
    overflow: hidden;
}

.focus ul {
    position: absolute;
    top: 0;
    left: 0;
    width: 1100%;
}

.focus li {
    float: left;
}

.focus img {
    /* width: 715px; */
    height: 455px;
}

.lft,
.rit {
    background: rgba(72, 68, 69, .3);
    width: 15px;
    height: 20px;
    z-index: 999;
    display: none;
}

.lft {
    position: absolute;
    top: 231px;
    left: 0;
}

.rit {
    position: absolute;
    top: 231px;
    right: 0px;
}

.lft a,
.rit a {
    color: #f9f9f9;
}

.lft,
.rit a:hover {
    color: #ccc;
}

.circle {
    position: absolute;
    bottom: 10px;
    left: 295px;
}

.circle li {
    float: left;
    width: 8px;
    height: 8px;
    border: 2px solid rgba(255, 255, 255, .5);
    margin: 0 3px;
    border-radius: 50%;
    cursor: pointer;
}

.current {
    background-color: #fff;
}

HTML:

html 复制代码
<div class="focus fl">
    <div class="lft"><a href="javascript:;">&lt</a></div>
    <div class="rit"><a href="javascript:;">&gt</a></div>
    <ul>
         <li><a href="#"><img src="images/loginbg.png" alt=""></a></li>
         <li><a href="#"><img src="images/loginbg.png" alt=""></a></li>
         <li><a href="#"><img src="images/loginbg.png" alt=""></a></li>
         <li><a href="#"><img src="images/loginbg.png" alt=""></a></li>
         <li><a href="#"><img src="images/loginbg.png" alt=""></a></li>
         <li><a href="#"><img src="images/loginbg.png" alt=""></a></li>
     </ul>
     <ol class="circle">
     </ol>
</div>

JavaScript:

javascript 复制代码
window.addEventListener('load', function () {
    var lft = document.querySelector('.lft');
    var rit = document.querySelector('.rit');
    var focus = document.querySelector('.focus');
    focus.addEventListener('mouseenter', function () {
        lft.style.display = 'block';
        rit.style.display = 'block';
        clearInterval(timer);
        timer = null;
    })
    focus.addEventListener('mouseleave', function () {
        lft.style.display = 'none';
        rit.style.display = 'none';
        timer = setInterval(function () {
            rit.click();
        }, 2000)
    })
    var ul = focus.querySelector('ul');
    var ol = focus.querySelector('.circle');
    for (var i = 0; i < ul.children.length; i++) {
        var li = document.createElement('li');
        li.setAttribute('index', i);
        ol.appendChild(li);
        li.addEventListener('click', function () {
            for (var i = 0; i < ol.children.length; i++) {
                ol.children[i].className = '';
            }
            this.className = 'current';
            var index = this.getAttribute('index');
            num = index;
            circle = index;
            var focusWidth = focus.offsetWidth;
            annimate(ul, -index * 1044);
        })
    }
    ol.children[0].className = 'current';
    var first = ul.children[0].cloneNode(true);
    ul.appendChild(first);
    var num = 0;
    var circle = 0;
    rit.addEventListener('click', function () {
        if (num == ul.children.length - 1) {
            ul.style.left = 0;
            num = 0
        }
        num++;
        annimate(ul, -num * 1044)
        circle++;
        if (circle == ol.children.length) {
            circle = 0;
        }
        circleChange();
    })
    lft.addEventListener('click', function () {
        if (num == 0) {
            num = ul.children.length - 1;
            ul.style.left = -num * 1044 + 'px';
        }
        num--;
        annimate(ul, -num * 1044)
        circle--;
        if (circle < 0) {
            circle = ol.children.length - 1;
        }
        circleChange();
    })
    function circleChange() {
        for (var i = 0; i < ul.children.length; i++) {
            ol.children[i].className = '';
            ol.children[circle].className = 'current';
        }
    }
    var timer = setInterval(function () {
        rit.click();
    }, 2000)
})
相关推荐
掘金者阿豪20 小时前
把业务数据变成共享仪表盘:Metabase可视化与远程访问实践
前端·后端
kyriewen21 小时前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
蜗牛前端21 小时前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
大龄秃头程序员1 天前
我在图文流 App 里落地双层缓存、弱网降级与 OOM 治理
前端
老王以为1 天前
React Renderer 分离的多平台架构
前端·react native·react.js
hunterandroid1 天前
Kotlin Coroutines 与 Flow:让异步任务更清晰
前端
Bigger1 天前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang4531 天前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
lichenyang4531 天前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端
用户059540174461 天前
Redis记忆存储故障恢复测试踩坑实录:手动测试让我漏掉了2个一致性Bug
前端·css