封装js动画
- 流程
- 定位
- 间隔定时器 计算运动量 让元素在当前位置的基础上加上一个运动量
- 判断是否到达终点 清除定时器
- 封装单属性动画
- 单个元素 单方向的运动
- 三个参数
- 运动的元素
- 运动的属性
- 运动的终点值
- 判断动画何时结束
- 四个参数
- 运动的元素
- 运动的属性
- 运动的终点值
- 运动结束之后执行的回调函数
- 四个参数
- 多属性的运动
- 三个参数
- 运动的元素
- 运动的一组属性(对象)
- 对象的key是属性
- 对象的value是终点值
- 运动结束之后执行的回调函数
- 三个参数
轮播图
- 通过改变图片长廊left属性
swiper轮播图插件
- 已经封装好的轮播图插件
- 使用步骤
- 下载swiper的css文件和js文件
- 准备轮播图的结构(不要轻易改变原结构的类名)记住低版本最外层
swiper-container
- 定义整个容器的宽高
- 进行初始化js操作
面向对象 OOP
-
面向对象是一门编程思想,用来指导我们思想编程的
-
编程思想
- 面向过程
- 关注每一步的实现
- 一般开发使用都面向过程
- 面向对象
- 不需要关注每一步的实现 只需要了解它的使用步骤
- 一般是用来造轮子
- 面向函数
- 暂不做介绍
- 面向过程
-
区别案例
- 吃饺子
- 面向过程
- 买面粉
- 买馅料
- 和面
- 做馅料
- 擀面皮
- 包饺子
- 煮饺子
- 吃饺子
- 面向对象
- 开一个饺子店
- 点一份饺子等着吃饺子
-
面向: 关注
-
对象: 有多个键值对的数据类型
-
核心思想: 利用自定义构造函数来创建对象,利用this来添加属性,利用构造函数的prototype来添加方法,在利用创建出来的对象解决开发中的问题
-
Math Date()
如何创建一个对象
- 需要给创建出来的对象添加属性 添加方法
+ 学生属性
- 用户名
- 密码
- 学号
+ 学生方法
- 登陆
- 测评
- 考试
- 采用字面量或者内置构造函数来创建对象
- var obj = {}
- var obj = new Object()
- 问题:代码冗余 不方便维护
- 利用工厂函数来创建对象
- 工厂函数: 某个函数功能是用来批量生产数据
- 优点: 代码简洁 方便维护
- 缺点: 自己创建一个对象 自己返回一个对象 公共的方法用的不是同一份
- 原型
- 每一个函数都有一个自带属性叫做prototype
- 每一个对象都有一个自带属性叫做__proto__
- 一般原型都要和构造函数结合使用
- 构造函数的prototype就是new出来对象的__proto__
- 构造函数
- 凡是利用new来调用的函数都称之为构造函数
- 内置构造函数: js自带的构造函数
- 自定义构造函数: 自己定义的构造函数
- 构造函数定义和普通一样(建议函数名大写)
- 构造函数利用new来调用的 普通函数直接调用的
- 利用自定义的构造函数创建对象
- new可以自动创建对象 自动返回对象
- 缺点:公共的方法用的不是同一份
- 原型链
- 每一个对象使用属性或者方法的时候会优先在自身查找 自身有就使用自身的
- 自身没有会往它的__proto__里面查找,找到了就使用 找不到就为undefined
- 利用自定义构造函数+原型来创建对象
-
new关键字到底做了啥?(面试题)
- 函数内部的this指向
- 函数的返回值
- 会自动在构造函数内部创建一个空对象
- 将构造函数内部的this指向到这个空对象
- 会自动返回这个创建的空对象
- 利用new来实现面向对象创建对象的功能,只要给构造函数内部的this添加属性和方法,new出来的对象就有这些属性和方法
-
实例化和实例(实例化对象)
- 实例化: new调用的过程就成为实例化过程
- new Person() 叫做new了一个person 或者叫做实例化了person
- 实例化对象: new出来的对象就成为实例化对象,简称实例
- var p = new Person() p叫做new出来的对象 也叫做实例化对象 也叫做实例 Person的实例
- 实例化: new调用的过程就成为实例化过程
实例
(以下代码均为课程实例)
(1)利用字面量或者构造函数创建对象
<script>
// 创建一个空对象
var student01 = {}
// 给对象添加属性
student01.username = 'jack'
student01.password = '123456'
student01.stuNo = '10086'
// 给对象添加方法
student01.login = function(){
console.log('登陆的方法')
}
student01.cePing = function(){
console.log('测评的方法')
}
student01.test = function(){
console.log('考试的方法')
}
// 得到一个对象
console.log(student01)
var student02 = {}
// 每一个同学都需要创建对象 添加属性和方法
// 对象属性
student02.username = 'rose'
student02.password = '123456'
student02.stuNo = '10088'
// 对象方法
student02.login = function(){
console.log('登陆的方法')
}
student02.cePing = function(){
console.log('测评的方法')
}
student02.test = function(){
console.log('考试的方法')
}
console.log(student02)
var student03 = {}
// 每一个同学都需要创建对象 添加属性和方法
// 对象属性
student03.username = 'tom'
student03.password = '3423'
student03.stuNo = '3333'
// 对象方法
student03.login = function(){
console.log('登陆的方法')
}
student03.cePing = function(){
console.log('测评的方法')
}
student03.test = function(){
console.log('考试的方法')
}
console.log(student03)
</script>
(2)利用工厂函数创建对象
<script>
function createStudent(username, password, stuNo){
// 创建一个空对象
var emptyObj = {}
// 添加属性
emptyObj.username = username
emptyObj.password = password
emptyObj.stuNo = stuNo
// 给对象添加方法
emptyObj.login = function(){
console.log('登陆的方法')
}
emptyObj.cePing = function(){
console.log('测评的方法')
}
emptyObj.test = function(){
console.log('考试的方法')
}
// 返回这个创建的对象
return emptyObj
}
var student01 = createStudent('jack', '123456', '10086')
console.log(student01)
var student02 = createStudent('rose', '123456', '10088')
console.log(student02)
// 两个对象方法功能相同 使用的是同一个方法吗?
student01.test()
student02.test()
// 不相等 原因是因为每一次创建的对象都是一个新的地址
console.log(student01.test === student02.test)
var student03 = createStudent('tom', '123456', '3333')
console.log(student03)
</script>
(3)原型
<script>
// 每一个函数都有一个自带的属性叫做prototype
function fn(){
}
console.dir(fn)
console.log(fn.prototype) // 这个prototype就是fn的原型 是一个对象
// 每一个对象都有一个自带的属性叫做__proto__ 也是一个对象
// 这个__proto__其实就是我们在浏览器里面看到的[[Prototype]]
var obj = {}
var obj1 = new Object()
console.log(obj)
console.log(obj.__proto__)
console.log(obj1)
console.log(obj.__proto__)
</script>
(4)构造函数
<script>
// 利用内置构造函数创建数组或者对象 正则表达式 date
new Array()
new Object()
new RegExp()
new Date()
// 自己创建一个构造函数
function student(){
}
// 使用方式 普通函数直接调用的 构造函数利用new来调用的
student() // 就是普通的函数
new student() // 就是构造函数
</script>
(5)new关键字
<script>
function student(){
console.log('student调用了...')
console.log(this) // 此处this代表谁? 直接调用指向是window 利用new来调用指向的是空对象
}
/* 从内部的this上面构造函数和普通函数的区别 */
// 普通函数
// student()
// 构造函数
// new student() // 也会调用student
/* 从返回值上面构造函数和普通函数的区别 */
function teacher(){
}
var t = teacher()
console.log(t) // undefined
var t1 = new teacher()
console.log(t1) // 返回的是一个空对象
/* */
function ss(){
// 会自动在构造函数ss里面创建一个空对象 var s = {}
// 会将函数内部的this指向到上面创建出来的对象
// 要给创建出来的空对象添加属性添加方法 this就是这个创建出来的空对象
// 给this添加属性 添加方式 其实就是给创建出来的空对象添加属性添加方法
// console.log(this)
this.username = 'jack' // this.username --> s.username
// 会将这个创建出来的空对象返回 return s
}
// 得到就是返回的空对象
var s = new ss()
console.log(s)
</script>
(6)利用自定义构造函数
<script>
function createStudent(username, password, stuNo){
// 添加属性
this.username = username
this.password = password
this.stuNo = stuNo
// 给对象添加方法
this.login = function(){
console.log('登陆的方法')
}
this.cePing = function(){
console.log('测评的方法')
}
this.test = function(){
console.log('考试的方法')
}
// 自动返回这个创建的对象
}
// createStudent就变成构造函数 这个构造函数是我们自己定义 称为自定义构造函数
var student01 = new createStudent('jack', '123456', '10086')
console.log(student01)
// 每new一次都会调用createStudent
var student02 = new createStudent('rose', '123456', '10088')
console.log(student02)
// 公用的方法用的是同一个吗?
console.log(student01.test === student02.test)
</script>
(7)再看原型
<script>
function student(){
}
console.log(student.prototype) // 构造函数也是一个函数 他也有prototype
// 在构造函数prototype添加一个属性或者方法
student.prototype.a = 1
student.prototype.test = function(){
console.log('考试')
}
var s = new student() // 每new一次构造函数 都会返回一个对象
// new出来的对象也是一个对象
console.log(s)
console.log(s.__proto__)
// 构造函数的prototype和new构造函数得到的对象__proto__是同一个地址
console.log(student.prototype === s.__proto__)
// 给student.prototype添加就相当于给s.__proto__添加
console.log(s.__proto__.a)
s.__proto__.test()
// 这是为什么?
console.log(s.a)
s.test()
</script>
(8)原型链
<script>
// 对象自身有a,b,say
var obj = {
// a:1,
b:2,
say: function(){
console.log('say...')
}
}
// 对象的原型上面也有 a,b,say
// obj.__proto__.a = 2
obj.__proto__.b = 3
obj.__proto__.say = function(){
console.log('say2...')
}
console.log(obj)
console.log(obj.a)
</script>
(9)利用自定义构造函数来解决创建对象的问题
<script>
function createStudent(username, password, stuNo) {
// 给对象自身添加属性 属性是每一个对象独特
this.username = username;
this.password = password;
this.stuNo = stuNo;
// 给对象添加方法
// 自动返回这个创建的对象
}
// 把公共的方法放到构造函数的原型添加
createStudent.prototype.login = function () {
console.log("登陆的方法");
};
createStudent.prototype.cePing = function () {
console.log("测评的方法");
};
createStudent.prototype.test = function () {
console.log("考试的方法");
};
// createStudent就变成构造函数 这个构造函数是我们自己定义 称为自定义构造函数
var student01 = new createStudent("jack", "123456", "10086");
console.log(student01);
// 每new一次都会调用createStudent
var student02 = new createStudent("rose", "123456", "10088");
console.log(student02);
// 每一个new出来对象它的__proto__和构造函数prototype是同一个地址
console.log(student01.__proto__ === createStudent.prototype)
console.log(student02.__proto__ === createStudent.prototype)
student01.test() // student01.__proto__.test()
student02.test()
// 公用的方法用的是同一个吗?
// 因为他们使用的都是createStudent.prototype里面的test方法
console.log(student01.test === student02.test);
</script>
(10)轮播图基础版
html部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<div class="slider">
<!-- 图片部分 -->
<ul class="imgs">
<li>
<img src="./img/01.png" alt="" />
</li>
<li>
<img src="./img/02.png" alt="" />
</li>
<li>
<img src="./img/03.jpg" alt="" />
</li>
<li>
<img src="./img/04.png" alt="" />
</li>
<li>
<img src="./img/05.png" alt="" />
</li>
</ul>
<!-- 左右箭头 -->
<div class="arrow">
<a href="javascript:void 0;" class="left"><</a>
<a href="javascript:void 0;" class="right">></a>
</div>
<!-- 小点 -->
<ol class="dots">
<li class="active"></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
</div>
<script>
// 获取整个图片的长廊
var slider = document.querySelector("ul");
// 获取所有的图片
var imgs = document.querySelectorAll("ul>li");
// 获取所有的点
var dots = document.querySelectorAll("ol>li");
// 获取一张图片的宽度
// console.log(imgs)
var width = imgs[0].offsetWidth;
// console.log(width)
/* 获取左右箭头 绑定点击事件 */
var leftBtn = document.querySelector(".arrow .left");
var rightBtn = document.querySelector(".arrow .right");
// 用一个变量表示当前是第几张图片
var index = 0;
/* 点击右箭头切换到下一张 */
rightBtn.addEventListener("click", function () {
index++;
// 如果到达最后一张 回到第一张
if (index === imgs.length) {
index = 0;
}
// 右键就是让整个长廊向左移动一张图片的宽度
slider.style.left = -width * index + "px";
setDotActive();
});
/* 点击左箭头切换到上一张 */
leftBtn.addEventListener("click", function () {
index--;
// 判断到达第一张
if (index === -1) {
index = imgs.length - 1;
}
slider.style.left = -width * index + "px";
setDotActive();
});
/* 封装一个点的激活色跟随的函数 */
function setDotActive() {
// console.log('设置点的激活色')
// 清除所有点的激活色
for (let i = 0; i < dots.length; i++) {
dots[i].classList.remove("active");
}
// 找到点和图片的对应关系 只给当前对应的点加激活色
dots[index].classList.add("active");
}
/* 点击点跳转到对应的图片 */
for (let i = 0; i < dots.length; i++) {
dots[i].onclick = function () {
index = i; // 把点击点的下标赋值给图片index
slider.style.left = -width * index + "px";
setDotActive();
};
}
var id = null
/* 自动轮播图 */
function next() {
id = setInterval(() => {
index++;
// 如果到达最后一张 回到第一张
if (index === imgs.length) {
index = 0;
}
// 右键就是让整个长廊向左移动一张图片的宽度
slider.style.left = -width * index + "px";
setDotActive();
}, 2000);
}
next()
// 鼠标移入停止轮播
slider.onmouseover = function () {
clearInterval(id);
};
// 鼠标离开继续轮播
slider.onmouseout = function () {
next()
};
</script>
</body>
</html>
css部分(index.css)
* {
margin: 0;
padding: 0;
list-style: none;
}
.slider {
position: relative;
width: 600px;
height: 400px;
border: 1px solid #ddd;
margin: 30px auto;
overflow: hidden;
}
.slider ul {
position: absolute;
left: 0;
top: 0;
}
.slider ul li {
width: 600px;
height: 400px;
float: left;
}
.slider ul li img {
width: 100%;
height: 100%;
}
.slider .arrow .left,
.slider .arrow .right {
position: absolute;
width: 40px;
height: 40px;
background-color: rgba(0, 0, 0, 0.4);
color: #fff;
text-align: center;
line-height: 40px;
text-decoration: none;
font-size: 30px;
top: 50%;
}
.slider .arrow .left {
left: 0;
}
.slider .arrow .right {
right: 0;
}
.slider .dots {
position: absolute;
bottom: 20px;
left: 0;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.slider .dots li {
width: 20px;
height: 20px;
border-radius: 50%;
border: 2px solid orange;
cursor: pointer;
margin: 0 5px;
}
.slider .dots li.active {
background-color: brown;
}
js部分(move.js)
function move(element,attr,end, cb) { // 在此处添加第四个参数 是一个回调函数
// 计算运动量
// 整个距离的十分之一
// 整个距离 (终点-起点)
// 运动量 = (终点-起点) / 10
// 获取起点的值
// console.log(getComputedStyle(element))
// console.log(getComputedStyle(element).attr) // attr不是对象里面某一个具体key名称 是一个变量
// console.log(getComputedStyle(element)[attr]) // attr不是对象里面某一个具体key名称 是一个变量
let start = parseFloat(getComputedStyle(element)[attr])
// console.log(start)
let step = (end - start) / 10
// console.log(step)
let id = setInterval(()=>{
// 获取当前位置
let current = parseFloat(getComputedStyle(element)[attr])
// 判断是否到达终点
if(current === end){
// 此处动画执行结束了
clearInterval(id)
// 在动画结束的时候调用cb函数 调用cb其实就是调用传递的第四个实参
cb && cb()
}else{
// 在当前位置的基础上运动一个运动量
element.style[attr] = current + step + 'px'
}
}, 400)
}
动画版
html部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./index.css" />
<script src="./move.js"></script>
</head>
<body>
<div class="slider">
<!-- 图片部分 -->
<ul class="imgs">
<li>
<img src="./img/01.png" alt="" />
</li>
<li>
<img src="./img/02.png" alt="" />
</li>
<li>
<img src="./img/03.jpg" alt="" />
</li>
<li>
<img src="./img/04.png" alt="" />
</li>
<li>
<img src="./img/05.png" alt="" />
</li>
</ul>
<!-- 左右箭头 -->
<div class="arrow">
<a href="javascript:void 0;" class="left"><</a>
<a href="javascript:void 0;" class="right">></a>
</div>
<!-- 小点 -->
<ol class="dots">
<li class="active"></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
</div>
<script>
// 获取整个图片的长廊
var slider = document.querySelector("ul");
// 获取所有的图片
var imgs = document.querySelectorAll("ul>li");
var len = imgs.length;
console.log(len);
// 获取所有的点
var dots = document.querySelectorAll("ol>li");
// 获取一张图片的宽度
// console.log(imgs)
var width = imgs[0].offsetWidth;
/* 动态生成假的第一张和最后一张 */
var cloneFirst = imgs[0].cloneNode(true);
var cloneLast = imgs[len - 1].cloneNode(true);
console.log(cloneFirst);
console.log(cloneLast);
// 把第一张插入最后一张
slider.appendChild(cloneFirst);
// 把最后一张插入第一张
slider.insertBefore(cloneLast, imgs[0]);
// 默认显示第一张
slider.style.left = -width + "px";
// 重新设置ul的宽度 = 图片的张数 * 单张图片宽度
slider.style.width = width * (len + 2) + "px";
// console.log(width)
/* 获取左右箭头 绑定点击事件 */
var leftBtn = document.querySelector(".arrow .left");
var rightBtn = document.querySelector(".arrow .right");
// 用一个变量表示当前是第几张图片
var index = 1; // 前面还有一张假的
/*
点击的时候需要判断动画是否在运动的过程中
如果动画还没有结束 不允许它再开启新的定时器
只有动画结束之后 才允许开启新的定时器
*/
// 定义变量
var isMove = false; // 是否在动画过程中
/* 点击右箭头切换到下一张 */
rightBtn.addEventListener("click", function () {
if(isMove) return // 如果动画在运动过程中 不能往下走 不会再次开启一个新的动画
console.log('动画是否在运动', isMove)
index++;
isMove = true // 点击代表动画开启
// 右键就是让整个长廊向左移动一张图片的宽度
move(slider, "left", -width * index, function () {
// 到达最后一张切换到假的第一张 瞬间切换真的最后一张
if (index === len + 1) {
index = 1;
slider.style.left = -width * index + "px";
}
isMove = false // 动画结束
});
});
/* 点击左箭头切换到上一张 */
leftBtn.addEventListener("click", function () {
index--;
move(slider, "left", -width * index, function () {
// 动画结束之后才执行此处代码
// 判断如果由第一张切换到假的最后一张 瞬间拉到真的最后一张
if (index === 0) {
index = len;
slider.style.left = -width * index + "px";
}
});
});
</script>
</body>
</html>