1, 数据 函数程序 都定义在 实例化对象中
通过调用 实例化对象的函数方法
调用 实例化对象中 存储的数据
执行程序 实现效果
2, this指向必须是实例化对象
匿名函数 --- 箭头函数
回调函数 通过 bind() 语法修改设定 this指向
提前定义一个变量 存储this指向
一个函数中 要是用 多个this指向
提前使用变量储存不同的this
index.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>
<style>
*{
margin: 0;
padding: 0;
}
ul,ol,li{
list-style: none;
}
a,a:hover,a:active{
text-decoration: none;
}
img{
width: 100%;
height: 100%;
display: block;
}
.banner{
width: 600px;
height: 400px;
border: 5px solid #000;
position: relative;
margin: 50px auto;
/* overflow: hidden; */
}
.banner>ul{
width: 500%;
height: 100%;
position: absolute;
top:0;
left:0;
}
.banner>ul>li{
float: left;
width: 600px;
height: 400px;
}
.banner>ol{
height: 50px;
position: absolute;
bottom:50px;
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
border-radius: 15px;
}
.banner>ol>li{
width: 20px;
height:20px;
border-radius: 50%;
background: #fff;
margin: 0 15px;
cursor: pointer;
}
.banner>ol>li.active{
background: red;
}
.banner>div{
width: 100%;
height: 50px;
position: absolute;
left:0;
top: 50%;
transform: translateY(-50%);
display: flex;
justify-content: space-between;
align-items: center;
}
.banner>div>a{
display: flex;
justify-content: center;
align-items: center;
width: 50px;
height: 50px;
font-size: 40px;
color: #fff;
}
</style>
</head>
<body>
<!-- 轮播图div -->
<div class="banner">
<!-- 轮播图内容 -->
<ul></ul>
<!-- 焦点按钮 -->
<ol></ol>
<!-- 左右切换按钮 -->
<div>
<a href="JavaScript:;" name="left"><</a>
<a href="JavaScript:;" name="right">></a>
</div>
</div>
<!-- 导入外部文件加载move运动函数 -->
<script src="./move.js"></script>
<!-- 导入外部js文件中的构造函数 -->
<script src="./banner.js"></script>
<script>
// 面向对象轮播图
// 加载外部js文件导入其中定义的构造函数
// 定义数组
const bannerArr = [
{id:1 , width:500 , height:333 , size:29.7 , name:'1.jpg'},
{id:2 , width:500 , height:333 , size:19.3 , name:'2.jpg'},
{id:3 , width:500 , height:333 , size:17.1 , name:'3.jpg'},
{id:4 , width:500 , height:333 , size:20.3 , name:'4.jpg'},
{id:5 , width:600 , height:400 , size:320 , name:'5.jpg'},
];
// 获取 整个父级标签对象 .banner
const oDivBanner = document.querySelector('.banner');
// 调用执行 构造函数 创建实例化对象
// 参数1: 标签对象名称
// 参数2: 需要的数组数据
const bannerObj = new CreateBanner( oDivBanner , bannerArr);
console.log( bannerObj );
// 只需要调用一个入口函数就可以了
bannerObj.init();
</script>
</body>
</html>
banner.js
class CreateBanner{
// 构造器
constructor(element , imgArr){
// 定义属性 存储 参数数据
this.ele = element;
this.arr = imgArr;
// 属性存储数据
this.ul = element.querySelector('ul');
this.ol = element.querySelector('ol');
// 原始数组单元个数
this.length = imgArr.length ;
// 定义变量 存储显示li的索引下标
// 初始值 是 1
this.index = 1;
// 定义变量 存储数据防止点击过快
this.bool = true;
// 只定义不赋值
this.ulLis ;
this.olLis ;
this.liWidth ;
this.time ;
}
// 定义一个入口函数
// 调用这个函数 函数中调用执行所有需要运行的函数
// 入口函数一般叫 init
init(){
this.setPage();
this.autoLoop();
this.setMouse();
this.setClick();
this.setHide();
}
// 定义函数方法
// 生成页面
setPage(){
// 定义字符串
let ulStr = '' ;
let olStr = '' ;
// 循环遍历数组
// item 是 数组中存储的数据对象
this.arr.forEach( (item , key)=>{
// 生成 ul>li 标签内容
ulStr += `<li><img src="./images/${item.name}"></li>`;
olStr += key === 0 ? `<li class="active" name="focus" num="${key}"></li>` : `<li name="focus" num="${key}"></li>`;
});
// 写入页面
this.ul.innerHTML = ulStr;
this.ol.innerHTML = olStr;
// 获取 所有的 ul>li ol>li
// 给已经定义的属性赋值
this.ulLis = this.ul.querySelectorAll('li');
this.olLis = this.ol.querySelectorAll('li');
// 获取li标签宽度赋值给已经定义的属性
this.liWidth = parseInt( window.getComputedStyle(this.ulLis[0]).width ) ;
// 克隆 ul>li 的第一个和最后一个
const cloneFirst = this.ulLis[0].cloneNode(true);
const cloneLast = this.ulLis[this.ulLis.length-1].cloneNode(true);
// 克隆第一个写入 ul最后
// 克隆最后一个写入 ul第一个
this.ul.appendChild( cloneFirst );
this.ul.insertBefore( cloneLast , this.ulLis[0] );
// 设定ul标签宽度
// 原始数组单元个数+2 乘以 一个li宽度
this.ul.style.width = ( this.length + 2 ) * this.liWidth + 'px';
// 将 ul 向左定位一个li宽度
this.ul.style.left = -this.liWidth + 'px';
}
// 自动轮播
autoLoop(){
// 定义定时器
this.time = setInterval( ()=>{
// 变量累加1
this.index++;
// 在运动之前 先切换 焦点按钮 css样式
this.focusStyle();
// 调用move运动函数,改变ul定位
// 每次定位的数据是 显示li索引下标*一个li宽度
// 回调函数的this执行也会改变 一般是 undefined 或者 window
// 使用 bind方法 修改this指向 为 当前this中存储的指向 也就是 实例化对象
move( this.ul , {left:-this.index*this.liWidth} , this.loopEnd.bind(this) );
} , 4000 )
}
// 运动停止的回调函数
loopEnd(){
// 判断index的数值
// 如果 index的数值是 所有li的最后一个
// index 赋值 1
// 运动结束 从最后一个li 瞬间定位到 第二个li
if( this.index === this.length+1 ){
this.index = 1 ;
this.ul.style.left = -this.index*this.liWidth + 'px';
// index 是 0 瞬间定位到 倒数第二个li
}else if( this.index === 0 ){
this.index = this.length ;
this.ul.style.left = -this.index*this.liWidth + 'px';
}
// 当所有的运动结束
// 给 bool变量赋值 true 可以再次触发move()运动函数
this.bool = true ;
}
// 焦点样式函数
focusStyle(){
// 提前定义一个变量 存储this指向
const _this = this ;
// 给所有的ol>li清除class,active
this.olLis.forEach(function(item,key){
// item 是 每一个ol>li标签
item.classList.remove('active');
// 如果 key索引下标 和 index-1 相同 添加class,active
// 使用 _this中 存储的 指向 作为 调用数据
if( key === _this.index-1 ){
item.classList.add('active');
}
})
// 特殊情况:
// 最后一个ul>li 给 第一个ol>li添加样式
// 第一个ul>li 给 最后一个 ol>li添加样式
if( this.index === this.length+1 ){
this.olLis[0].classList.add('active');
}else if( this.index === 0 ){
this.olLis[this.olLis.length-1].classList.add('active');
}
}
// 鼠标移入移出
setMouse(){
// 移入父级div标签 清除 定时器
// 移出父级div标签 调用 自动轮播函数
this.ele.addEventListener('mouseenter' , ()=>{
clearInterval( this.time );
})
this.ele.addEventListener('mouseleave' , ()=>{
this.autoLoop();
})
}
// 点击效果
setClick(){
// 给父级div添加点击事件 通过事件委托添加点击效果
this.ele.addEventListener('click' , e=>{
if( e.target.getAttribute('name') === 'left' ){
// 防止点击过快
if( this.bool ){
this.bool = false;
}else{
return ;
}
// 索引下标--
this.index--;
// 设定焦点样式
this.focusStyle();
// 通过move运动函数切换显示li
move( this.ul , { left : -this.index * this.liWidth } , this.loopEnd.bind(this) );
}else if( e.target.getAttribute('name') === 'right' ){
// 防止点击过快
if( this.bool ){
this.bool = false;
}else{
return ;
}
// 索引下标++
this.index++;
// 设定焦点样式
this.focusStyle();
// 通过move运动函数切换显示li
move( this.ul , { left : -this.index * this.liWidth } , this.loopEnd.bind(this) );
}else if( e.target.getAttribute('name') === 'focus' ){
// 防止点击过快
if( this.bool ){
this.bool = false;
}else{
return ;
}
// 索引下标赋值 点击标签索引下标+1
this.index = e.target.getAttribute('num')-0 + 1 ;
// 设定焦点样式
this.focusStyle();
// 通过move运动函数切换显示li
move( this.ul , { left : -this.index * this.liWidth } , this.loopEnd.bind(this) );
}
})
}
// 浏览器最小化
setHide(){
document.addEventListener('visibilitychange' , ()=>{
if( document.visibilityState === 'hidden' ){
// 清除定时器,终止轮播图自动运行
clearInterval(this.time);
}else if( document.visibilityState === 'visible' ){
// 再次调用自动轮播函数
this.autoLoop();
}
})
}
}
move.js
// 参数1: 运动的标签对象
// 参数2: 对象形式 属性是要运动的css属性 属性值是要运动的css样式的最终值
// 参数3: 存储要执行的函数程序 默认值是空函数
function move(element, object, callback = function () { }) {
// 定义一个变量 存储 参数2 对象中 单元个数
let num = 0;
// 使用 for...in 循环遍历 参数2对象
// 定义的变量 存储对象的键名 也就是 left,top.width,height,opacity...
// 对象[变量] 获取对象键名存储的键值 也就是 最终值 500 300 0.3....
// 当前变量必须要使用 let 关键词来定义
for (let type in object) {
// 每次循环 给 变量累加1 表示对象参数有一个单元
num++;
// 之前的type参数是 现在 for..in循环 type变量
// 之前的最终值是 现在 对象[变量] 获取的数据
// 获取运动属性的初始值
// 如果是 透明度 直接获取结果 * 100
// 不是 透明度 结果 parseInt() 取整
let startVal = type === 'opacity' ? window.getComputedStyle(element)[type] * 100 : parseInt(window.getComputedStyle(element)[type]);
// 如果 是 透明度 最终值 * 100
// 如果 不是 透明度 最终值就是本身
// 之前的最终值 是 当前 对象参数中,属性存储的属性值
let endVal = type === 'opacity' ? object[type] * 100 : object[type];
// 设定定时器
let time = setInterval(function () {
// 计算步长
let step = (endVal - startVal) / 10;
// 步长取整
step = step > 0 ? Math.ceil(step) : Math.floor(step);
// 初始值累加步长值
startVal += step;
// 新的初始值 赋值给标签对象的css
// 如果是 透明度 赋值 当前累加之后的初始值 除以 100
// 如果不是 透明度 赋值 当前累加之后的初始值 拼接 px单位
element.style[type] = type === 'opacity' ? startVal / 100 : startVal + 'px';
// 判断 如果初始值 等于 最终值
if (startVal === endVal) {
// 清除定时器
clearInterval(time);
// 给 变量 --
num--;
// 当num数值是 0 时
// 表示所有执行的定时器都被清除了
// 也就是所有css运动都执行结束了
if (num === 0) {
// 执行回调函数
callback();
}
}
}, 30)
}
}
注意:
- 将 匿名函数 function(){}/forEach/定时器/事件绑定 修改为 箭头函数 ()=>{}
- 回调函数 使用 bind方法 设定this指向
this.loopEnd.bind(this)