一.PC端网页特效
1.元素偏移量offset系列
offset
系列相关属性可以动态得到元素的位置(偏移)、大小等。
offset系列属性 | 作用 |
---|---|
element.offsetTop | 返回元素相对带有定位父元素上方的偏移 |
element.offsetLeft | 返回元素相对带有定位父元素左侧的偏移 |
element.offsetWidth | 返回自身包括padding、边框、内容的宽度,返回数值不带单位 |
element.offsetHeight | 返回自身包括padding、边框、内容的高度,返回数值不带单位 |
element.offsetParent | 返回带有定位的父级,如果父级都没有带定位,则返回body |
var div = document.querySelector('div')
// 1.offset获取位置
console.log(div.offsetTop);
console.log(div.offsetLeft);
// 2.offset获取元素大小(width+border+padding)
console.log(div.offsetWidth);
console.log(div.offsetHeight);
// 3.offset获取带有定位的父元素
console.log(div.offsetParent)
offset和style的区别:
- offset能获取
任意样式
表中元素的样式值,style只能得到行内样式表中的样式值 - offset获取到的
数值
是没有单位的,style获取到的是带有单位的字符串 - offset获取元素大小(
width+boder+padding
),style获取到的元素大小不包含padding,border - offset只能读取属性,不能赋值,style能读取属性,也能赋值
2.元素可视区client系列
client
(可视区),client相关属性来获取元素的可视区的信息,通过client的相关属性可以动态的获取到元素的边框大小、元素大小等。
client系列属性 | 作用 |
---|---|
element.clientTop | 获取元素上边框 |
element.clientLeft | 获取元素左边框 |
element.clientWidth | 返回自身包括padding、内容的宽度,不包含边框,返回数值不单单位 |
element.clientHeight | 返回自身包括padding、内容的高度,不包含边框,返回数值不单单位 |
var div = document.querySelector('div');
// 1.获取元素上边框
console.log(div.clientTop);
// 2.获取元素左边框
console.log(div.clientLeft);
// 3.获取元素大小
console.log(div.clientWidth);
console.log(div.clientHeight);
client和offset的区别:
- client得到的元素大小不包含边框(border)
3.元素滚动scroll系列
scroll
意为滚动的。scroll系列的相关属性可以动态的获得元素的大小,滚动距离等。
scroll系列属性 | 作用 |
---|---|
element.scrollTop | 返回被卷去的上侧距离,返回数值不带单位 |
element.scrollLeft | 返回被卷去的左侧距离,返回数值不带单位 |
element.scrollWidth | 返回自身实际的宽度,不包含边框,返回数值不带单位 |
element.scrollHeight | 返回自身实际的高度,不包含边框,返回数值不带单位 |
var div = document.querySelector('div');
// 1.获取自身实际的大小
console.log(div.scrollWidth);
console.log(div.scrollHeight);
// 2.返回被卷去上侧的距离(滚动距离)
div.addEventListener('scroll', function () {
console.log(div.scrollTop);
})
// 3.返回被全局左侧的距离(滚动距离)
console.log(div.scrollLeft);
scroll和client的区别:
- croll也client获取元素大小都不包含边框,scroll得到的是元素自身(包括内容)的大小,client得到的元素就是元素(盒子)的大小。
扩展
- 获取元素被卷去的(上、左侧距离)用
element.scrollTop
,element.scrollLeft
- 获取页面被卷去的(上、左侧距离)用
window.pageYOffset
,window.pageXOffset
4.立即执行函数
立即执行函数就是不需要调用就能够直接执行的函数,它有两种书写方式。
1.(function(){})()
(function (a, b) {
console.log(a + b);
})(1, 2);
2.(function(){}())
(function (a, b) {
console.log(a + b);
}(1, 2))
- 立即执行函数最大的作用就是,独立创建了一个作用域,里面所有的变量都是局部变量,不会有命名冲突的情况
- 如果一个js文件的代码都封装到立即执行函数里,此时引入其他js文件,不会存在命名冲突
mouseenter
和mouseover
的区别:
- mouseover经过自身和他的子盒子都会触发,mouserenter只会经过自身盒子触发。
- mouseenter和
mouseleave
不会冒泡。
4.动画函数封装
动画的核心原理: 通过定时器setInterval()
不断移动盒子位置。
实现步骤:
- 获得盒子当前位置
- 让盒子在当前位置加上一个移动距离
- 利用定时器不断重复这个操作
- 加一个结束定时器的条件
- 实现动画的元素需要添加定位,这样才能使用到
element.style.left
(1).简单的动画封装函数
function 函数名(目标对象,目标位置) {}
// 1.函数声明
function animate(obj, target) {
// 设置定时器
var Timer = setInterval(function () {
if (obj.offsetLeft > target) {
clearInterval(Timer);
}
// 元素添加移动距离
obj.style.left = obj.offsetLeft + 2 + 'px';
}, 50)
}
var div = document.querySelector('div');
// 2.调用函数
animate(div, 300);
- 参数1(
obj
)代表目标对象,参数2(target
)代表目标位置
(1). 给不同元素添加不同的定时器
核心原理:利用 JS 是一门动态语言,可以很方便的给当前对象添加属性。
function animate(obj, target) {
// 在调用定时器前,先清除一次原先的定时器
clearInterval(obj.Timer);
obj.Timer = setInterval(function () {
if (obj.offsetLeft > target) {
clearInterval(obj.Timer);
}
obj.style.left = obj.offsetLeft + 2 + 'px';
}, 50)
}
- 函数内如果用
var Timer
声明,每有一个对象调用,内存就多开辟一个空间
(变量Timer),如果对象太多了,浪费内层空间 - 使用
obj.Timer
(对象.属性
)的赋值形式就能很好的解决这个问题 - 先清除之前的定时器,只保留当前的一个定时器执行,这样能很好的解决连续点击事件动画连续调用animate()使得动画效果移动很快的
bug
(3). 缓动动画
function animate(obj, target) {
clearInterval(obj.Timer);
obj.Timer = setInterval(function () {
// 把步长值改为整数,不要出现小数的问题
var step = (target - obj.offsetLeft) / 10;
// 正着走时,向上取整,回来走时,向下取整
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
clearInterval(obj.Timer);
}
// 把每次+1这个步长值改为一个慢慢变小的值(step)
obj.style.left = obj.offsetLeft + step + 'px';
}, 15)
}
-
缓动动画就是让元素速度有所变化,最常见的就是让速度慢慢停下来。
-
核心算法:
(目标值-当前位置) / 10
做每次移动的距离(步长) -
判断步长是正值还是负值,如果是正值,则步长往大了取整,反之亦然
-
停止条件: 让当前盒子位置等于目标位置就停止定时器
(4). 缓动动画添加回调函数
函数回调原理: 函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当另一个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调。
// 1.函数声明
function animate(obj, target, callback) {
clearInterval(obj.Timer);
obj.Timer = setInterval(function () {
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
clearInterval(obj.Timer);
// 在定时器结束之后调用
if (callback) { // 判断有没有回调函数参数传过来,有就调用
callback();
}
}
obj.style.left = obj.offsetLeft + step + 'px';
}, 15)
}
// 2.函数调用
animate(span, 800, function () {
span.style.backgroundColor = 'red';
});
})
animate()
函数中的第三个参数用了函数作为实参,传递给函数内的形参callback
- callback接收传过来的实参(函数),相当于
callback = function() {}
所以调用的时候就是callback()
- 在定时器结束之后,在进行函数回调
动画函数使用:
可以将动画函数写入到一个js文件,当需要使用到动画效果时,直接引入动画函数进行调用即可。
5.常见网页特效案例
1.网页轮播图(js部分)
window.addEventListener('load', function () {
var focus = document.querySelector('.focus');
var left = document.querySelector('.left-arrow');
var right = document.querySelector('.right-arrow');
// 1. 鼠标经过/离开focus 左右箭头显示/隐藏
focus.addEventListener('mouseenter', function () {
left.style.display = 'block';
right.style.display = 'block';
clearInterval(timer);
timer = null;
})
focus.addEventListener('mouseleave', function () {
left.style.display = 'none';
right.style.display = 'none';
timer = setInterval(function () {
// 手动调用点击右按钮的事件
right.click();
}, 2000);
})
// 2.动态生成小圆圈(有几张图,就有多少小圆圈,利用循环)
var ul = focus.querySelector('ul');
var ol = focus.querySelector('ol');
// focusWidth放到外面来,点击右箭头时,图片滚动需要用到这个变量
var focusWidth = focus.offsetWidth;
for (var i = 0; i < ul.children.length; i++) {
// 创建小圆圈节点
var li = document.createElement('li');
// 创建小圆点(li)的同时,给每个小圆点设置索引号(自定义属性)
li.setAttribute('index', i);
// 将li放到ol中
ol.appendChild(li);
// 3.创建li的同时,给每个li添加点击事件
li.addEventListener('click', function () {
//排他思想清除其他li的current类
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 设置当前li的类名为current
this.className = 'current';
//4.添加动画函数 (target就是小圆点的索引号*图片的宽度 图片的宽度就是focus的宽度,获取过来)
// 获取当前li的索引号,并且乘上图片宽度,作为target参数
var index = this.getAttribute('index');
// 当我们点击了某个li,就要把这个li的索引号给num,circle(num控制下一张图片的播放,circle控制下一个小圆圈的播放,当前索引号给他们,他们就保持到了同步)
num = index;
circle = index;
// ul是往左走的,所以要的是负值
animate(ul, -index * focusWidth);
})
// 将第一个li的类名设置成current
ol.children[0].className = 'current';
}
// 5. 克隆ul的第一个孩子li,放到最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 6.点击左/右侧按钮,图片滚动一张
//声明一个变量,每点击一次就自增1,让这个变量乘图片的宽度,就是每次ul滚动的距离
var num = 0;
var circle = 0;
// flag节流阀 (我们点击按钮的时候,图片切换的很快(动画播放效果效果并没有完成就切换到下一张图片了),我们想让点击按钮很快的情况下,也要让动画先播放完成)
var flag = true;
right.addEventListener('click', function () {
if (flag) {
flag = false; // 关闭节流阀
// 做无缝滚动效果(如果走到了最后一张复制的图片,ul快速复原为left = 0)
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * focusWidth, function () {
flag = true; // 开启节流阀(让动画函数执行完成后,再打开节流阀)
});
// 点击右侧按钮,小圆圈跟随变化,可以声明一个变量cicle控制小圆圈
circle++;
// 判断当cicle == 4了,说明走到了最后一张图(克隆出来的图片),circle复原
if (circle == ol.children.length) {
circle = 0;
}
circleChange();
}
})
// 7.左侧按钮做法
left.addEventListener('click', function () {
if (flag) {
flag = false;
if (num == 0) {
// num走到最后一张
num = ul.children.length - 1;
// ul快速切换到最后一张
ul.style.left = -num * focusWidth + 'px';
}
num--;
animate(ul, -num * focusWidth, function () {
flag = true;
});
// 点击右侧按钮,小圆圈跟随变化,可以声明一个变量cicle控制小圆圈
circle--;
// 判断当cicle == 4了,说明走到了最后一张图(克隆出来的图片),circle复原
if (circle < 0) {
circle = ol.children.length - 1;
}
circleChange();
}
})
function circleChange() {
// 先清除其余的小圆圈的类名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下当前小圆圈的current类名
ol.children[circle].className = 'current';
}
// 8.自动播放轮播图
// 和点击右侧类型非常之相似
var timer = setInterval(function () {
// 手动调用点击右按钮的事件
right.click();
}, 2000);
})
二.移动端网页特效
1.触屏事件
移动端浏览器兼容性
较好,不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动端也有自己独特的地方。比如触屏事件 touch
(也称触摸事件),Android 和 IOS 都有。
1.常见的触屏事件:
触屏touch事件 | 说明 |
---|---|
touchstart | 手指触摸到一个DOM元素时触发 |
touchmove | 手指在一个DOM元素上滑动触发 |
touchend | 手指在一个DOM元素移开时触发 |
示例:
var div = document.querySelector('div');
div.addEventListener('touchstart', function () {
console.log('hello');
})
div.addEventListener('touchmove', function () {
console.log('hello');
})
div.addEventListener('touchend', function () {
console.log('hello');
})
2.touchEvent触摸事件对象
touchEvent
是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等。
触摸事件对象常见的三个对象列表:
触摸列表 | 说明 |
---|---|
touches | 正在触摸屏幕的所有手指列表 |
targetTouches | 正在触摸当前DOM元素的手指的列表 |
changeTouches | 手指状态发生了改变的列表(从无到右,从右到无的变化) |
示例:
var div = document.querySelector('div');
div.addEventListener('touchstart', function (e) {
// 一般都是触摸元素,所以最常用的是targetTouches
console.log(e.targetTouches[0]); // 可以获得正在触摸元素的第一个手指信息
})
div.addEventListener('touchend', function (e) {
console.log(e);
// 当我们手指离开了,就没有touches和targetTouches列表,但是会有changeTouches
})
3.移动端拖动元素
移动端拖动的原理: 手指移动中,计算出手指移动的距离,然后用盒子原来的位置
+ 手指移动的距离
。
touchstart
、touchmove
、touchend
可以实现拖动元素。
思路:
- touchstart: 获取手指的初始坐标以及盒子原来的位置
- touchmove: 计算手指滑动的距离并且移动盒子。
示例:
var div = document.querySelector('div');
// 保存手指初始位置的变量
var startX = 0;
var startY = 0;
// 保存盒子原来的位置的变量
var x = 0;
var y = 0;
div.addEventListener('touchstart', function (e) {
// 1.获取手指的初始位置
startX = e.targetTouches[0].pageX;
startY = e.targetTouches[0].pageY;
// 2.获取盒子原来的位置
x = this.offsetLeft;
y = this.offsetTop;
})
div.addEventListener('touchmove', function (e) {
// 3.获取手指移动的距离(手指移动后的距离 - 手指的初始位置)
var moveX = e.targetTouches[0].pageX - startX;
var moveY = e.targetTouches[0].pageY - startY;
// 4.移动盒子(盒子原来的位置 + 手指移动距离)
this.style.left = x + moveX + 'px';
this.style.top = y + moveY + 'px';
// 5.阻止屏幕滚动后的默认行为
e.preventDefault();
})
e.targetTouches[0].pageX
/.pageY
可以获取到第一个手指触摸到元素的x轴或y轴坐标- 用户在滑动屏幕上的DOM元素过程中,可能也会滑动到屏幕,使用
e.preventDefault()
可以阻止屏幕滚动后的默认行为
4.classList的使用
classList
属性是HTML5新增的一个属性,返回元素的类名。ie10以上版本支持。
该属性用于在元素中添加,移除及切换类。
(1) 添加类
element.classList.add('类名')
var div = document.querySelector('.one');
div.classList.add('three');
(2) 删除类
element.classList.remove('类名')
div.classList.remove('one')
(3)切换类
div.classList.toggle('one')
- classList.toggle()使用时,无该类名则添加,有该类名则删除
2.移动端常见特效
移动端轮播图
移动端轮播图功能和PC端基本一致,可以自动播放图片,也可以用手指拖动播放轮播图。
示例(js部分):
window.addEventListener('load', function () {
// 获取元素
var focus = document.querySelector('.focus');
var ul = focus.children[0];
var ol = focus.children[1];
// 1.利用定时器自动轮播图片
var index = 0;
var w = focus.offsetWidth;
var timer = setInterval(function () {
index++;
var translatex = -index * w;
// 移动端可以使用过渡做动画效果
ul.style.transition = 'all .3s'
ul.style.transform = 'translateX(' + translatex + 'px)';
}, 2000);
// 2.无缝滚动(等着过渡完成之后,再加上判断) transitionend 过渡完成事件
ul.addEventListener('transitionend', function () {
// 判断滚动到最后一张图(复制出来的图)时
if (index >= 3) {
index = 0;
// 关闭过渡效果 ul能快速跳到目标位置
ul.style.transition = 'none';
// 用最新索引号乘图片宽度
var translatex = -index * w;
// 回到第一张的图片
ul.style.transform = 'translateX(' + translatex + 'px)';
} else if (index < 0) {
// 滚动到最后一张
index = 2;
ul.style.transition = 'none';
var translatex = -index * w;
ul.style.transform = 'translateX(' + translatex + 'px)';
}
//3.小圆点跟随变化
// 把ol里面li带有current类名的全部选出来去掉
ol.querySelector('.current').classList.toggle('current');
// 给当前的li添加current类
ol.children[index].classList.add('current');
})
//4.手指拖动轮播图
// 触摸元素: 获取手指初始坐标
var startX = 0;
var moveX = 0; //全局变量 后面还要使用到
var flag = false;
ul.addEventListener('touchstart', function (e) {
startX = e.targetTouches[0].pageX;
// 手指触摸时,定时器关闭
clearInterval(timer);
})
// 移动手指: 计算手指滑动距离,并且移动盒子
ul.addEventListener('touchmove', function (e) {
// 计算移动距离
moveX = e.targetTouches[0].pageX - startX;
// 原先盒子位置下移动盒子
var translatex = -index * w + moveX;
// 手指拖动时,不需要动画效果,关闭过渡
ul.style.transition = 'none';
ul.style.transform = 'translateX(' + translatex + 'px)';
// 如果用户手指移动过 我们再去判断否则不做判断效果(有时候可能犹豫了,只是点了一下并没有拖动,)
flag = true;
e.preventDefault(); //阻止滚动屏幕行为
})
// 手指离开: 根据移动距离判断是回弹还是播放上一张/下一张
ul.addEventListener('touchend', function () {
if (flag) {
// 1.移动距离大于50 播放上/下一张
if (Math.abs(moveX) > 50) { // 要取绝对值,因为左右两边滑动距离得出的数值有正有负
if (moveX > 0) { // 如果是向右滑动(moveX是正值),则播放上一张图片
index--;
} else {
index++; // 如果是向左滑动(moveX是负值),则播放下一张图片
}
var translatex = -index * w;
ul.style.transition = 'all .3s';
ul.style.transform = 'translateX(' + translatex + 'px)';
} else {
// 2.移动距离小于50 回弹当前
var translatex = -index * w;
ul.style.transition = 'all .3s';
ul.style.transform = 'translateX(' + translatex + 'px)';
}
}
// 3.手指离开后,继续开启定时器
// 开启前,先清除之前的定时器
clearInterval(timer);
timer = setInterval(function () {
index++;
var translatex = -index * w;
// 移动端可以使用过渡做动画效果
ul.style.transition = 'all .3s'
ul.style.transform = 'translateX(' + translatex + 'px)';
}, 2000);
})
//5.返回顶部模块
var goBack = document.querySelector('.goBack');
var nav = document.querySelector('nav');
window.addEventListener('scroll', function () {
if (window.pageYOffset >= nav.offsetTop) {
goBack.style.display = 'block';
} else {
goBack.style.display = 'none';
}
});
goBack.addEventListener('click', function () {
window.scroll(0, 0);
})
})
2.移动端解决click延迟300ms问题方案
(1).禁用用户缩放
<meta name="viewport" content="user-scalable=no"
(2).利用touch事件自己封装这个事件解决300ms延迟
// 1.封装tap
function tap(obj, callback) {
var isMove = false;
var startTime = 0; // 记录触摸时的时间变量
obj.addEventListener('touchstart', function (e) {
startTime = Date.now(); // 记录触摸时间
});
obj.addEventListener('touchmove', function (e) {
isMove = true; // 判断是否有滑动,有滑动算拖拽,不算点击
})
obj.addEventListener('touchend', function (e) {
if (!isMove && (Date.now() - startTime) < 150) { // 如果手指触摸和离开时间小于150ms,就算点击
callback && callback(); // 执行回调函数
}
isMove = false; // 取反 重置
startTime = 0;
});
}
// 2.调用tap
tap(div, function () {
// 执行代码
});
(3).使用fastclick插件
<!-- 引入插件 -->
<script src="./fastclick.js">
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function () {
FastClick.attach(document.body);
}, false);
}
</script>