前言
作为一名5年工作经验的安卓开发人员,我们知道安卓除了布局方式需要熟悉掌握之外,还需要熟练掌握的就是动画效果的实现。作为大前端,客户端和用户的交互不可避免,为了掌握Javascript这门脚本语言,所以我们需要熟练掌握JavaScript的动画实现。

1)JavaScript动画:offset和匀速动画详解(含轮播图的实现)。

JS动画的主要内容如下:1、三大家族和一个事件对象:- 三大家族:offset/scroll/client。也叫三大系列。- 事件对象/event(事件被触动时,鼠标和键盘的状态)(通过属性控制)。2、动画(闪现/匀速/缓动)3、冒泡/兼容/封装4、正则

Offset

js中有一套方便的**获取元素尺寸**的办法就是offset家族。offset家族包括:- offsetWidth- offsetHight- offsetLeft- offsetTop- offsetParent
用于检测盒子自身的**宽高+padding+border**,不包括margin。如下:- offsetWidth = width + padding + border;- offsetHeight = Height + padding + border;

2、offsetLeft 和 offsetTop

返回距离上级盒子(带有定位)左边的位置;如果父级都没有定位,则以body为准。

offsetLeft: 从父亲的 padding 开始算,父亲的 border 不算。

offsetLeft和style.left区别

(1)最大区别在于:

offsetLeft 可以返回没有定位盒子的距离左侧的位置。如果父系盒子中都没有定位,以body为准。

style.left 只能获取行内式,如果没有,则返回""(意思是,返回空);

(2)offsetTop 返回的是数字,而 style.top 返回的是字符串,而且还带有单位:px。
(3)offsetLeft 和 offsetTop 只读,而 style.left 和 style.top 可读写(只读是获取值,可写是赋值)
(4)如果没有给 HTML 元素指定过 top 样式,则style.top 返回的是空字符串。

总结:我们一般的做法是:**用offsetLeft 和 offsetTop 获取值,用style.left 和 style.top 赋值**(比较方便)。理由如下:
- style.left:只能获取行内式,获取的值可能为空,容易出现NaN。
- offsetLeft:获取值特别方便,而且是现成的number,方便计算。它是只读的,不能赋值。

动画的种类

  • 闪现(基本不用)

  • 匀速(本文重点)

  • 缓动(后续重点)

简单举例如下:(每间隔500ms,向右移动盒子100px)

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>div {width: 100px;height: 100px;background-color: pink;position: absolute;}</style>
</head>
<body>
<button>动画</button>
<div class="box" style="left: 0px"></div><script>var btn = document.getElementsByTagName("button")[0];var div = document.getElementsByTagName("div")[0];//1、闪动//    btn.onclick = function () {//        div.style.left = "500px";//    }//2、匀速运动btn.onclick = function () {//定时器,每隔一定的时间向右走一些setInterval(function () {console.log(parseInt(div.style.left));//动画原理: 盒子未来的位置 = 盒子现在的位置 + 步长;//用style.left赋值,用offsetLeft获取值。div.style.left = div.offsetLeft + 100 + "px";//div.style.left = parseInt(div.style.left)+10+"px";  //NaN不能用}, 500);}
</script>
</body>
</html>

效果如下:

匀速动画的封装:每间隔30ms,移动盒子10px【重要】

代码如下:

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>.box1 {margin: 0;padding: 5px;height: 300px;background-color: #ddd;position: relative;}button {margin: 5px;}.box2 {width: 100px;height: 100px;background-color: red;position: absolute;left: 195px;top: 40px;}.box3 {width: 100px;height: 100px;background-color: yellow;position: absolute;left: 0;top: 150px;}</style>
</head>
<body>
<div class="box1"><button>运动到 left = 200px</button><button>运动到 left = 400px</button><div class="box2"></div><div class="box3"></div>
</div><script>var btnArr = document.getElementsByTagName("button");var box2 = document.getElementsByClassName("box2")[0];var box3 = document.getElementsByClassName("box3")[0];//绑定事件btnArr[0].onclick = function () {//如果有一天我们要传递另外一个盒子,那么我们的方法就不好用了//所以我们要增加第二个参数,被移动的盒子本身。animate(box2, 200);animate(box3, 200);}btnArr[1].onclick = function () {animate(box2, 400);animate(box3, 400);}//【重要】方法的封装:每间隔30ms,将盒子向右移动10pxfunction animate(ele, target) {//要用定时器,先清除定时器//一个盒子只能有一个定时器,这样的话,不会和其他盒子出现定时器冲突//我们可以把定时器本身,当成为盒子的一个属性clearInterval(ele.timer);//我们要求盒子既能向前又能向后,那么我们的步长就得有正有负//目标值如果大于当前值取正,目标值如果小于当前值取负var speed = target > ele.offsetLeft ? 10 : -10;  //speed指的是步长ele.timer = setInterval(function () {//在执行之前就获取当前值和目标值之差var val = target - ele.offsetLeft;ele.style.left = ele.offsetLeft + speed + "px";//移动的过程中,如果目标值和当前值之差如果小于步长,那么就不能在前进了//因为步长有正有负,所有转换成绝对值来比较if (Math.abs(val) < Math.abs(speed)) {ele.style.left = target + "px";clearInterval(ele.timer);}}, 30)}
</script>
</body>
</html>

实现的效果:

上方代码中的方法封装,可以作为一个模板步骤,要记住。其实,这个封装的方法,写成下面这样,会更严谨,更容易理解:(将if语句进行了改进)

    //【重要】方法的封装:每间隔30ms,将盒子向右移动10pxfunction animate(ele, target) {//要用定时器,先清除定时器//一个盒子只能有一个定时器,这样的话,不会和其他盒子出现定时器冲突//我们可以把定时器本身,当成为盒子的一个属性clearInterval(ele.timer);//我们要求盒子既能向前又能向后,那么我们的步长就得有正有负//目标值如果大于当前值取正,目标值如果小于当前值取负var speed = target > ele.offsetLeft ? 10 : -10;  //speed指的是步长ele.timer = setInterval(function () {//在执行之前就获取当前值和目标值之差var val = target - ele.offsetLeft;//移动的过程中,如果目标值和当前值之差如果小于步长,那么就不能在前进了//因为步长有正有负,所有转换成绝对值来比较if (Math.abs(val) < Math.abs(speed)) {  //如果val小于步长,则直接到达目的地;否则,每次移动一个步长ele.style.left = target + "px";clearInterval(ele.timer);} else {ele.style.left = ele.offsetLeft + speed + "px";}}, 30)}

代码举例:轮播图的实现

完整版代码如下:(注释已经比较详细)

<!doctype html>
<html lang="en">
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>无标题文档</title><style type="text/css">* {padding: 0;margin: 0;list-style: none;border: 0;}.all {width: 500px;height: 200px;padding: 7px;border: 1px solid #ccc;margin: 100px auto;position: relative;}.screen {width: 500px;height: 200px;overflow: hidden;position: relative;}.screen li {width: 500px;height: 200px;overflow: hidden;float: left;}.screen ul {position: absolute;left: 0;top: 0px;width: 3000px;}.all ol {position: absolute;right: 10px;bottom: 10px;line-height: 20px;text-align: center;}.all ol li {float: left;width: 20px;height: 20px;background: #fff;border: 1px solid #ccc;margin-left: 10px;cursor: pointer;}.all ol li.current {background: yellow;}#arr {display: none;}#arr span {width: 40px;height: 40px;position: absolute;left: 5px;top: 50%;margin-top: -20px;background: #000;cursor: pointer;line-height: 40px;text-align: center;font-weight: bold;font-family: '黑体';font-size: 30px;color: #fff;opacity: 0.3;border: 1px solid #fff;}#arr #right {right: 5px;left: auto;}</style><script>window.onload = function () {//需求:无缝滚动。//思路:赋值第一张图片放到ul的最后,然后当图片切换到第五张的时候//     直接切换第六章,再次从第一张切换到第二张的时候先瞬间切换到//     第一张图片,然后滑动到第二张//步骤://1.获取事件源及相关元素。(老三步)//2.复制第一张图片所在的li,添加到ul的最后面。//3.给ol中添加li,ul中的个数-1个,并点亮第一个按钮。//4.鼠标放到ol的li上切换图片//5.添加定时器//6.左右切换图片(鼠标放上去隐藏,移开显示)//1.获取事件源及相关元素。(老三步)var all = document.getElementById("all");var screen = all.firstElementChild || all.firstChild;var imgWidth = screen.offsetWidth;var ul = screen.firstElementChild || screen.firstChild;var ol = screen.children[1];var div = screen.lastElementChild || screen.lastChild;var spanArr = div.children;//2.复制第一张图片所在的li,添加到ul的最后面。var ulNewLi = ul.children[0].cloneNode(true);ul.appendChild(ulNewLi);//3.给ol中添加li,ul中的个数-1个,并点亮第一个按钮。for (var i = 0; i < ul.children.length - 1; i++) {var olNewLi = document.createElement("li");olNewLi.innerHTML = i + 1;ol.appendChild(olNewLi)}var olLiArr = ol.children;olLiArr[0].className = "current";//4.鼠标放到ol的li上切换图片for (var i = 0; i < olLiArr.length; i++) {//自定义属性,把索引值绑定到元素的index属性上olLiArr[i].index = i;olLiArr[i].onmouseover = function () {//排他思想for (var j = 0; j < olLiArr.length; j++) {olLiArr[j].className = "";}this.className = "current";//鼠标放到小的方块上的时候索引值和key以及square同步
//                    key = this.index;
//                    square = this.index;key = square = this.index;//移动盒子animate(ul, -this.index * imgWidth);}}//5.添加定时器var timer = setInterval(autoPlay, 1000);//固定向右切换图片//两个定时器(一个记录图片,一个记录小方块)var key = 0;var square = 0;function autoPlay() {//通过控制key的自增来模拟图片的索引值,然后移动ulkey++;if (key > olLiArr.length) {//图片已经滑动到最后一张,接下来,跳转到第一张,然后在滑动到第二张ul.style.left = 0;key = 1;}animate(ul, -key * imgWidth);//通过控制square的自增来模拟小方块的索引值,然后点亮盒子//排他思想做小方块square++;if (square > olLiArr.length - 1) {//索引值不能大于等于5,如果等于5,立刻变为0;square = 0;}for (var i = 0; i < olLiArr.length; i++) {olLiArr[i].className = "";}olLiArr[square].className = "current";}//鼠标放上去清除定时器,移开后在开启定时器all.onmouseover = function () {div.style.display = "block";clearInterval(timer);}all.onmouseout = function () {div.style.display = "none";timer = setInterval(autoPlay, 1000);}//6.左右切换图片(鼠标放上去显示,移开隐藏)spanArr[0].onclick = function () {//通过控制key的自增来模拟图片的索引值,然后移动ulkey--;if (key < 0) {//先移动到最后一张,然后key的值取之前一张的索引值,然后在向前移动ul.style.left = -imgWidth * (olLiArr.length) + "px";key = olLiArr.length - 1;}animate(ul, -key * imgWidth);//通过控制square的自增来模拟小方块的索引值,然后点亮盒子//排他思想做小方块square--;if (square < 0) {//索引值不能大于等于5,如果等于5,立刻变为0;square = olLiArr.length - 1;}for (var i = 0; i < olLiArr.length; i++) {olLiArr[i].className = "";}olLiArr[square].className = "current";}spanArr[1].onclick = function () {//右侧的和定时器一模一样autoPlay();}function animate(ele, target) {clearInterval(ele.timer);var speed = target > ele.offsetLeft ? 10 : -10;ele.timer = setInterval(function () {var val = target - ele.offsetLeft;ele.style.left = ele.offsetLeft + speed + "px";if (Math.abs(val) < Math.abs(speed)) {ele.style.left = target + "px";clearInterval(ele.timer);}}, 10)}}</script>
</head><body>
<div class="all" id='all'><div class="screen" id="screen"><ul id="ul"><li><img src="data:images/1.jpg" width="500" height="200"/></li><li><img src="data:images/2.jpg" width="500" height="200"/></li><li><img src="data:images/3.jpg" width="500" height="200"/></li><li><img src="data:images/4.jpg" width="500" height="200"/></li><li><img src="data:images/5.jpg" width="500" height="200"/></li></ul><ol></ol><div id="arr"><span id="left"><</span><span id="right">></span></div></div>
</div>
</body>
</html>

实现效果:

缓动动画

三个函数

缓慢动画里,我们要用到三个函数,这里先列出来:

  • Math.ceil() 向上取整

  • Math.floor() 向下取整

  • Math.round(); 四舍五入

缓动动画的原理

缓动动画的原理就是:在移动的过程中,步长越来越小。

设置步长为**:目标位置和盒子当前位置的十分之一**。用公式表达,即:

 盒子位置 = 盒子本身位置 + (目标位置 - 盒子本身位置)/ 10;

代码举例:

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>div {width: 100px;height: 100px;background-color: pink;position: absolute;}</style>
</head>
<body>
<button>运动到left = 400px</button>
<div></div><script>var btn = document.getElementsByTagName("button")[0];var div = document.getElementsByTagName("div")[0];btn.onclick = function () {setInterval(function () {//动画原理:盒子未来的位置 = 盒子当前的位置+步长div.style.left = div.offsetLeft + (400 - div.offsetLeft) / 10 + "px";}, 30);}</script>
</body>
</html>

效果:

缓慢动画的封装(解决四舍五入的问题)

我们发现一个问题,上图中的盒子最终并没有到达400px的位置,而是只到了396.04px就停住了:

原因是:JS在取整的运算时,进行了四舍五入。

我们把打印396.04px这个left值打印出来看看:

我么发现,通过div.style.left获取的值是精确的,通过div.offsetLeft获取的left值会进行四舍五入。

此时,我们就要用到取整的函数了。

通过对缓动动画进行封装,完整版的代码实现如下:

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>div {width: 100px;height: 100px;background-color: pink;position: absolute;left: 0;}</style>
</head>
<body>
<button>运动到200</button>
<button>运动到400</button>
<div></div><script>var btn = document.getElementsByTagName("button");var div = document.getElementsByTagName("div")[0];btn[0].onclick = function () {animate(div, 200);}btn[1].onclick = function () {animate(div, 400);}//缓动动画封装function animate(ele, target) {//要用定时器,先清定时器//一个萝卜一个坑儿,一个元素对应一个定时器clearInterval(ele.timer);//定义定时器ele.timer = setInterval(function () {//获取步长//步长应该是越来越小的,缓动的算法。var step = (target - ele.offsetLeft) / 10;//对步长进行二次加工(大于0向上取整,小于0向下取整)//达到的效果是:最后10像素的时候都是1像素1像素的向目标位置移动,就能够到达指定位置。step = step > 0 ? Math.ceil(step) : Math.floor(step);//动画原理: 目标位置 = 当前位置 + 步长ele.style.left = ele.offsetLeft + step + "px";console.log(step);//检测缓动动画有没有停止console.log("smyhvae");if (Math.abs(target - ele.offsetLeft) <= Math.abs(step)) {//处理小数赋值ele.style.left = target + "px";clearInterval(ele.timer);}}, 30);}</script>
</body>
</html>

实现效果:

scroll 家族的组成

当我们用鼠标滚轮,滚动网页的时候,会触发window.onscroll()方法。效果如下:(注意看控制台的打印结果)

1、ScrollWidth 和 scrollHeight

获取盒子的宽高。调用者为节点元素。不包括 border和margin。如下:

  • scrollWidth = width + padding;

  • scrollHeight = height + padding;

scrollHeight有一个特点:如果文字超出了盒子,高度为内容的高(包括超出的内容);不超出,则是盒子本身高度。

举例:

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>div {width: 100px;height: 100px;padding: 10px;margin: 3px;border: 8px solid red;}</style>
</head>
<body><div class="box">静,能寒窗苦守;动,能点石成金。静,能寒窗苦守;动,能点石成金。静,能寒窗苦守;动,能点石成金。静,能寒窗苦守;动,能点石成金。静,能寒窗苦守;动,能点石成金。静,能寒窗苦守;动,能点石成金。
</div>
<script>var div = document.getElementsByTagName("div")[0];//scrollHeight有一个特点:如果文字超出了盒子,高度为内容的高(包括超出的内容);不超出,则是盒子本身高度。//IE8以下(不包括IE8),为盒子本身内容的多少。console.log(div.scrollWidth);console.log(div.scrollHeight);</script>
</body>
</html>

打印结果:

2、scrollTop 和 scrollLeft

网页被卷去的头部和左边的部分。

比如说,一个网页往上滚动的时候,上面的部分自然被浏览器遮挡了,遮挡的高度就是scrollTop。

scrollTop 这个属性的写法要注意兼容性,如下。

(1)如果文档没有 DTD 声明,写法为:

    document.body.scrollTop

在没有 DTD 声明的情况下,如果不是这种写法,chrome浏览器认不出来。

(2)如果文档有 DTD 声明,写法为:

   document.documentElement.scrollTop

在有 DTD 声明的情况下,如果不是这种写法,IE678认不出来。

综合上面这两个,就诞生了一种兼容性的写法:

    document.body.scrollTop || document.documentElement.scrollTop //方式一document.body.scrollTop + document.documentElement.scrollTop  //方式二

另外还有一种兼容性的写法:window.pageYOffsetwindow.pageXOffset。这种写法无视DTD的声明。这种写法支持的浏览器版本是:火狐/谷歌/ie9+。

综合上面的几种写法,为了兼容,不管有没有DTD,最终版的兼容性写法:

    window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;

判断是否已经 DTD声明

方法如下:

    document.compatMode === "CSS1Compat"   // 已声明document.compatMode === "BackCompat"   // 未声明

将 scrollTop 和 scrollLeft封装为 json

将 scrollTop 和 scrollLeft封装为一个方法,名叫scroll(),返回值为 json。json里的键为 top 和 left。以后就直接调用json.top 和json.left就好。

代码实现:

<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>body {height: 5000px;width: 5000px;}</style>
</head>
<body><script>//需求:封装一个兼容的scroll().返回值是json,用scroll().top获取scrollTop,用scroll().left获取scrollLeftwindow.onscroll = function () {//        var json = scroll();
//        json.top;console.log(scroll().top);console.log(scroll().left);}//函数封装(简单封装,实际工作使用)function scroll() {return { //此函数的返回值是json"top": window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop,"left": window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft}}
</script>
</body>
</html>

上方代码中,函数定义的那部分就是要封装的代码。另外还有一种较为复杂的封装方式:

function scroll() {  // 开始封装自己的scrollTopif(window.pageYOffset !== undefined) {  // ie9+ 高版本浏览器// 因为 window.pageYOffset 默认的是  0  所以这里需要判断return {left: window.pageXOffset,top: window.pageYOffset}}else if(document.compatMode === "CSS1Compat") {    // 标准浏览器   来判断有没有声明DTDreturn {left: document.documentElement.scrollLeft,top: document.documentElement.scrollTop}}return {   // 未声明 DTDleft: document.body.scrollLeft,top: document.body.scrollTop}
}

获取 html 文档的方法

获取title、body、head、html标签的方法如下:

  • document.title 文档标题;

  • document.head 文档的头标签

  • document.body 文档的body标签;

  • document.documentElement (这个很重要)。

document.documentElement表示文档的html标签。也就是说,基本结构当中的 html 标签而是通过document.documentElement访问的,并不是通过 document.html 去访问的。

scrollTop 举例:固定导航栏

完整版代码实现:

(1)index.html:

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>* {margin: 0;padding: 0}img {vertical-align: top;}.main {margin: 0 auto;width: 1000px;margin-top: 10px;}#Q-nav1 {overflow: hidden;}.fixed {position: fixed;top: 0;left: 0;}</style><!--引入工具js--><script src="tools.js"></script><script>window.onload = function () {//需求1:当我们滚动界面的时候,被卷曲的头部如果超过第二个盒子距离顶部的位置,那么直接给第二个盒子加类名.fixed//需求2:当我们滚动界面的时候,被卷曲的头部如果小于第二个盒子距离顶部的位置,那么直接给第二个盒子取消类名.fixed//1.老三步。var topDiv = document.getElementById("top");var height = topDiv.offsetHeight;var middle = document.getElementById("Q-nav1");var main = document.getElementById("main");window.onscroll = function () {//2.判断 ,被卷曲的头部的大小if (scroll().top > height) {//3.满足条件添加类,否则删除类middle.className += " fixed";//第二个盒子也要占位置,为了避免重叠,我们给第三个盒子一个上padding的空间,把这个空间留给第二个盒子main.style.paddingTop = middle.offsetHeight + "px";} else {middle.className = "";//清零main.style.paddingTop = 0;}}}</script>
</head>
<body>
<div class="top" id="top"><img src="data:images/top.png" alt=""/>
</div>
<div id="Q-nav1"><img src="data:images/nav.png" alt=""/>
</div>
<div class="main" id="main"><img src="data:images/main.png" alt=""/>
</div>
</body>
</html>

上方代码中,有一个技巧:

main.style.paddingTop = middle.offsetHeight + "px";

仔细看注释就好。

(2)tools.js:

/*** Created by smyhvae on 2018/02/03.*/
function scroll() {  // 开始封装自己的scrollTopif (window.pageYOffset !== undefined) {  // ie9+ 高版本浏览器// 因为 window.pageYOffset 默认的是  0  所以这里需要判断return {left: window.pageXOffset,top: window.pageYOffset}}else if (document.compatMode === "CSS1Compat") {    // 标准浏览器   来判断有没有声明DTDreturn {left: document.documentElement.scrollLeft,top: document.documentElement.scrollTop}}return {   // 未声明 DTDleft: document.body.scrollLeft,top: document.body.scrollTop}
}

实现效果:

工程文件:

  • 2018-02-03-scrollTop固定导航栏.rar

window.scrollTo()方法举例:返回到顶部小火箭

(1)index.html:

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>img {position: fixed;bottom: 100px;right: 50px;cursor: pointer;display: none;border: 1px solid #000;}div {width: 1210px;margin: 100px auto;text-align: center;font: 500 26px/35px "simsun";color: red;}</style><script src="tools.js"></script><script>window.onload = function () {//需求:被卷去的头部超过100显示小火箭,然后点击小火箭屏幕缓慢移动到最顶端。//难点:我们以前是移动盒子,现在是移动屏幕,我们没有学过如何移动屏幕。//      技术点:window.scrollTo(x,y);浏览器显示区域跳转到指定的坐标//步骤://1.老三步var img = document.getElementsByTagName("img")[0];window.onscroll = function () {//被卷去的距离大于200显示小火箭,否则隐藏//2.显示隐藏小火箭if (scroll().top > 1000) {img.style.display = "block";} else {img.style.display = "none";}//每次移动滚动条的时候都给leader赋值,模拟leader获取距离顶部的距离leader = scroll().top;}//3.缓动跳转到页面最顶端(利用我们的缓动动画)var timer = null;var target = 0; //目标位置var leader = 0; //显示区域自身的位置img.onclick = function () {//技术点:window.scrollTo(0,0);//要用定时器,先清定时器clearInterval(timer);timer = setInterval(function () {//获取步长var step = (target - leader) / 10;//二次处理步长step = step > 0 ? Math.ceil(step) : Math.floor(step);leader = leader + step; //往上移动的过程中,step是负数。当前位置减去步长,就等于下一步的位置。//显示区域移动window.scrollTo(0, leader);//清除定时器if (leader === 0) {clearInterval(timer);}}, 25);}}</script>
</head>
<body>
<img src="data:images/Top.jpg"/>
<div>我是最顶端.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br>生命壹号,永不止步.....<br></div>
</body>
</html>

(2)tools.js:

/*** Created by smyhvae on 2015/12/8.*///函数:获取scrollTop和scrollLeft的值
function scroll() {  // 开始封装自己的scrollTopif (window.pageYOffset != null) {  // ie9+ 高版本浏览器// 因为 window.pageYOffset 默认的是  0  所以这里需要判断return {left: window.pageXOffset,top: window.pageYOffset}}else if (document.compatMode === "CSS1Compat") {    // 标准浏览器   来判断有没有声明DTDreturn {left: document.documentElement.scrollLeft,top: document.documentElement.scrollTop}}return {   // 未声明 DTDleft: document.body.scrollLeft,top: document.body.scrollTop}
}

实现效果:

小火箭的图片资源:

window.scrollTo()方法举例:楼层跳跃(暂略)

缓动框架封装

1、缓动框架封装:同时设置多个属性

这里我们通过window.getComputedStyle的方式获取属性值。

完整代码如下:

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>div {position: absolute;top: 40px;left: 10px;width: 100px;height: 100px;background-color: pink;}</style>
</head>
<body><button>运动到json中设置的位置</button>
<div></div><script>var btnArr = document.getElementsByTagName("button");var div = document.getElementsByTagName("div")[0];btnArr[0].onclick = function () {var json = {"left": 100, "top": 200, "width": 300, "height": 300};animate(div, json);}//参数变为3个function animate(ele, json) {//先清定时器clearInterval(ele.timer);ele.timer = setInterval(function () {//遍历属性和值,分别单独处理json//attr == key(键)    target == json[key](值)for (var key in json) {//四部var current = parseInt(getStyle(ele, key)) || 0;//1.获取步长var step = (json[key] - current) / 10;//2.二次加工步长step = step > 0 ? Math.ceil(step) : Math.floor(step);current = current + step;//3.赋值ele.style[key] = current + "px";console.log(1);//4.清除定时器
//                    if(Math.abs(json[key]-current)<=Math.abs(step)){//                        ele.style[key] = json[key] + "px";
//                        clearInterval(ele.timer);
//                    }}}, 25);}//兼容方法获取元素样式function getStyle(ele, attr) {if (window.getComputedStyle) {return window.getComputedStyle(ele, null)[attr];}return ele.currentStyle[attr];}
</script>
</body>
</html>

实现效果:

2、上方的代码改进:清除定时器

上方的代码中,我们还需做一下清除定时器的判断:只有所有的参数都到达指定位置了,我们就清除定时器。

完整版代码如下:

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>div {position: absolute;top: 40px;left: 10px;width: 100px;height: 100px;background-color: pink;}</style>
</head>
<body><button>运动到json中设置的位置</button>
<div></div><script>var btnArr = document.getElementsByTagName("button");var div = document.getElementsByTagName("div")[0];btnArr[0].onclick = function () {var json = {"left": 100, "top": 200, "width": 300, "height": 300};animate(div, json);}//参数变为3个function animate(ele, json) {//先清定时器clearInterval(ele.timer);ele.timer = setInterval(function () {//开闭原则var bool = true;//遍历属性和值,分别单独处理json//attr == key(键)    target == json[key](值)for (var key in json) {//四部var current = parseInt(getStyle(ele, key)) || 0;//1.获取步长var step = (json[key] - current) / 10;//2.二次加工步长step = step > 0 ? Math.ceil(step) : Math.floor(step);current = current + step;//3.赋值ele.style[key] = current + "px";//4.清除定时器//判断: 目标值和当前值的差大于步长,就不能跳出循环//不考虑小数的情况:目标位置和当前位置不相等,就不能清除清除定时器。if (json[key] !== current) {bool = false;}}console.log(1);//只有所有的属性都到了指定位置,bool值才为true;if (bool) {clearInterval(ele.timer);}}, 25);}//兼容方法获取元素样式function getStyle(ele, attr) {if (window.getComputedStyle) {return window.getComputedStyle(ele, null)[attr];}return ele.currentStyle[attr];}</script>
</body>
</html>

运行效果同上。

3、进一步深入:如果有要同时执行讴多个动画时,就要用到回调函数(重要)**:

上面的代码中,我们要做的动画是:

    btnArr[0].onclick = function () {var json = {"left": 100, "top": 200, "width": 300, "height": 300};animate(div, json);}

上面的代码是执行这一个动画,可如果要同时执行两个动画呢?一般人自然想到的是下面的写法:(错误的写法)

    btnArr[0].onclick = function () {var json1 = {"left": 100, "top": 200, "width": 300, "height": 300};var json2 = {"left": 200, "top": 10, "width": 150, "height": 150};animate(div, json1);animate(div, json2);}

但是这样写的话,第二个动画 json2 会把第一个动画 json1 层叠掉。也就是说,第一个动画直接就不执行了。效果如下:

这显然不是我们想看到的。

如果我们想先执行第一个动画fn1(),再执行第二个动画fn2()的话,就要用到回调函数。意思是,将第二个动画fn2()作为回调函数即可。

完整版代码如下:

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>div {position: absolute;top: 40px;left: 10px;width: 100px;height: 100px;background-color: pink;}</style>
</head>
<body><button>运动到 json 设置的位置</button>
<div></div><script>var btnArr = document.getElementsByTagName("button");var div = document.getElementsByTagName("div")[0];btnArr[0].onclick = function () {var json1 = {"left": 100, "top": 200, "width": 300, "height": 300};var json2 = {"left": 300, "top": 10, "width": 100, "height": 100};animate(div, json1, function () { //第三个参数是回调,可以保证json1的动画执行结束后,再执行json2的动画animate(div, json2);})}//带有回调的动画封装function animate(ele, json, fn) {//先清定时器clearInterval(ele.timer);ele.timer = setInterval(function () {//开闭原则var bool = true;//遍历属性和值,分别单独处理json//attr == key(键)    target == json[key](值)for (var key in json) {//四部var current = parseInt(getStyle(ele, key)) || 0;//1.获取步长var step = (json[key] - current) / 10;//2.二次加工步长step = step > 0 ? Math.ceil(step) : Math.floor(step);current = current + step;//3.赋值ele.style[key] = current + "px";//4.清除定时器//判断: 目标值和当前值的差大于步长,就不能跳出循环//不考虑小数的情况:目标位置和当前位置不相等,就不能清除清除定时器。if (json[key] !== current) {bool = false;}}console.log(1);//只有所有的属性都到了指定位置,bool值才不会变成false;if (bool) {clearInterval(ele.timer); //定时器结束,代表第一个函数fn1()执行完毕了,接下来可以执行回调函数fn2()了。//只有传递了回调函数,才能执行if (fn) {  //【重要】第一个函数执行完毕了,定时器也清除了。现在,如果有人送了fn()这个回调函数过来,那就执行fn()fn();  // 函数名+():执行该函数}}}, 25);}//兼容方法获取元素样式function getStyle(ele, attr) {if (window.getComputedStyle) {return window.getComputedStyle(ele, null)[attr];}return ele.currentStyle[attr];}</script>
</body>
</html>

效果如下:

上方代码中,如果我们要先后完成两个动画,执行的代码是:

        animate(div, json1, function () { //第三个参数是回调,可以保证json1的动画执行结束后,再执行json2的动画animate(div, json2);})

如果想要先后执行两个动画,那就以此类推:

        animate(div, json1, function () { //第三个参数是回调,可以保证json1的动画执行结束后,再执行json2的动画animate(div, json2,function () {animate(div,json3);});})

举例:仿360的右下角开机效果

代码实现:

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title><style>.box {width: 322px;position: fixed;bottom: 0;right: 0;}span {position: absolute;top: 0;right: 0;width: 30px;height: 20px;cursor: pointer;}</style><script>window.onload = function () {//需求:下面的盒子高先变为0,然后上面的大盒子的宽再变为0.var guanbi = document.getElementById("guanbi");var box = guanbi.parentNode;var b = document.getElementById("b");guanbi.onclick = function () {//下面的盒子高度变为0,然后大盒子的宽在变为0.animate(b, {"height": 0}, function () {animate(box, {"width": 0});});}}//封装好的动画函数function animate(ele, json, fn) {//先清定时器clearInterval(ele.timer);ele.timer = setInterval(function () {//开闭原则var bool = true;//遍历属性和值,分别单独处理json//attr == key(键)    target == json[key](值)for (var key in json) {//四部var current = parseInt(getStyle(ele, key)) || 0;//1.获取步长var step = (json[key] - current) / 10;//2.二次加工步长step = step > 0 ? Math.ceil(step) : Math.floor(step);current = current + step;//3.赋值ele.style[key] = current + "px";//4.清除定时器//判断: 目标值和当前值的差大于步长,就不能跳出循环//不考虑小数的情况:目标位置和当前位置不相等,就不能清除清除定时器。if (json[key] !== current) {bool = false;}}console.log(1);//只有所有的属性都到了指定位置,bool值才不会变成false;if (bool) {clearInterval(ele.timer);//所有程序执行完毕了,现在可以执行回调函数了//只有传递了回调函数,才能执行if (fn) {fn();}}}, 1);}/*** 获取元素样式兼容写法* @param ele* @param attr* @returns {*}*/function getStyle(ele, attr) {if (window.getComputedStyle) {return window.getComputedStyle(ele, null)[attr];}return ele.currentStyle[attr];}</script>
</head>
<body>
<div class="box"><span id="guanbi"></span><div class="hd" id="t"><img src="data:images/1.jpg" alt=""/></div><div class="bd" id="b"><img src="data:images/2.jpg" alt=""/></div>
</div>
</body>
</html>

效果如下:

4、对 opacity和 z-index 属性进行单独改进

我们以上的代码中,如果要进行动画参数的设置,是直接把参数放到json里面去的。例如:

        var json1 = {"left": 100, "top": 200, "width": 300, "height": 300};var json2 = {"left": 300, "top": 10, "width": 100, "height": 100};

可是,下面这两个属性,却不能这样放到json里,会出现兼容性的问题:

    opacity: 0.5;  //透明度z-index: 1;

如何改进呢?暂略。

client 家族的组成

clientWidth 和 clientHeight

盒子调用时:

  • clientWidth:获取盒子区域宽度(padding + width)。

  • clientHeight:获取盒子区域高度(padding + height)。

body/html调用时:

  • clientWidth:获取网页可视区域宽度。

  • clientHeight:获取网页可视区域高度。

clientX 和 clientY

event调用:

  • clientX:鼠标距离可视区域左侧距离。

  • clientY:鼠标距离可视区域上侧距离。

clientTop 和 clientLeft

  • clientTop:盒子的上border。

  • clientLeft:盒子的左border。

三大家族 offset/scroll/client 的区别

区别1:宽高

  • offsetWidth = width + padding + border

  • offsetHeight = height + padding + border

  • scrollWidth = 内容宽度(不包含border)

  • scrollHeight = 内容高度(不包含border)

  • clientWidth = width + padding

  • clientHeight = height + padding

区别2:上左

offsetTop/offsetLeft:

  • 调用者:任意元素。(盒子为主)
  • 作用:距离父系盒子中带有定位的距离。

scrollTop/scrollLeft:

  • 调用者:document.body.scrollTop(window调用)(盒子也可以调用,但必须有滚动条)
  • 作用:浏览器无法显示的部分(被卷去的部分)。

clientY/clientX:

  • 调用者:event
  • 作用:鼠标距离浏览器可视区域的距离(左、上)。

函数封装:获取浏览器的宽高(可视区域)

函数封装如下:

//函数封装:获取屏幕可视区域的宽高
function client() {if (window.innerHeight !== undefined) {//ie9及其以上的版本的写法return {"width": window.innerWidth,"height": window.innerHeight}} else if (document.compatMode === "CSS1Compat") {//标准模式的写法(有DTD时)return {"width": document.documentElement.clientWidth,"height": document.documentElement.clientHeight}} else {//没有DTD时的写法return {"width": document.body.clientWidth,"height": document.body.clientHeight}}
}

案例:根据浏览器的可视宽度,给定不同的背景的色。

PS:这个可以用来做响应式。

代码如下:(需要用到上面的封装好的方法)

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title>
</head>
<body><script src="tools.js"></script>
<script>//需求:浏览器每次更改大小,判断是否符合某一标准然后给背景上色。//  // >960红色,大于640小于960蓝色,小于640绿色。window.onresize = fn;  //页面大小发生变化时,执行该函数。//页面加载的时候直接执行一次函数,确定浏览器可视区域的宽,给背景上色fn();//封装成函数,然后指定的时候去调用和绑定函数名function fn() {if (client().width > 960) {document.body.style.backgroundColor = "red";} else if (client().width > 640) {document.body.style.backgroundColor = "blue";} else {document.body.style.backgroundColor = "green";}}
</script>
</body>
</html>

上当代码中,window.onresize事件指的是:在窗口或框架被调整大小时发生。各个事件的解释如下:

  • window.onscroll 屏幕滑动

  • window.onresize 浏览器大小变化

  • window.onload 页面加载完毕

  • div.onmousemove 鼠标在盒子上移动(注意:不是盒子移动)

获取显示器的分辨率

比如,我的电脑的显示器分辨率是:1920*1080。

获取显示器的分辨率:

    window.onresize = function () {document.title = window.screen.width + "    " + window.screen.height;}

显示效果:

上图中,不管我如何改变浏览器的窗口大小,title栏显示的值永远都是我的显示器分辨率:1920*1080。

方法的注释

方法写完之后(注意,一定要先写完整),我们在方法的前面输入/**,然后回车,会发现,注释的格式会自动补齐。

比如:

/*** 功能:给定元素查找他的第一个元素子节点,并返回* @param ele* @returns {Element|*|Node}*/
function getFirstNode(ele){var node = ele.firstElementChild || ele.firstChild;return node;
}

断点调试

(1)先让程序运行一遍。

(2)f12,弹出代码调试工具

(3)打断点:

然后刷新页面。

(4)一步步调试,每点击一次,执行一步:

(5)监视变量:

当然,也可以添加变量或者表达式到监视窗口。操作如下:

上图中,选择变量或表达式,然后右键add to watch.

然后监视窗口:

JavaScript:JavaScript特效相关推荐

  1. php实现页面雪花效果,JavaScript_使用javascript实现雪花飘落的效果,看了javascript网页特效实例大全 - phpStudy...

    使用javascript实现雪花飘落的效果 看了javascript网页特效实例大全中的图片飘下的效果实例,觉得值得动手学习下. 就把图片改成雪花图,完成一个雪花飘下的效果. 并且,其中有些内容比较陈 ...

  2. JavaScript文本特效实例小结【3个示例】

    本文实例讲述了JavaScript文本特效.分享给大家供大家参考,具体如下: 1.标题跑马灯 常常能够在一些新闻网站,或者其他地方,看到文字在一个特定的区域内,来回滚动.一旦超出边界就消失了,那么这些 ...

  3. JavaScript网页特效-限时秒杀

    限时秒杀是网络商家在某一预定的时间里,大幅度降低活动商品的价格,买家只要在这个时间里成功拍得此商品,便可以用超低的价格买到原本很昂贵的物品的一种营销活动.限时秒杀短时效的限定会给予用户更强烈的紧迫感, ...

  4. JavaScript网页特效-“渔夫打鱼晒网”程序设计

    程序设计时,把可能需要反复执行的代码封装为函数,然后在需要执行该段代码功能的地方进行调用,这样不仅可以实现代码的复用,更重要的是可以保证代码的一致性,只需要修改该函数代码,则所有调用位置均得到体现.同 ...

  5. JavaScript网页特效-“扫雷”游戏随机布雷功能

    "扫雷"游戏是一款经典益智小游戏.游戏目标是在最短的时间内找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输.本节介绍"扫雷"游戏随机布雷功能的设计与实现. ...

  6. 《JavaScript网页特效经典300例》

    <JavaScript网页特效经典300例> 基本信息 作者: 杨磊    张志美 丛书名: 百炼成钢系列丛书 出版社:电子工业出版社 ISBN:9787121220524 上架时间:20 ...

  7. JavaScript网页特效-浮现社会主义核心价值观文字动画效果

    1.案例呈现 用户在页面单击鼠标左键,页面浮现"富强"."民主"."文明"."和谐"."自由".&q ...

  8. Javascript相框特效

    一款Javascript相框特效,效果效果可以参考: [url]http://www.zakisoft.com/html/effect2/effect2.html[/url] 运行效果图: [img] ...

  9. javascript经典特效(二)

    二十一 类似与google个性页面的好东东: View Code 二十二 漂亮的表格: View Code <!DOCTYPE html PUBLIC "-//W3C//DTD XHT ...

  10. [JavaScript] JavaScript数组挖掘,不只是讲数组哟(2)

    课程来源:后盾人 上一篇的内容:[JavaScript] JavaScript数组挖掘,不只是讲数组哟 数组引用类型分析,多维数组,用Array.of为数组创建细节,类型检测与转换,在一个数组后面加一 ...

最新文章

  1. 【高并发】32位多核CPU并发读写long型数据为何会出现诡异问题?看完这篇我懂了!...
  2. Boost::context模块callcc的circle测试程序
  3. leetcode 191. 位1的个数(移位操作)
  4. 电脑声音太小如何增强_感觉手机音量太小了?教你这样设置,声音立马大上许多...
  5. 错误:在keystone中无法找到默认角色user_第四章 keystone认证组件安装1
  6. 网络爬虫--14.【糗事百科实战】
  7. mysql保存时乱码了_MySQL保存中文乱码的原因和解决办法
  8. 网页小工具(学习中国象棋)
  9. LINUX下载编译nasm
  10. python选择哪个版本安装-初学Python版本选择
  11. android textview 设置字体,Android 设置TextView自动调整字体大小
  12. 查看git brach_Excel Go Brach – Excelebrations
  13. 嵌入式工程师必备开发工具
  14. CFA一级学习笔记--衍生品(二)--定价与估值
  15. 穿山甲android对接错误码40029,空Android项目集成Cocos、穿山甲。Lua调用网络接口。...
  16. VS2019源码,通过基址+偏移修改软件内阳光数值(植物大战僵尸单机版)
  17. C指针之指针与数组的天生姻缘
  18. 洛克菲勒:世界上只有两种人头脑聪明...
  19. Leetcode 1125:最小的必要团队
  20. 论 *.pdm 文件的正确打开方式

热门文章

  1. 【无标题】 <程序环境>和<预处理>(也许会迟到,但永不缺席)
  2. 基于PyQt5完成的抠图功能-程序实现
  3. 关于安装谷歌浏览器的插件的说明
  4. 智能油烟机触控面板中应用的电容式触摸芯片
  5. 深度学习机器学习理论知识:范数、稀疏与过拟合合集(2)有监督模型下的过拟合与正则化加入后缓解过拟合的原理
  6. 张逸谈如何评价架构的优劣 转
  7. 学软件开发需要多少钱,要学多久?
  8. 怎么查同一网络下的计算机,Windows下查看同局域网其它主机MAC地址
  9. 【C语言基础】野指针与空指针
  10. 如何塑造社群荣誉感与归属感