JavaScript 动画

1. 元素尺寸和定位获取方法

1.1 元素尺寸的获取

  • 元素的宽高属性不一定等于实际宽高

在这里插入图片描述

1.2 offset 系列属性

1.2.1 offset 作用
  • 获取元素尺寸实际大小和边距

在这里插入图片描述

1.2.2 offset 常用属性
  • offsetWidth

    • 获取对象自身的宽度 ,包括内容、边框和内边距,(不包括外边距)
    • offsetWidth = width + border + padding
  • offsetHeight

    • 获取对象自身的高度 ,包括内容、边框和内边距,(不包括外边距)
    • offsetHeight = height + border + padding
  • offsetLeft

    • 距离第一个有定位的父级盒子左边的距离
    • 父级盒子必须要有定位,如果没有,则最终以body为准
  • offsetTop

    • 距离第一个有定位的父级盒子上边的距离
    • 父级盒子必须要有定位,如果没有,则最终以body为准
  • offsetParent

    • 返回当前对象的父级(带有定位)盒子,可能是父亲、也可能是爷爷
1.2.3 offset 系列属性使用说明
  • offsetLeft 和 offsetTop 相关的父级必须具有定位属性
    • 祖先级都有定位时,以最近的为基准
    • 祖先级都没有定位时,以body为基准
1.2.4 offset 系列属性和 style 的区别
  • offset 系列属性和 style 属性区别
    • offset 系列属性可以获取任何元素的边距style 只能获取具有定位元素的边距
    • offset 系列属性获取的是数字style 获取的除了数字,还有单位(px)
    • offset 系列属性只能读取不能赋值style 可以读写操作
    • offset 系列属性没有限制style 只能获取行内样式的边距
1.2.5 offsetParent 和 parentNode 区别
  • parentNode
    • parentNode 指与位置无关上级元素
    • parentNode最多能获取到document文档对象
  • offsetParent
    • offsetParent 指与位置有关上级元素
    • offsetParent 最多获取到body元素,在往上获取就是null
1.2.6 offset 练习
  • offset 属性导航条
<style>
   *{
     
     margin: 0;padding: 0;}
   ul{
     
     list-style: none;}
   #header{
     
     
       width: 400px;
       margin: 10px auto;
       position: relative;

   }
   span{
     
     
       display: inline-block;
       width: 80px;
       height: 40px;
       background: url(leaf.png);
       position: absolute;
       left: 0;
       top: 0;
   }
   li{
     
     
       width: 80px;
       height: 40px;
       text-align: center;
       line-height: 40px;
       float: left;
       position: relative;
   }
</style>
<script>
   window.onload = function(){
     
     
       var currentLeft = 0
       // 1. 绑定li的onmouseover事件
       var lis = document.getElementsByTagName('li');
       var span = document.getElementsByTagName('span')[0];
       var length = lis.length
       for(var i = 0; i < length; i++) {
     
     
           lis[i].onmouseover = function(){
     
     
               // console.log('当前列表项的左边距' + this.offsetLeft);
               // 2. 在事件里,设置span的style.left += 100px
               span.style.left = this.offsetLeft + 'px'
           }
           lis[i].onclick = function(){
     
     
               // 记录下当前列表项的位置
               currentLeft = this.offsetLeft
           }
           lis[i].onmouseout = function(){
     
     
               // 读取之前记录的列表项位置,并设置给span
               span.style.left = currentLeft + 'px'
           }
       }

   }
</script>
<body>
    <div id="header">
        <span></span>
        <ul>
            <li>首页</li>
            <li>热门商品</li>
            <li>商品详情</li>
            <li>购物车</li>
            <li>论坛</li>
        </ul>
    </div>
</body>

1.3 scroll 系列属性

1.3.1 scroll 作用
  • 获取元素的实际尺寸以及超出可视区域的尺寸

在这里插入图片描述

1.3.2 scroll 常用属性
  • scrollWidth
    • 网页正文全文宽度
  • scrollHeight
    • 网页正文全文高度
  • scrollTop
    • 网页被卷去的高度
  • scrollLeft
    • 网页被卷去的左边距
1.3.3 处理 scroll 家族浏览器适配问题
  • ie9+ 和 最新浏览器
window.pageXOffset; (scrollLeft)
window.pageYOffset; (scrollTop)
  • Firefox浏览器 和 其他浏览器
document.documentElement.scrollTop;
  • Chrome浏览器 和 没有声明 DTD <DOCTYPE >
document.body.scrollTop;
  • 兼容写法(推荐)
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; 
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
1.3.4 scrollTo(x,y) 和 scrollBy(x,y) 方法
  • scrollTo(x,y)

    • 把内容滚动到指定的坐标
    • 格式
      • scrollTo(xpos,ypos)
        • xpos 必需,要在窗口文档显示区左上角显示的文档的 x 坐标
        • ypos 必需,要在窗口文档显示区左上角显示的文档的 y 坐标
    • 网页大部分都没有水平滚动条,所以,这个 x 不太常用
  • scrollBy(x,y)

    • 把内容滚动指定的像素数
    • 格式
      • scrollBy(xnum,ynum)
        • xnum 必需,把文档向右滚动的像素数
        • ynum 必需,把文档向下滚动的像素数
    • 要使此方法工作 window 滚动条的可见属性必须设置为true
1.3.5 scroll 系列属性的获取
<style media="screen">
  #container {
     
     
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
    overflow: scroll;
  }

  #d1 {
     
     
    width: 400px;
    height: 500px;
    background-color: #ff0;
  }
</style>
<script type="text/javascript">
  window.onload = function(){
     
     
    var div = document.getElementById('d1');
    var divContainer = document.getElementById('container')
    console.log('scrollWidth:'+ div.scrollWidth) // scrollWidth 是内部子元素的自身宽度
    // 针对父级容器绑定 onscroll 事件,子元素绑定事件无效
    // 针对父级容器使用 scrollTop 属性,子元素无效
    divContainer.onscroll = function(){
     
     
      console.log('子元素滚动位置:' + divContainer.scrollTop);
    }
  }
</script>
<body>
  <div id="container">
    <div id="d1"></div>
  </div>
</body>
1.3.6 scroll 练习
  • NBA 导航练习
<style>
    *{
     
     margin: 0;padding: 0;}
    #head{
     
     
        width: 0;
        height: 0;
        background: url(../images/head.jpg);
        margin: 0;
    }
    #nav{
     
     
        width: 1530px;
        height: 48px;
        background: url(../images/nav.jpg);
        margin: 0;
    }
    #content{
     
     
        width: 1530px;
        height: 2350px;
        background: url(../images/main.jpg);
        margin: 0 auto;
    }
</style>
<script>
    window.onscroll = function(){
     
     
        var nav = document.getElementById('nav')
        var top = document.documentElement.scrollTop
        if (top >= 100) {
     
     
            nav.style.position = 'fixed'
            nav.style
        }else{
     
     

        }
    }
</script>
<body>
    <div id="head"></div>
    <div id="nav"></div>
    <div id="content"></div>
</body>

1.4 client 系列属性

1.4.1 client 作用
  • 获取元素可视区域的尺寸(不包含边框)

在这里插入图片描述

1.4.2 client 常用属性
  • clientWidth
    • 网页可见区域宽度
  • clientHeight
    • 网页可见区域高度
  • clientLeft
    • 返回的是元素左边框borderWidth
    • 如果不指定一个边框或者不定位该元素,其值就为0
  • clientTop
    • 返回的是元素上边框borderHeight
    • 如果不指定一个边框或者不定位该元素,其值就为0

1.5 pageX/pageY、screenX/screenY、clientX/clientY 区别

  • screenX/screenY 是以屏幕为基准进行测量
    • 当前元素距离屏幕的尺寸
  • pageXpageY 是以当前文档(绝对定位)为基准,不适用于IE6-8
  • clientXclientY 是以当前可视区域为基准,类似于固定定位

1.6 offset、client 和 scroll 区别

1.6.1 width 和 height 分析
  • scrollHeight 和 scrollWidth 表示元素实际尺寸(包含超出可视区域)
  • offsetHeight 和 offsetWidth 表示元素可视区域内尺寸,包括边框
  • clientHeight 和 clientWidth 表示元素可视区域内尺寸,不包括边框和滚动条
1.6.2 left 和 top 分析
  • scrollLeft(Top) 表示子元素移出容器的左(顶)边的距离
  • offsetleft(Top) 表示子元素和带定位属性的父级元素的左(顶)边距
  • clientLeft(Top) 表示元素的左(顶)边距

1.7 获取屏幕的可视区域

  • ie9及其以上的版本、最新浏览器
window.innerWidth, window.innerHeight
  • 标准模式浏览器
document.documentElement.clientWidth, document.documentElement.clientHeight
  • 怪异模式
document.body.clientWidth, document.body.clientHeight
  • 通用写法
function client() {
    
        
       if(window.innerWidth){
    
     // ie9及其以上的版本  
           return{
    
               
               width: window.innerWidth,
               height:  window.innerHeight  
           }    
       }else if(document.compatMode != 'CSS1Compat'){
    
      // 怪异模式  
           return{
    
               
               width: document.body.clientWidth,            
               height: document.body.clientHeight       
           }    
       }  
       // 标准    
       return{
    
          
             width: document.documentElement.clientWidth,        
             height: document.documentElement.clientHeight   
       }
}

2. JavaScript 动画制作基础

2.1 JavaScript 动画概述

  • Javascript 动画是指页面元素的外观样式发生逐步变化的过程
  • Javascript 依靠定时器,对页面元素的样式进行修改实现动画效果

2.2 JavaScript 动画类型

  • 匀速动画
  • 缓动动画
  • 复合动画
  • 链式动画
  • 。。。

2.3 JavaScript 动画制作步骤

  • 使用定时器进行定时控制
  • 设置定时的开始和终止条件
  • 修改元素的外观样式
var div = document.getElementById('d1')
setInterval(function(){
    
    
        var left = parseInt(div.style.left)
        div.style.left = (left + 10) + 'px'
}, 200);

2.4 JavaScript 动画制作中的 bug

  • 动画不能停止
  • 设置具体值时不能停止
  • 到达边界值时还可以运动
  • 重复点击加快动画执行速度

2.5 JavaScript 动画制作要点

  • 开始时先关闭定时器
  • 把运动和停止分开
  • 动画速度的调整方式
    • 通过定时器的时间间隔进行调整
    • 通过动画变化的步长进行调整,推荐采用
var timer = null
function moveRight(){
    
    
	var div = document.getElementById('d1')
	clearInterval(timer) // 停止定时器,可以在定时器创建之前执行
	timer = setInterval(function(){
    
    
		if(div.offsetLeft == 800) {
    
    
			clearInterval(timer)
		} else {
    
    
			div.style.left = (div.offsetLeft + 10) + 'px'
		}
	},100)	
}

2.6 JavaScript 动画练习

<style>
    *{
     
     margin: 0;padding: 0;}

    div {
     
     float: left;}

    #left {
     
     
      width: 100px;
      height: 200px;
      background-color: #ff0;
      margin-left: -100px;
      position: relative;
    }

    #right {
     
     
      width: 30px;
      height: 80px;
      background-color: #0f0;
      margin-top: 60px;
      position: absolute;
      right: -30px;
    }
</style>
<script>
    var timer = null
    var left,right
    window.onload = function(){
     
     
      left = document.getElementById('left')
      right = document.getElementById('right')
      left.onmouseover = function(){
     
     
        toRight()
      }
      left.onmouseout = function(){
     
     
        toLeft()
      }
    }
    function toRight(){
     
     
      clearInterval(timer)
      timer = setInterval(function(){
     
     
        var offsetLeft = left.offsetLeft
        if(offsetLeft == 0){
     
     
          clearInterval(timer)
        } else {
     
     
          left.style.marginLeft = (offsetLeft + 10) + 'px'
        }
      },100)
    }
    function toLeft(){
     
     
      clearInterval(timer)
      timer = setInterval(function(){
     
     
        var offsetLeft = left.offsetLeft
        if(offsetLeft == -100){
     
     
          clearInterval(timer)
        }else {
     
     
          left.style.marginLeft = (offsetLeft - 10) + 'px'
        }
      },100)
    }

</script>
<body>
    <div id="left">
      <div id="right">点我</div>
    </div>
</body>

3. JavaScript 动画框架设计

3.1 匀速动画制作

  • 通过传参方式实现动画代码的函数封装
    • 传入动画的对象
    • 传入样式修改后的终值target
    • 传入步长参数
function uniformAnimation(obj,target,step){
    
    
	clearInterval(timer)
	timer = setInterval(function(){
    
    
		var offsetLeft = obj.offsetLeft
		if(offsetLeft == target){
    
    
			clearInterval(timer)
		}else{
    
    
			obj.style.marginLeft = (offsetLeft + step) + 'px'
		}
	},1000)
}
  • 优化参数
    • 如何明确步长的正负
    • 通过终值的计算省略步长参数
function uniformAnimation(obj,target){
    
    
	var step
	clearInterval(timer)
	timer = setInterval(function(){
    
    
		var offsetLeft = obj.offsetLeft // 当前值

		// 根据当前值和目标终值的差异来确定动画步长
		step = (target - offsetLeft) > 0 ? 10 : -10
		if(offsetLeft == target){
    
    
			clearInterval(timer)
		}else{
    
    
			obj.style.marginLeft = (offsetLeft + step) + 'px'
		}
	},1000)
}
3.1.1 匀速动画制作的优化
  • 终止值无法相等时采取接近原则代替
  • 利用 Math.abs 函数进行数据的判断
function myAnimation1(obj, target){
    
    
    var step
    clearInterval(timer)
    timer = setInterval(function(){
    
    
        var offsetLeft = obj.offsetLeft  // 当前值
        // 根据当前值和目标终值的差异,来确定动画的步长
        step = (target - offsetLeft) > 0 ? 7 : -7
        if(Math.abs(offsetLeft - target) < 7){
    
     // 利用绝对值来判断是否清除定时器
            clearInterval(timer)
            obj.style.marginLeft = target + 'px' // 直接用终值进行赋值,排除误差
            // console.log('end:' + obj.style.marginLeft)
            // console.log('target:' + target)
        } else {
    
    
            obj.style.marginLeft = (offsetLeft + step) + 'px'
        }
    },100)
}
3.1.2 匀速淡入淡出动画练习
  • 匀速淡入淡出动画框架
    • 如何获取一个对象当前的透明值
    • window.getComputedStyle(obj, null).opacity
function fadeAnimation(obj,target) {
    
    
	var step
	clearInterval(timer)
	timer = setInterval(function(){
    
    
		var opacity = parseFloat(window.getComputedStyle(obj,null).opacity) // 当前透明度
		step = (target - opacity) > 0 ? 0.1 : -0.1
		if(opacity - target == 0){
    
    
			clearInterval(timer)
		}else{
    
    
			obj.style.opacity = opacity + step
		}
	},100)
}

3.2 缓动动画制作

  • 缓动动画是一种逐渐变慢并最后停止的动画
  • 缓动动画的速度由当前和终止两种状态的差值决定
  • 速度计算公式
    • 步长速度 =(终值止-当前值)/ 变化系数
function slowAnimation(obj,target){
    
    
	var step = 0
	clearInterval(timer)
	timer = setInterval(function(){
    
    
		step = (target - obj.offsetLeft) / 10  // 设置缓动动画的步长,逐渐递减
		if(target == obj.offsetLeft){
    
    
			clearInterval(timer)
		}else{
    
    
			obj.style.left = (obj.offsetLeft + step) + 'px'
		}
	},100)
}
3.2.1 缓动动画存在的问题
  • 由于除法带来的尾数问题,导致无法达到精度要求

在这里插入图片描述

3.2.2 缓动动画解决精度问题
  • 缓冲动画结束时的精度处理
    • Math.ceil
    • Math.floor
  • 缓冲动画结束的条件是终点重合
function slowAnimation(obj,target){
    
    
	var step = 0
	clearInterval(timer)
	timer = setInterval(function(){
    
    
		// 设置缓动动画的步长,逐渐递减
		step = (target - obj.offsetLeft) / 10
		// 根据动画执行的方向,确定尾数的处理方式
		step = step > 0 ? Math.ceil(step) : Math.floor(step)
		if(target == obj.offsetLeft){
    
    
			clearInterval(timer)
		}else{
    
    
			obj.style.left = parseFloat(obj.offsetLeft + step) + 'px'
		}
	},100)
}
3.2.3 缓动淡入淡出动画
  • 缓动淡入淡出动画框架
function fadeSlowAnimation(obj, target){
    
    
    var step = 0
    clearInterval(timer)
    timer = setInterval(function(){
    
    
        // 获取当前元素的透明度,并且乘以100,得到0-100之间的数值
        var opacity = parseFloat(window.getComputedStyle(obj, null).opacity) * 100
        // 获取缓动的步长
        step = (target - opacity) / 10
        // 确定缓动动画的方向
        step = step > 0 ? Math.ceil(step):Math.floor(step)
        if(target == opacity){
    
    
            clearInterval(timer)
        }else{
    
    
            obj.style.opacity = (opacity + step) / 100  // 设置当前透明度
        }
    },100)
}

3.3 复合动画制作

  • 复合动画是由多个元素或多个属性同时形成的动画
  • 多个元素动画存在的问题
    • 定时器产生冲突
  • 多个属性动画存在的问题
    • 属性值无法通用
3.3.1 多元素动画
  • 多个元素动画解决方案
    • 采用多个定时器
    • 定时器作为每个元素的独立属性
    • 公共属性也作为每个元素的独立属性
// 多元素缓动宽度变化动画框架
function eleAnimation(obj, target){
    
    
    var step = 0
    // 将定时器绑定在动画元素身上,避免公用定时器产生的冲突
    clearInterval(obj.timer)
    obj.timer = setInterval(function(){
    
    
        // 1. 设置缓动动画的步长,逐渐递减
        step = (target - obj.offsetWidth) / 10
        // 2. 根据动画执行的方向,确定尾数的处理方式
        step = step > 0 ? Math.ceil(step) : Math.floor(step)
        if(target == obj.offsetWidth){
    
    
            clearInterval(obj.timer)
        }else{
    
    
            obj.style.width = (obj.offsetWidth + step) + 'px'
        }
    },100)
}
3.3.2 多属性动画
  • 多个属性动画解决方案
    • 使用属性作为框架的参数,分别处理
    • 使用非行间样式的获取方法
console.log(div.style.width)  // 成员访问符获取元素的样式值
console.log(div.style.height)
console.log(div.style['width'])  // 键值方式获取元素的样式值
console.log(div.styel['height'])
// 多属性缓动动画框架
function attrAnimation(obj, target, attribute){
    
    
    var step = 0
    // 将定时器绑定在动画元素身上,避免公用定时器产生的冲突
    clearInterval(obj.timer)
    obj.timer = setInterval(function(){
    
    
        // 1. 设置缓动动画的步长,逐渐递减
        step = (target - parseInt(obj.style[attribute])) / 10
        // 2. 根据动画执行的方向,确定尾数的处理方式
        step = step > 0 ? Math.ceil(step) : Math.floor(step)
        if(target == parseInt(obj.style[attribute])){
    
    
            clearInterval(obj.timer)
        }else{
    
    
            obj.style[attribute] = (parseInt(obj.style[attribute]) + step) + 'px'
        }
    },100)
}

3.4 链式动画制作

  • 链式动画是指按先后顺序执行的多个动画
  • 工作原理
    • 在框架函数中添加一个动画函数作为参数
    • 在前一个动画清除定时器后,调用参数提供的动画函数
// 链式动画框架
function chainAnimation(obj, target, attribute, fn) {
    
    
    var step = 0
    // 将定时器绑定在动画元素上,避免公用定时器产生的冲突
    clearInterval(obj.timer)
    obj.timer = setInterval(function(){
    
    
        // 1. 设置动画执行的方向,确定尾数的处理方式
        step = (target - parseInt(obj.style[attribute])) / 10
        // 2. 根据动画执行的方向,确定尾数的处理方式
        step = step > 0 ? Math.ceil(step) : Math.floor(step)
        if(target == parseInt(obj.style[attribute])){
    
    
            clearInterval(obj.timer)
            if(fn) // 判断函数是否存在
              fn()   // 如果存在,则调用函数
        }else{
    
    
            obj.style[attribute] = (parseInt(obj.style[attribute]) + step) + 'px'
        }
    },100)
}

3.5 动画制作小结

动画类型 动画框架结构
基本型动画 Animation(target)
多元素动画 Animation(obj, target)
多类属性动画 Animation(obj, attr, target)
链式动画 Animation(obj, attr, target, fn)

4. 总结

  • JavaScript 简单动画封装总结,以后有机会再完善一下动画框架

猜你喜欢

转载自blog.csdn.net/qq_43645678/article/details/104569437
今日推荐