JS BOM技术

BOM

BOM概述

BOM( Browser Object Model )浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window

BOM的构成

在这里插入图片描述

  • BOM 比 DOM 更大,它包含 DOM。
  • window 对象是浏览器的顶级对象,它具有双重角色
  • 它是 JS 访问浏览器窗口的一个接口
  • 它是一个全局对象。定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法
  • 在调用的时候可以省略 window,前面学习的对话框都属于 window 对象方法,如 alert()、prompt()等。

window对象的常见事件

窗口加载事件

window.onload是窗口(页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像,脚本文件,CSS文件等),就调用的处理函数。

1
2
3
4
5
6
7
window.onload = function(){
//里面可以写JS代码
};

window.addEventListener("load",function(){

});
  • 有了window.onload就可以把JS代码写到页面元素的上方
  • 因为onload是等页面内容全部加载完毕,再去执行处理函数
  • window.onload 传统注册事件方式,只能写一次,以最后一个为准
  • 如果使用addEventListener 则没有限制
1
document.addEventListener('DOMContentLoaded',function(){})
  • DOMCountentLoaded事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等

  • 如果页面的图片很多的话, 从用户访问到onload触发可能需要较长的时间,交互效果就不能实现,必然影响用户的体验,此时用 DOMContentLoaded事件比较合适。

调整窗口大小事件

window.onresize 是调整窗口大小加载事件,当触发时就调用的处理函数

1
2
3
window.onresize = function() {}
// 或者
window.addEventListener('resize',function(){});
  • 只要窗口大小发生像素变化,就会触发这个事件
  • 我们经常利用这个事件完成响应式布局。window.innerWidth 当前屏幕的宽度

定时器

window 对象给我们提供了两个定时器

  • setTimeout()
  • setInterval()

setTimeout()定时器

setTimeout()方法用于设置一个定时器,该定时器在定时器到期后执行调用函数。

1
window.setTimeout(调用函数,[延迟的毫秒数]);
  • window可以省略
  • 这个调用函数
    • 可以直接写函数
    • 或者写函数名
    • 或者采取字符串 ‘函数名()’ (不推荐)
  • 延迟的毫秒数省略默认是0,如果写,必须是毫秒
  • 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符
  • setTimeout() 这个调用函数我们也称为回调函数 callback
  • 普通函数是按照代码顺序直接调用,而这个函数,需要等待事件,事件到了才会去调用这个函数,因此称为回调函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<script>
setTimeout(function(){
console.log('boom!!!');
}, 3000)


function fn(){
console.log('boooom!');
}

var timer1 = setTimeout(fn, 3000);
var timer2 = setTimeout(fn, 5000);
</script>
</body>

clearTimeout()停止定时器

clearTimeout()方法取消了先前通过调用 setTimeout()建立的定时器

1
window.clearTimeout(timeoutID)
  • window可以省略
  • 里面的参数就是定时器的标识符

setInterval()定时器

setInterval()方法重复调用一个函数,每隔这个时间,就去调用一次回调函数

  • 用法和注意点同setTimeout()

  • 第一次执行也是间隔毫秒数之后执行,之后每隔毫秒数就执行一次

clearInterval()停止定时器

  • clearInterval() 方法取消了先前通过调用 setInterval() 建立的定时器

倒计时效果案例

Snipaste_2022-04-30_16-26-12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<body>
<span class="hour">1</span>
<span class="minute">2</span>
<span class="second">3</span>

<script>
var hour = document.querySelector('.hour');
var minute = document.querySelector('.minute');
var second = document.querySelector('.second');
var inputTime = +new Date('2022-5-1 18:00:00'); //全局变量,设置倒计时时间
countDown(); //定时器会延迟,所以每次刷新页面先执行一次
setInterval(countDown, 1000);
function countDown(){
var nowTime = +new Date();
var times = (inputTime - nowTime) / 1000;

var h = parseInt(times / 60 / 60 % 24);
h = h < 10 ? '0' + h : h;
hour.innerHTML = h; //写入时间
var m = parseInt(times / 60 % 60);
m = m < 10 ? '0' + m : m;
minute.innerHTML = m;
var s = parseInt(times % 60);
s = s < 10 ? '0' + s : s;
second.innerHTML = s;
}
</script>
</body>

发送短信案例

Snipaste_2022-04-30_16-30-12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
发送短信: <input type="text"> <button>发送</button>

<script>
var btn = document.querySelector('button');
var time = 9; //设置剩余时间的全局变量,每次减一
btn.addEventListener('click', function(){ //点击发送后
btn.disabled = true; //禁用按钮
var timer = setInterval(function(){
if(time == 0){ //减到零
clearInterval(timer);
btn.disabled = false;
btn.innerHTML = '发送';
time = 9;
}else{
btn.innerHTML = '还剩'+ time +'秒可以重新发送';
time--;
}
}, 1000)
})
</script>
</body>

this指向

  • this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁

现阶段,我们先了解一下几个this指向

  • 全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)
  • 方法调用中谁调用this指向谁
  • 构造函数中this指向实例对象

JS执行机制

JS是单线程

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。

  • 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。

同步和异步

为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,于是,JS 中出现了同步和异步

  • 同步:前一个任务结束后再执行后一个任务

  • 异步:在做这件事的同时,你还可以去处理其他事情

同步任务

同步任务都在主线程上执行,形成一个 执行栈

异步任务

  • JS中的异步是通过回调函数实现的

  • 异步任务有以下三种类型

    • 普通事件,如click,resize
    • 资源加载,如load,error
    • 定时器,包括setInterval,setTimeout
  • 异步任务相关回调函数添加到任务队列中

在这里插入图片描述

执行过程

  1. 先执行执行栈中的同步任务
  2. 异步任务由异步进程处理放到任务队列(执行时才放入任务队列)
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行
  4. 由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( event loop)

在这里插入图片描述

location对象

window 对象给我们提供了一个 location属性用于获取或者设置窗体的url,并且可以解析url。

url

统一资源定位符(uniform resouce locator)是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。

url 的一般语法格式为:

1
2
3
protocol://host[:port]/path/[?query]#fragment

http://www.itcast.cn/index.html?name=andy&age=18#link
组成 说明
protocol 通信协议 常用的http,ftp,maito等
host 主机(域名) www.itheima.com
port 端口号,可选
path 路径 由零或多个'/'符号隔开的字符串
query 参数 以键值对的形式,通过&符号分隔开来
fragment 片段 #后面内容 常见于链接 锚点

location 对象属性

location对象属性 返回值
location.href 获取或者设置整个URL
location.host 返回主机(域名)www.baidu.com
location.port 返回端口号,如果未写返回空字符串
location.pathname 返回路径
location.search 返回参数
location.hash 返回片段 #后面内容常见于链接 锚点

案例:5s后跳转页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<button>点击</button>
<div></div>
<script>
var btn = document.querySelector('button');
var div = document.querySelector('div');
var timer = 5;
setInterval(function() {
if (timer == 0) {
location.href = 'http://www.itcast.cn';
} else {
div.innerHTML = '您将在' + timer + '秒钟之后跳转到首页';
timer--;
}

}, 1000);
</script>
</body>

获取URL参数

实现登录页面login.html到首页index.html的跳转,表单的提交

login.html

1
2
3
4
5
6
7
<body>
<form action="index.html">
用户名: <input type="text" name="uname">
<input type="submit" value="登录">
</form>
</body>

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<div></div>
<script>
console.log(location.search); // ?uname=andy
// 1.先去掉? substr('起始的位置',截取几个字符);
var params = location.search.substr(1); // uname=andy
console.log(params);
// 2. 利用=把字符串分割为数组 split('=');
var arr = params.split('=');
console.log(arr); // ["uname", "ANDY"]
var div = document.querySelector('div');
// 3.把数据写入div中
div.innerHTML = arr[1] + '欢迎您';
</script>
</body>

location对象方法

location对象方法 返回值
location.assign() 跟href一样,可以跳转页面(也称为重定向页面),可以后退
location.replace() 替换当前页面,因为不记录历史,所以不能后退页面
location.reload() 重新加载页面,相当于刷新按钮或者 f5 ,如果参数为true 强制刷新 ctrl+f5

navigator 对象包含有关浏览器的信息,它有很多属性

我们常用的是userAgent,该属性可以返回由客户机发送服务器的user-agent头部的值

下面代码可以判断用户是用哪个终端打开页面的,如果是用 PC 打开的,我们就跳转到 PC 端的页面,如果是用手机打开的,就跳转到手机端页面

1
2
3
4
5
6
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
window.location.href = ""; //手机
} else {
window.location.href = ""; //电脑
}

history对象

window 对象给我们提供了一个 history 对象,与浏览器历史记录进行交互

该对象包含用户(在浏览器窗口中)访问过的 URL。

history对象方法 作用
back() 可以后退功能
forward() 前进功能
go(参数) 前进后退功能,参数如果是 1 前进1个页面 如果是 -1 后退1个页面
1
2
3
4
5
6
7
8
9
10
11
12
<body>
<a href="list.html">点击我去往列表页</a>
<button>前进</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
// history.forward();
history.go(1);
})
</script>
</body>

PC端网页特效

元素偏移量offset

offset概述

我们使用offset系列相关属性可以动态地得到该元素的位置(偏移)、大小等

  • 获得元素距离带有定位的父元素的偏移量
  • 获得元素自身的大小(宽度高度)
  • 返回的数值都不带单位
offset系列属性 作用
element.offsetParent 返回该元素带有定位的父级元素,如果没有则返回body
element.offsetTop 返回元素带有定位的父元素上方的偏移,如果没有则返回body的偏移
element.offsetLeft 返回元素带有定位的父元素左边的偏移,如果没有则返回body的偏移
element.offsetWidth 返回自身包括padding、边框、内容区的宽度
element.offsetHeight 返回自身包括padding、边框、内容区的宽度

offset和style的区别

offset style
offset可以得到任意样式表中的样式值 style只能得到行内样式表的样式值
offset系列获得的数值时没有单位的 style.width获得的是带有单位的字符串
offsetWidth包含padding+border+width style获得不包含padding和border的值
offsetWidth等属性是只读属性,只能获取不能赋值 style.width是可读写属性,可以获取也可以赋值
想要获取元素大小位置,用offset更合适 想要给元素更改值,则使用style

拖拽模态框案例

点击链接,显示登录框和遮罩背景,鼠标在登录框标题位置按下,可以移动登录框

Snipaste_2022-05-03_15-52-37

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<script>
var login = document.querySelector('.login'); //登录框
var mask = document.querySelector('.login-bg'); //背景
var link = document.querySelector('#link'); //点击链接
var closeBtn = document.querySelector('#closeBtn'); //关闭按钮
var title = document.querySelector('#title'); //登录框标题栏
//点击链接,显示遮罩和登录框
link.addEventListener('click', function(){
login.style.display = 'block';
mask.style.display = 'block';
})
//点击关闭按钮,去除遮罩和登录框
closeBtn.addEventListener('click',function(){
login.style.display = 'none';
mask.style.display = 'none';
})
//点击登录框标题,实现鼠标按下移动效果
title.addEventListener('mousedown', function(e){
var x = e.pageX - login.offsetLeft; //获得鼠标在距离登录框的偏移量,在按下
var y = e.pageY - login.offsetTop; //之后这两个值为固定值
document.addEventListener('mousemove', move); //在整个页面移动
function move(e){
login.style.left = e.pageX - x + 'px'; //更改登录框位置
login.style.top = e.pageY - y + 'px';
}
document.addEventListener('mouseup', function(){ //在鼠标弹起后移除事件
document.removeEventListener('mousemove', move);
})
})
</script>

放大镜效果

Snipaste_2022-05-03_16-47-29

  • 页面结构搭建:在小图片盒子里放入一个遮罩盒子,采取定位方式,再在小盒子里放入一个大的图片盒子,在右边显示,也采用定位的方式,在这大图片盒子里放入大图片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//使用外部的js文件
window.addEventListener('load', function() {

var preview_img = document.querySelector('.preview_img'); //小图片盒子
var mask = document.querySelector('.mask'); //遮罩
var big = document.querySelector('.big'); //大图片盒子

// 1. 当我们鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子
preview_img.addEventListener('mouseover', function() {
mask.style.display = 'block';
big.style.display = 'block';
})
preview_img.addEventListener('mouseout', function() {
mask.style.display = 'none';
big.style.display = 'none';
})
// 2. 鼠标移动的时候,让黄色的盒子跟着鼠标来走
preview_img.addEventListener('mousemove', function(e) {

// (1). 先计算出鼠标在盒子内的坐标
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;

// (2) 减去盒子高度 300的一半 是 150 就是我们mask 的最终 left 和top值了
// (3) 我们mask 移动的距离
var maskX = x - mask.offsetWidth / 2;
var maskY = y - mask.offsetHeight / 2;

// (4) 如果x 坐标小于了0 就让他停在0 的位置
// 遮挡层的最大移动距离
var maskMax = preview_img.offsetWidth - mask.offsetWidth;
if (maskX <= 0) {
maskX = 0;
} else if (maskX >= maskMax) {
maskX = maskMax;
}
if (maskY <= 0) {
maskY = 0;
} else if (maskY >= maskMax) {
maskY = maskMax;
}

mask.style.left = maskX + 'px';
mask.style.top = maskY + 'px';

// 3. 遮罩层的移动和大图片的移动成比例:
// 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离

var bigIMg = document.querySelector('.bigImg'); //大图片

// 大图片最大移动距离
var bigMax = bigIMg.offsetWidth - big.offsetWidth;
// 大图片的移动距离 X Y

var bigX = maskX * bigMax / maskMax;
var bigY = maskY * bigMax / maskMax;

bigIMg.style.left = -bigX + 'px';
bigIMg.style.top = -bigY + 'px';

})

})

client系列

使用client系列相关属性可以获取元素可视区的相关信息,动态得到该元素的边框大小,元素大小

client系列属性 作用
element.clientTop 返回元素上边框的大小
element.clientLeft 返回元素左边框的大小
element.clientWidth 返回自身包括padding,内容区的宽度,不含边框,返回数值不带单位
element.clientHeight 返回自身包括padding,内容区的高度,不含边框,返回数值不带单位

scroll系列

使用scroll系列的相关属性可以动态的得到该元素的大小,滚动距离

scroll系列属性 作用
element.scrollTop 返回被卷去的上侧距离,返回数值不带单位
element.scrollLeft 返回被卷去的左侧距离,返回数值不带单位
element.scrollWidth 返回自身内容实际的宽度,不含边框,返回数值不带单位
element.scrollHeight 返回自身内容实际的高度,不含边框,返回数值不带单位

固定侧边栏案例

Snipaste_2022-05-10_16-41-16

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<body>
<div class="slider-bar">
<span class="goBack">返回顶部</span>
</div>
<div class="header w">头部区域</div>
<div class="banner w">banner区域</div>
<div class="main w">主体部分</div>

<script>
//1. 获取元素
var sliderbar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
// banner.offestTop 就是被卷去头部的大小 一定要写到滚动的外面
var bannerTop = banner.offsetTop
// 当我们侧边栏固定定位之后应该变化的数值
var sliderbarTop = sliderbar.offsetTop - bannerTop;

var main = document.querySelector('.main');
var goBack = document.querySelector('.goBack');
var mainTop = main.offsetTop;
// 2. 页面滚动事件 scroll
document.addEventListener('scroll', function() {

// 3 .当我们页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位
if (window.pageYOffset >= bannerTop) { //获取页面滚动大小
sliderbar.style.position = 'fixed';
sliderbar.style.top = sliderbarTop + 'px';
} else {
sliderbar.style.position = 'absolute';
sliderbar.style.top = '300px';
}
// 4. 当我们页面滚动到main盒子,就显示 goback模块
if (window.pageYOffset >= mainTop) {
goBack.style.display = 'block';
} else {
goBack.style.display = 'none';
}

})
</script>
</body>

三大系列总结

三大系列大小对比 作用
element.offsetWidth 返回自身包括padding,边框,内容区的宽度
element.clientWidth 返回自身包括padding,内容区的宽度,不包含边框
element.scrollWidth 返回自身实际的宽度,不包含边框

主要用法

  • offset系列常用于获取元素位置:offsetLeft offsetTop

  • client系列常用于获取元素大小: clientWidth clientHeight

  • scroll系列常用于获取元素滚动距离: scrollTop scrollLeft

  • 获取页面滚动距离通过:window.pageXOffset window.pageYOffset

mouseenter和mouseover的区别

当鼠标移动到元素上时就会触发mouseenter事件,类似mouseover,两者的差别是

  • mouseover鼠标经过自身盒子会触发,经过子盒子还会触发,mouseenter只会经过自身盒子时触发,因为mouseenter不会冒泡

  • 与mouseenter搭配鼠标离开mouseleave同样不会冒泡

动画原理

核心原理:通过定时器setInterval()不断移动盒子位置

1
2
3
4
5
6
7
8
9
<script>
var div = document.querySelector('div');
var timer = setInterval(function(){
if(div.offsetLeft >= 300){
clearInterval(timer);
}
div.style.left = div.offsetLeft + 1 + 'px'; //获取当前位置并加上1个移动距离
}, 30)
</script>

简单的动画函数封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<body>
<button>点击purple</button>
<div></div>
<span></span>
<script>
var div = document.querySelector('div');
var span = document.querySelector('span');
var btn = document.querySelector('button');
function animate(obj, target){
//先清除,避免连续点击按钮触发多个定时器
clearInterval(obj.timer);
//给每个对象的属性添加
obj.timer = setInterval(function(){
if(obj.offsetLeft > target){
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 1 + 'px';
}, 30)
}
btn.addEventListener('click', function(){
animate(span, 200);
})
animate(div, 300);
</script>
</body>

缓动动画的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<body>
<button class="btn500">点击到500</button>
<button class="btn900">点击到900</button>
<span></span>
<script>
var span = document.querySelector('span');
var btn500 = document.querySelector('.btn500');
var btn900 = document.querySelector('.btn900');
function animate(obj, target, callback){ //回调函数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)
}
btn500.addEventListener('click', function(){
animate(span, 500, function(){ //传入回调函数
span.style.backgroundColor = 'skyblue';
});
})

btn900.addEventListener('click', function(){
animate(span, 900);
})
</script>
</body>

滑动条案例

Snipaste_2022-05-15_15-46-49

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//引入animate.js的缓动动画函数
<body>
<div class="sliderbar">
<span></span>
<div class="con">问题反馈</div>
</div>
<script>
var sliderbar = document.querySelector('.sliderbar');
var con = document.querySelector('.con');
sliderbar.addEventListener('mouseenter', function() {
animate(con, -160, function() {
sliderbar.children[0].innerHTML = '→'; //h
});
})

sliderbar.addEventListener('mouseleave', function() {
animate(con, 0, function() {
sliderbar.children[0].innerHTML = '←';
});
})
</script>
</body>

轮播图案例

Snipaste_2022-05-16_16-48-41

结构搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<body>

<div class="focus fl">
<!-- 左侧按钮 -->
<a href="javascript:;" class="arrow-l">
&lt;
</a>
<!-- 右侧按钮 -->
<a href="javascript:;" class="arrow-r">
&gt;
</a>
<!-- 核心的滚动区域 -->
<ul>
<li>
<a href="#"><img src="upload/focus.jpg" alt=""></a>
</li>
<li>
<a href="#"><img src="upload/focus1.jpg" alt=""></a>
</li>
<li>
<a href="#"><img src="upload/focus2.jpg" alt=""></a>
</li>
<li>
<a href="#"><img src="upload/focus3.jpg" alt=""></a>
</li>

</ul>
<!-- 小圆圈 -->
<ol class="circle">

</ol>
</div>
</body>

功能需求

  1. 当鼠标经过轮播图时,左右按钮显示,离开时隐藏
  2. 点击小圆圈可以播放对应的图片
  3. 点击右侧按钮,图片向左播放一张,左侧按钮同理
  4. 图片播放的同时,小圆圈模块跟随一起变化
  5. 鼠标不经过轮播图,会自动播放图片,鼠标经过时,播放停止
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//在页面加载完之后
window.addEventListener('load', function(){
//1.当鼠标经过轮播图时显示左右两个按钮
//获得左右按钮和轮播图模块
var arrow_l = document.querySelector('.arrow-l');
var arrow_r = document.querySelector('.arrow-r');
var focus = document.querySelector('.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);

})

//2.根据图片个数动态生成小圆圈个数,点击每个圆圈,图片发生滚动
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle')
//获取轮播图大小
var focusWidth = focus.offsetWidth;

for(var i = 0; i < ul.children.length ; i++){
var li = document.createElement('li');
//为每个圆圈设置索引
li.setAttribute('index', i);
//为每个小圆圈添加事件
li.addEventListener('click', function(){
//选中圆圈改变颜色,排他思想
for(var i = 0; i < ol.children.length ; i++){
ol.children[i].className = '';
}
this.className = 'current';
//通过获得索引来控制图片移动距离
var index = this.getAttribute('index');
//同时需要更改这两个值保证左右按钮的移动和点击圆圈的移动的统一
num = index;
circle = index;
//调用缓动动画函数实现图片的移动
animate(ul, -index * focusWidth);
})
ol.appendChild(li);
}

ol.children[0].className = 'current';
//3.实现点击右侧按钮滚动图片,无缝滚动原理:在图片最后多复制一个第一张图片,在移动这一张图片时快速跳转到第一张,再滚动

//通过js克隆图片
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
//设置变量记录移动次数
var num = 0;
var circle = 0;
//右侧按钮点击事件
arrow_r.addEventListener('click', function(){
//达到最后一张图片时,并点击
if(num == ul.children.length - 1){
//跳转为第一张再滚动
ul.style.left = 0;
num = 0;
}
num++;
//滚动
animate(ul, -num * focusWidth);

//4.实现点击右侧按钮,小圆圈同时改变
circle++;
if(circle == ol.children.length){
circle = 0;
}
//封装的小圆圈改变函数
circleChange();
})

//左侧按钮实现,同右侧
arrow_l.addEventListener('click', function(){
//达到第一张图片时,点击
if(num == 0){
//回到最后一张再滚动
num = ul.children.length - 1;
ul.style.left = -num * focusWidth + 'px';
}
num--;
animate(ul, -num * focusWidth);

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 = '';
}
ol.children[circle].className = 'current';
}

//5.自动播放图片定时器,图片的移动方法同点击右侧按钮
var timer = setInterval(function(){
//手动触发dia
arrow_r.click();
}, 2000);
})

节流阀

用来防止轮播图按钮连续点击造成播放过快,当上一个函数动画内容执行完毕,再去执行下一个动画函数,让事件无法连续点击

核心思路:利用回调函数,添加一个变量来控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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(){
false = true; //设置回调函数
});
circle++;
if(circle == ol.children.length){
circle = 0;
}
circleChange();
}
})

带有动画的返回顶部案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
goBack.addEventListener('click',function(){
animate(0, 0);
})
//修改缓动动画函数
function animate(obj, target, callback){
clearInterval(obj.timer);
obj.timer = setInterval(function(){
//页面向上移动
var step = (target - window.pageYOffset) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if(window.pageYOffset == target){
clearInterval(obj.timer);
if(callback){
callback();
}
}
window.scroll(0, window.pageYOffset + step)

}, 15)

筋斗云案例

Snipaste_2022-05-19_20-23-26

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script src="animate.js"></script>
<script>
window.addEventListener('load', function(){
var cloud = document.querySelector('.cloud');
var c_nav = document.querySelector('.c-nav');
var lis = c_nav.querySelectorAll('li');
//记录点击后的位置
var current = 0;
for(var i = 0; i < lis.length; i++){
lis[i].addEventListener('mouseenter', function(){
animate(cloud, this.offsetLeft);
})
lis[i].addEventListener('mouseleave', function(){
animate(cloud, current);
})
lis[i].addEventListener('click', function(){
current = this.offsetLeft;
})
}
})
</script>