目录
一、元素偏移量offset系列
1. offset概述
2. 常见属性
3. offset与style区别
案例——鼠标在盒子内坐标
案例——拖动模态框
案例——京东放大镜
二、元素可视区client系列
flexible源码分析
立即执行函数
pageshow事件
三、元素滚动scroll系列
1. 页面被卷去头部
案例——仿淘宝固定右侧侧边栏
2. 页面被卷曲头部兼容性方案
四、三大系列总结
五、mouseenter和mouseover事件
六、动画函数封装
1. 动画实现原理
2. 动画函数的简单封装
3. 动画函数给不同元素记录不同定时器
4. 缓动效果动画
5. 动画函数在多个目标之间移动
6. 给动画函数添加回调参数
7. 动画函数封装到单独JS文件里面
案例——右侧盒子滑动
七、常见网页特效案例
案例——网页轮播图
1. 节流阀
短路运算简写callback函数
案例——返回顶部
案例——筋斗云移动
offset翻译过来就是偏移量,我们使用offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
1
通过client系列的相关属性可以动态的得到该元素的边框大小、元素大小等。
(function flexible(window,document){
// 获取html的根元素
var docEl=document.documentElement;
// dpr像素比
var dpr=window.devicePixelRatio || 1;
// 设置body的字体大小
function setBodyFontSize(){
// 如果页面有body元素,就设置body页面文字大小
if(document.body){
document.body.style.fontSize=(12*dpr)+'px';
}else{
// 如果页面中无body,则等页面主要DOM元素加载完毕再设置body字体大小
document.addEventListener('DOMContentLoaded',setBodyFontSize);
}
}
setBodyFontSize();
//设置html元素文字大小
function setRumUnit(){
var rem=docEl.clientWidth/10;
docEl.style.fontSize=rem+'px';
}
setRumUnit();
// 当我们页面尺寸大小发生变化,要重新设置rem的大小
window.addEventListener('resize',setRumUnit());
//pageshow使我们重新加载页面触发的事件
window.addEventListener('pageshow',function(e){
// e.persisted返回true,就是说如果这个页面是从缓冲取过来的页面,也需要重新计算rem大小
if(e.persisted){
setRumUnit();
}
})
// 有些移动端不支持0.5像素的写法
if(dpr>=2){
var fakeBody=document.createElement('body');
var testElement=document.createElement('div');
testElement.style.border='.5px solid transparent';
fakeBody.appendChild(testElement);
docEl.appendChild(fakeBody);
if(testElement.offsetHeight===1){
docEl.classList.add('hairlines')
}
docEl.removeChild('fakeBody')
}
}(window,document))
//创建方法
//也可以传递参数
//独立创建了一个作用域,里面所有变量都是局部变量,不会有命名冲突情况
使用scroll系列的相关属性可以动态的得到该元素的大小、滚动距离等。
如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发onscroll事件
头部区域
主体部分
主要用法:
核心原理:通过定时器setInterval()不断移动盒子位置
实现步骤:
常见网页特效案例
注意需要传递2个参数,动画对象和移动到的距离
//简单函数封装,obj目标对象,target目标位置
function animate(obj, target) {
var timer = setInterval(function() {
if (obj.offsetLeft >= target) {
//停止动画,本质停止定时器
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 1 + 'px';
}, 30);
}
以上优化有bug:当我们不断点击按钮,这个元素会越来越快,应为开启太多定时器
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';
}, 30);
}
解决方案:先清除以前定时器,只保留当前的定时器
缓动动画就是让元素运动有所变化。
思路:
function animate(obj, target) {
clearInterval(obj.timer);
obj.timer = setInterval(function() {
//步长值写定时器里面
var step=(target-obj.offsetLeft)/10;
if (obj.offsetLeft >= target) {
clearInterval(obj.timer);
}
//把每次加1改为慢慢变小,(目标值-现在的位置)/ 10 ,作为每次移动的步长
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
可以让动画函数从800移动到500.
当我们点击按钮时,判断步长时正值还是负值
回调函数原理:函数可以作为一个参数。将这个函数作为参数传递到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫回调。
回调函数写的位置:定时器结束的位置。
因为以后经常使用这个动画函数,可以单独封装到一个JS文件里面,使用的时候引用这个JS文件即可。
function animate(obj, target, callback) {
// console.log(callback);//等价于 callback=function(){}调用的时候callback()
clearInterval(obj.timer);
obj.timer = setInterval(function() {
//步长值写定时器里面
// 把步长值改为整数,避免到不了固定位置
// var step = Math.ceil((target - obj.offsetLeft) / 10);
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();
}
}
//把每次加1改为慢慢变小,(目标值-现在的位置)/ 10 ,作为每次移动的步长
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
轮播图也称焦点图
//js
window.addEventListener('load', function() {
// 1.获取元素
var arrow_l = document.querySelector('.arrow-l');
var arrow_r = document.querySelector('.arrow-r');
var focus = document.querySelector('.focus');
var focusWidth = focus.offsetWidth;
// 2.鼠标经过focus 就显示隐藏左右按钮
focus.addEventListener('mouseenter', function() {
arrow_l.style.display = 'block';
arrow_r.style.display = 'block';
clearInterval(timer);
timer = null;
})
focus.addEventListener('mouseleave', function() {
arrow_l.style.display = 'none';
arrow_r.style.display = 'none';
timer = setInterval(function() {
// 手动调用事件
arrow_r.click()
}, 2000);
})
// 3.动态生成小圆圈 有几张图片,我就生成几个小圆圈
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle');
for (var i = 0; i < ul.children.length; i++) {
// 创建一个小li
var li = document.createElement('li');
// 记录当前小圆圈索引号,通过自定义属性来做
li.setAttribute('index', i);
// 把小li插入到ol里面
ol.appendChild(li);
//4.小圆圈的排他思想,我们可以直接再生产小圆圈的同时直接绑定点击事件
li.addEventListener('click', function() {
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
this.className = 'current';
//5.点击小圆圈,移动图片 当然是ul
//ul的移动距离就是小圆圈索引号乘以图片宽度 注意是负值
// 当我们点击了某个小li,就拿到li的索引号
var index = this.getAttribute('index');
// 当我们点击了某个小li,就要把这个li的索引号给num
num = index;
//当我们点击了某个小li,就把li的索引号给circle1
circle = index;
animate(ul, -index * focusWidth);
})
}
// 把ol里面第一个小li设置为current
ol.children[0].className = 'current';
// 6.克隆第一张图片放到ul最前面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
//7.右侧按钮点击事件
var num = 0;
var circle = 0; //控制小圆圈播放
var flag = true; //节流阀
arrow_r.addEventListener('click', function() {
if (flag) {
flag = false; //关闭节流阀
// 如果走到最后,图片滚到第一张
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * focusWidth, function() {
flag = true; //打开节流阀
});
// 8.点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放
circle++;
// 如果circle==4 说明走到了克隆的图片,我们就复原
if (circle == ol.children.length) {
circle = 0;
}
circleChange();
}
})
// 9.左侧按钮做法
arrow_l.addEventListener('click', function() {
if (flag) {
flag = false;
// 如果第一张,图片滚到最后一张
if (num == 0) {
num = ul.children.length - 1;
ul.style.left = -num * focusWidth + 'px';
}
num--;
animate(ul, -num * focusWidth, function() {
flag = true;
});
// 点击左侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放
circle--;
// 如果circle<0 说明走到了克隆的图片,我们就复原
// if (circle < 0 ) {
// circle = ol.children.length-1;
// }
circle = circle < 0 ? ol.children.length - 1 : circle;
circleChange();
}
});
function circleChange() {
// 先清除其余小圆圈的current类名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下当前的小圆圈current类名
ol.children[circle].className = 'current';
}
// 10。自动播放
var timer = setInterval(function() {
// 手动调用事件
arrow_r.click()
}, 2000);
})
//css
.focus {
position: relative;
width: 721px;
height: 455px;
background-color: purple;
overflow: hidden;
}
.focus ul {
position: absolute;
top: 0;
left: 0;
width: 600%;
}
.focus ul li {
float: left;
}
.arrow-l,
.arrow-r {
display: none;
position: absolute;
top: 50%;
margin-top: -20px;
width: 24px;
height: 40px;
background: rgba(0, 0, 0, .3);
text-align: center;
line-height: 40px;
color: #fff;
font-family: 'icomoon';
font-size: 18px;
z-index: 2;
}
.arrow-r {
right: 0;
}
.circle {
position: absolute;
bottom: 10px;
left: 50px;
}
.circle li {
float: left;
width: 8px;
height: 8px;
/*background-color: #fff;*/
border: 2px solid rgba(255, 255, 255, 0.5);
margin: 0 3px;
border-radius: 50%;
/*鼠标经过显示小手*/
cursor: pointer;
}
.current {
background-color: #fff;
}
防止轮播图点击按钮播放过快。
节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。
arrow_r.addEventListener('click', function() {
if (flag) {
flag = false; //关闭节流阀
// 如果走到最后,图片滚到第一张
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * focusWidth, function() {
flag = true; //打开节流阀
});
// 8.点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放
circle++;
// 如果circle==4 说明走到了克隆的图片,我们就复原
if (circle == ol.children.length) {
circle = 0;
}
circleChange();
}
})
// if (callback) {
// // 调用函数
// callback();
// }
callback && callback();
滚动窗口至文档中特定位置
window.scroll(x,y)
头部区域
主体部分