Javascript动画相关
- 家族之一offset
三大家族(offset/scroll/client)
事件对象/event (事件被触动时,鼠标和键盘的状态)(通过属性控制)
offset --偏移,补偿,位移。
js中有一套方便的获取元素尺寸的办法就是offset家族;
offsetWidth和offsetHight 以及offsetLeft和offsetTop以及offsetParent
共同组成了offset家族。
1.1 offsetWidth和offsetHight (检测盒子自身宽高+padding+border)
这两个属性,他们绑定在了所有的节点元素上。获取之后,只要调用这两个属性,我们就能够获取元素节点的宽和高。
offset宽/高 = 盒子自身的宽/高 + padding +border;
offsetWidth = width+padding+border;
offsetHeight = Height+padding+border;
1.2 offsetLeft和offsetTop (检测距离父盒子有定位的左/上面的距离)
返回距离上级盒子(带有定位)左边s的位置
如果父级都没有定位则以body为准
offsetLeft 从父亲的padding 开始算,父亲的border 不算。
在父盒子有定位的情况下,offsetLeft == style.left(去掉px)
1.3 offsetParent (检测父系盒子中带有定位的父盒子节点)
1、返回该对象的父级 (带有定位)
如果当前元素的父级元素没有进行CSS定位 (position为absolute或relative,fixed), offsetParent为body。
2、如果当前元素的父级元素中有CSS定位 (position为absolute或relative,fixed), offsetParent取最近的那个父级元素。
1.4 offsetLeft和style.left区别
最大区别在于offsetLeft可以返回没有定位盒子的距离左侧的位置。
而 style.left不可以
二、offsetTop 返回的是数字,而 style.top 返回的是字符串,除了数字外还带有单位:px。
三、offsetTop 只读,而 style.top 可读写。(只读是获取值,可写是赋值)
四、如果没有给 HTML 元素指定过 top 样式,则style.top 返回的是空字符串。
style.left在=的左边和右边还不一样。(左边的时候是时候是值)属性,右边的
offsetLeft获取盒子距离左侧具有定位的父盒子的距离(没有的body),四舍五入取整。
Style.left获取的是具体值。 (赋值的时候也是直接赋值)
ffsetLeft 和 offsetTop获取值
style.left style.top赋值
- 动画入门
部分函数(必须掌握)
Math.abs(num); //取绝对值
Math.floor(num); //向下取整
Math.ceil(num); //向上取整
Math.round(num); //四舍五入 负数四舍六入
Math.random() //随机数0-1
- 家族之二Scroller
scroll这个单词本身是--卷页,卷曲
3.1 ScrollWidth和scrollHeight(不包括border)
检测盒子的宽高。(调用者:节点元素。属性。)
盒子内容的宽高。(如果有内容超出了,显示内容的高度)
IE567可以比盒子小。 IE8+火狐谷歌不能比盒子小
3.2 scrollTop和scrollLeft
网页,被浏览器遮挡的头部和左边部分。
被卷去的头部和左边部分。
3.3 他有兼容性问题
1.未声明 DTD(谷歌只认识他)
document.body.scrollTop
2.已经声明DTD(IE678只认识他)
document.documentElement.scrollTop
火狐/谷歌/ie9+以上支持的
window.pageYOffset
3.兼容写法:
var aaa=window.pageYOffset||document.documentElement.scrollTop || document.body.scrollTop || 0;
var aaa=document.documentElement.scrollTop+document.body.scrollT
3.4判断页面有没有DTD
document.compatMode === "BackCompat"
BackCompat 未声明
CSS1Compat 已经声明
注意大小写
function scroll() { // 开始封装自己的scrollTop
if(window.pageYOffset !== undefined) { // ie9+ 高版本浏览器
// 因为 window.pageYOffset 默认的是 0 所以这里需要判断
return {
left: window.pageXOffset,
top: window.pageYOffset
}
}
else if(document.compatMode === "CSS1Compat") { // 标准浏览器 来判断有没有声明DTD
return {
left: document.documentElement.scrollLeft,
top: document.documentElement.scrollTop
}
}
return { // 未声明 DTD
left: document.body.scrollLeft,
top: document.body.scrollTop
}
}
3.5 事件
a) onscroll事件
只要页面滚动无论向左向右,向上向下,哪怕只有1px,都会触动这个事件
b) 屏幕跳转
window.scrollTo
方法可把内容滚动到指定的坐标。
格式:
scrollTo(xpos,ypos)
xpos 必需。要在窗口文档显示区左上角显示的文档的 x 坐标。
ypos 必需。要在窗口文档显示区左上角显示的文档的 y 坐标
3.5 案例
使用fixed定位
window.onsroll(){}绑定事件
- 定义全局定时器timer
- 获取对象
- 给对象绑定事件
- 计算步长
- 处理步长(向上取整 向下取整)
- 计算屏幕滑动距离(leader = leader+step)
- 调用函数window.srollTo(0,learder);
- 判断是否到达指定地点,闪现清理定时器
4获取title、body、head、html标签
document.title --- 文档标题;
document.head --- 文档的头标签
document.body --- 文档的body标签;
document.documentElement --- 这个很重要
它表示文档的html标签,也就是说,基本结构的html标签并不是通过document.html访问的,而是document.documentElement;
5 关于json
Json是一种和数组类似的数据类型。
不同的是:数组中的元素是单一的。
而json中的元素,是以键值对的形式出现的。(key: value)
定义方法
var json = { key1:value1,key2:value2,key3:value3... };
数组是通过索引值获取数组中的元素的,而json是通过key获取元素的。
获取内容
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,我们称之为JavaScript对象表示法。使用JSON进行数据传输的优势之一。表示方法为键值对,key:value。
var myjson={k1:v1,k2:v2,k3:v3...}
注意:不用添加双引号,
var json = {"name":"xh","sno":"35","lover":{"sex":"gril","name":"ty"}};
获取方式:v1 == myjson.k1 v2 == myjson.k2
Json一般就是被当做一个配置单用;
遍历:
for(var i in json){
console.log(i);
console.log(json[i]);
}
6.事件对象(event)
再触发DOM上的某个事件时,会产生一个事件对象event
比如鼠标操作时候,会添加鼠标位置的相关信息到事件对象中。(类似Date)
普通浏览器支持 event(带参,任意参数)
ie 678 支持 window.event(无参,内置)
总结:他是一个事件中的内置对象。内部装了很多关于鼠标和事件本身的信息。
6.1 event的事件获取
IE678中,window.event
在火狐谷歌中,event或者,在事件绑定的函数中,加参,这个参数就是event.
Box.onclick = function (aaa){ //aaa就是event
var aa = event.target; //获取点击的dom节点
6.2 clientX与clientY
pageX/pageY:鼠标相对于整个页面顶部的x坐标,y坐标
screenX/screenY: 鼠标相对于整个屏幕的x坐标,y坐标
6.3 兼容获取方式有两种:
不写参数直接使用event;
写参数,但是为event....var event = event || window.event;(主要用这种)
6.4 PageY和pageX的兼容写法(很重要)
在页面位置就等于 = 看得见的+看不见的
pageY/pageX=event.clientY/clientX+scroll().top/scroll().left
6.5 onmousemove事件
常用事件:
- onmouseover 鼠标经过
- onmouseenter
- onmouseleave
- onmouseout 鼠标离开
- onmousedown 鼠标按下
- onmouseup 鼠标弹起
- onmousemove 鼠标移动(1px也触动)
只要鼠标在绑定该事件的事件源上移动,哪怕1像素,也会触动这个事件。(这个事件可以直接或者间接的替代定时器)
获取鼠标在盒子的位置
sda |
targex = pageX – div.offsetTop;
targey = pageY – div.offsetLeft;
div.onmousemove = function(event){
event = event ||window.event;
//1.获取鼠标在整个页面的位置
var pagex = event.pageX||scroll().left+event.clientX;
var pagey = event.pageY||scroll.top+event.clientY;
//2.获取盒子在整个页面的位置
//3.用鼠标的位置减去盒子的位置赋值给盒子的内容
var targetx = pagex – div.offsetLeft;;
var targety = pagey – div.offsetTop;
}
clientX/clientY: 鼠标相对于当前可见页面的x坐标,y坐标
event阻止表单submit跳转
7.三大家族之三client用户(可视区)
注意:event.clientX 与 event.clientY是event事件的成员变量。与这个不同
包括:
7.1 client的属性
1、clientWidth 获取网页可视区域宽度(两种用法)
clientHeight 获取网页可视区域高度(两种用法)
调用者不同,意义不同:
盒子调用:指盒子本身。
body/html调用:可视区域大小。
/*2、clientX 鼠标距离可视区域左侧距离(event调用)
clientY 鼠标距离可视区域上侧距离(event调用)
*/
3. clientTop 盒子的上border
clientLeft 盒子的左border
区别1:(offset/scroll/client宽高)
clientWidth = width + padding
clientHeight = height + padding
offsetWidth = width + padding + border
offsetHeight = height + padding + border
scrollWidth = 内容宽度(不包含border)
scrollHeight = 内容高度(不包含border)
区别2:(offset/scroll/client上下)
offsetTop/offsetLeft :
调用者:任意元素。(盒子为主)
作用:距离父系盒子中带有定位的距离。
scrollTop/scrollLeft:(盒子也可以调用,必须有滚动条)
调用者:document.body.scrollTop/.....(window)
作用:浏览器无法显示的部分(被卷去的部分)。
clientY/clientX:(clientTop/clientLeft 值的是border)
调用者:event.clientX(event)
作用:鼠标距离浏览器可视区域的距离(左、上)。
7.2 client家族之:检浏览器宽/高度(可视区域)
ie9及其以上的版本
window.innerWidth/Height
标准模式(有DTD)(“CSS1Compat”)
document.documentElement.clientWidth
document.documentElement.clientHeight
怪异模式 (没有DTD)
document.body.clientWidth
document.body.clientHeight
封装:类似scroll();
根据浏览器可视宽度,给定背景色。
模拟响应式 : 移动版(640)/平板版/PC版(960)
移动端 / PC端
8. 冒泡事件
事件传播的三个阶段是:捕获、冒泡和目标阶段
事件捕获阶段:事件从最上一级标签开始往下查找,直到捕获到事件目标(target)。
事件冒泡阶段:事件从事件目标(target)开始,往上冒泡直到页面的最上一级标签。
8.1 冒泡定义
div1>div2>div3 都设置点击事件时,
点击div3 会触发div3 div2 div1 事件
冒泡:从原始元素一直冒泡到dom树的最上层
捕获:从上往下执行
8.2冒泡顺序
IE 6.0:
div -> body -> html -> document
其他浏览器:
div -> body -> html -> document -> window
不是所有的事件都能冒泡。以下事件不冒泡:blur、focus、load、unload、onmouseenter
onmouseleave
检测一个事件是否冒泡:
document.onmouseenter = function(fn){
event||event||window.event;
console.log(event.bubbles);
}
如何阻止冒泡
w3c的方法是:(火狐、谷歌、IE11)
event.stopPropagation()
IE10以下则是使用:
event.cancelBubble = true
兼容代码如下:
var event = event || window.event;
if(event && event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
- 变量属性获取/赋值方法
1给属性赋值:(既能获取又能赋值)
-
- div.style.width 单个赋值
- div.style[“width”] 变量赋值
2获取属性值:(只能获取)
-
- div.currentStyle.width; IE678 单个获取
- window.getComputedStyle(div,null).width;
- div.currentStyle[“width”]; IE678 变量获取
- window.getComputedStyle(div,null)[“width”];
参数1:获取属性的元素。
参数2:伪元素,C3学习。
兼容方法获取元素样式
function getStyle(ele,attr){
if(window.getComputedStyle){
return window.getComputedStyle(ele,null)[attr];
}
return ele.currentStyle[attr];
}
注意:封装获取到的数值要用parseInt去掉px单位
如var leader = parseInt(getStyle(div,attr))||0;
10.正则表达式
正则定义(1.内置对象法,2.字面量)
//1.对象定义法
var reg1 = new RegExp(/abc/);
//2.字面量
var reg2 = /def/;
console.log(reg2);
//验证 该方法返回的是一个bool值
reg1.test(“ab”);
N.经验
if(x<0){
mask.style.left = 0+"px";
}
if(x>box.offsetWidth-mask.offsetWidth){
mask.style.left = box.offsetWidth-mask.offsetWidth;
}
else{
mask.style.left = x+"px";
}
if(y<0){
mask.style.top = 0+"px";
}
else if(y>box.offsetHeight-mask.offsetHeight){
mask.style.top = box.offsetHeight-mask.offsetHeight;
}
else{
mask.style.top = y +"px";
}
if语句都tm要赋值,能不能移出if再赋值呢?
不然卡死你?为什么卡呢?
改良:
if(x<0){
x = 0;
}
if(x>box.offsetWidth-mask.offsetWidth){
x = box.offsetWidth-mask.offsetWidth;
}
if(y<0){
y = 0;
}
else if(y>box.offsetHeight-mask.offsetHeight){
y= box.offsetHeight-mask.offsetHeight;
}
mask.style.left = x+"px";
mask.style.top = y +"px";
- 自己封装的框架获取属性并赋值并移动
很多属性我们的框架无法获取值和赋值。
border-radius: 1px 21px 41px 1px ;
opacity: 0.5;
background: rgba(0,0,0,.4);
z-index: 1;
- 值为小数,获取的时候要特殊处理。
- 兼容问题。IE678不识别opacity;
层级的提高是一次性直接提到最高,不需要一点一点儿的缓动。
function animate(ele,json,fn){
clearInterval(ele.timer);
//开闭原则
var bool = true;
ele.timer = setInterval(function(){
//遍历属性与值
for(var k in json){
//如果b不等于undefined和null
var leader = parseInt(getStyle(ele,k))||0;
//判断如果属性为opacity的时候特殊获取值
if(k === "opacity"){
leader = getStyle(ele,k)*100 || 1;
}else{
leader = parseInt(getStyle(ele,k)) || 0;
}
//1获取步长
var step = (json[k]-leader)/10;
step = step>0?Math.ceil(step):Math.floor(step);
leader = leader + step;
//3.赋值
//特殊情况特殊赋值
if(k === "opacity"){
ele.style[k] = leader/100;
//兼容IE678
ele.style.filter = "alpha(opacity="+leader+")";
//如果是层级,一次行赋值成功,不需要缓动赋值
//为什么?需求!
}else if(k === "zIndex"){
ele.style.zIndex = json[k];
}else{
ele.style[k] = leader + "px";
}
console.log(1);
//4.清理定时器
//判断: 目标值和当前值的差大于步长,就不能跳出循环
//不考虑小数的情况:目标位置和当前位置不相等,就不能清除清除定时器。
if(json[k] !== leader){
bool = false;
}
}
if(bool){
clearInterval(ele.timer);
if(fn){
fn();
}
}
},50);
N.案例
- (案例)轮播图(setInterval+ style.left+animate)
基本概念:
步长 = step = (目标距离 – 所在距离)/10 = target – div.offsetLeft;(缓动)
var speed = target>ele.offsetLeft?10:-10; (平均)
移动方法: div.style.left = div.offsetLeft+ step;
时间控制函数: setInterval(函数,间隔时间) //每间隔时间执行一次函数
实现效果
原理:通过子绝父相 控制ul的style-left距离(向左移动肯定为负数)
//移动函数自定义
function animate(ele,target) {
clearInterval(ele.timer);
var speed = target>ele.offsetLeft?10:-10;
ele.timer = setInterval(function(){
//传递的目标值如果比当前值大,那么步长为+10
//传递的目标值如果比当前值小,那么步长为-10
if (ele.offsetLeft === target){
clearInterval(ele.timer);
}else{
ele.style.left = ele.offsetLeft+ speed+"px";
}
})
}
function autoPlay() {
//通过控制key的自增来模拟图片的索引值,然后移动ul
key++;
if(key>indicators.length){
//图片已经滑动到最后一张,接下来,跳转到第一张,然后在滑动到第二张
ul.style.left = 0;
key = 0;
}
animate(ul,-key*imageWidth);
//修改indicator的样式
}
- 拖拽窗口(style.left+event+onmouse事件)
结构:div(absolute)>top+bottom
整体思路:
1).top设置鼠标按下事件(onmousedown)
2).在按下事件内记录鼠标在盒子中的所在位置,以及设置文档鼠标移动事件
document.onmousemove = function(event){
记录鼠标在网页中的位置}
3).移动div,在鼠标离开div时清除document.onmousemove事件
hd.onmouseup = function () {
//解绑
document.onmousemove = null;
}
4).需要注意的是:盒子移动的距离为: 鼠标在网页中的位置-鼠标在盒子中的位置
移动盒子之后,//禁止文本选中(选中后取消) window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
其实没有也没什么问题
- 放大镜(onmousemove+event)
获得鼠标在盒子中的位置
移动黄色盒子
特殊情况1.离box 的距离小于黄色盒子的宽高时,黄色盒子距离box的距离为0
特殊情况2当鼠标距离盒子右边的值-1/2黄盒子的宽时>整个大盒子的宽-黄色盒子的宽时,黄色盒子移动的距离固定为,个大盒子的宽-黄色盒子的宽时
最后,大图片需要等比例移动
//大图片走的距离/mask盒子都的距离 = 大图片/小图片
var bili = bigImg.offsetWidth/small.offsetWidth;
var xx = bili*x;
var yy = bili*y;
bigImg.style.marginTop = -yy+"px";
bigImg.style.marginLeft = -xx+"px";
- 模拟滚动条
结构:
绑定事件:注意:onmousemove时document
block.onmousedown = function(event){
document.onmousemove = function(event){
//高级比例: 内容走的距离/bar走的距离 = (内容的高-大盒子的高)/(scroll的高-bar的高)
var bili = (p.offsetHeight - box_left.offsetHeight)/(block.offsetHeight-box_right.offsetHeight);
p.style.top = y*bili +"px";
//让被选文字清除。
window.getSelection() ? window.getSelection().removeAllRanges() : document.selection.empty();
document.onmouseup = function(event){
document.onmousemove = null;
}
- 隐藏登录框(event+ stopPropagation)
需求:点击白框外面隐藏白框
思路:
第一步:给登录按钮绑定显示白框事件,并阻止冒泡事件(第二步需要用到)
第二步:给document对象,绑定点击事件,判断事件源的id是否为login,否则隐藏白框
- 旋转木马(json,animate)
原理:将五张图片的css写成一个json数组,点击按钮为li更换样式,更换样式的方法为jquey的animate(div,json,fn);