一、静态版本(ES5写法)
OOA:轮播图 点击按钮切换图片
0.布局 可视区(r定位)>图片盒子(a定位)+按钮(a定位)
0-1.设置可视区box 溢出隐藏
0-2.设置放图片的大盒子imgbox,让图片浮动一行显示 (width宽度和left位置由js自动设定)
0-3.把左右按钮定位到box上
0-4.为了实现无缝轮播图,复制第一张图到最后,利用视觉欺骗效果,改变索引和位置实现
1.获取元素 设置默认索引 补全布局:图片大容器宽和位置
2.绑定点击事件
3.改变左右索引
3-1.左按钮索引--,索引到第一个:强行跳转到倒数第二张,设置初始位置为倒数第一张
3-2.右按钮索引++,索引到最后一个:强行跳转到第二张,设置位置为初始位置为0
4.改变imgbox的位置
(1)HTML部分
<!-- 可视区 -->
<div class="box">
<!-- 图片盒子 -->
<div class="imgbox">
<img src="imgs/banner1.jpg" alt="">
<img src="imgs/banner2.jpg" alt="">
<img src="imgs/banner3.jpg" alt="">
<img src="imgs/banner4.jpg" alt="">
<img src="imgs/banner5.jpg" alt="">
<img src="imgs/banner6.jpg" alt="">
<img src="imgs/banner7.jpg" alt="">
<!-- 把第一张复制到最后 利用视觉欺骗 -->
<img src="imgs/banner1.jpg" alt="">
</div>
<div class="btns">
<input type="button" id="left" value="<">
<input type="button" id="right" value=">">
</div>
</div>
(2)CSS部分
/* 可视区 */
.box {
position: relative;
width: 1000px;
height: 300px;
margin: 0 auto;
overflow: hidden;
}
/* 图片盒子 width宽度和left位置js设置 */
.imgbox {
position: absolute;
}
.imgbox img {
width: 1000px;
height: 300px;
float: left;
}
.btns input {
position: absolute;
width: 40px;
height: 40px;
background: rgba(255, 255, 255, .5);
font-size: 25px;
border: none;
top: 130px;
}
.btns input:active {
background: rgba(97, 147, 255, 0.39);
color: #fff;
}
#left {
left: 0;
}
#right {
right: 0;
}
(3)JS部分
<!-- 引入缓冲动画move -->
<script src="../move.js"></script>
<script>
function Banner(index) {
// 1.获取元素 设置初始值
this.left = document.getElementById("left");
this.right = document.getElementById("right");
this.imgbox = document.querySelector(".imgbox");
this.imgs = this.imgbox.children;
// 传参 可以改变默认显示哪一张图
this.index = index;
// 补全布局
// 设置宽(有px) imgbox的宽=imgs数组长度(几个)*一个图的宽
this.imgbox.style.width = this.imgs.length * this.imgs[0].offsetWidth + "px";
// 设置默认第几张图显示 即imgbox往左移动的距离 负的
this.imgbox.style.left = -this.index * this.imgs[0].offsetWidth + "px";
// 执行2.执行绑定事件
this.addEvent();
}
Banner.prototype = {
// 内置属性 补上 指向自己
constructor: Banner,
// 2.绑定事件
addEvent: function() {
const that = this;
// 左点击绑定
this.left.onclick = function() {
// 注意:新函数内this的指向问题
// 执行3左索引
that.changeIndexLeft();
}
// 右点击绑定
this.right.onclick = function() {
// 执行3右索引
that.changeIndexRight();
}
},
// 3.获取索引
// 左
changeIndexLeft: function() {
//假如到了第1张(index0),跳转到倒数第二张(length-2)
if (this.index === 0) {
this.index = this.imgs.length - 2;
// 要站在最后一张图的位置(第1张的复制品)(length-1)
this.imgbox.style.left = -(this.index + 1) * this.imgs[0].offsetWidth + "px";
} else {
this.index--;
}
// 执行5.改变imgbox的位置
this.changePos();
},
// 右
changeIndexRight: function() {
// 为了实现无缝轮播,到最后一张图(第1张的复制品)的时候,直接跳到第二张图(index1);为了避免反向轮播,这张图的定位要在0(站在第0位)
if (this.index === this.imgs.length - 1) {
// 跳到第二张图(index1)
this.index = 1;
// 站在第一张图的位置(index0)
this.imgbox.style.left = 0;
} else {
this.index++;
}
// 执行5.改变imgbox的位置
this.changePos();
},
// 4.改变位置
changePos: function() {
// 位置:imgbox往左移动的距离 负的
// 第一张0 * -1000:0
// 第二张1 * -1000:-1000
// 第三张2 * -1000:-2000
// 第四张3 * -1000:-3000
// this.imgbox.style.left=-this.index*this.imgs[0].offsetWidth+"px";
// 写成缓冲样式 (这里不用写px)
move(this.imgbox, {
left: -this.index * this.imgs[0].offsetWidth
})
}
}
new Banner(0);
</script>
二、渲染版本(ES6写法)
(1)HTML部分
<div class="box">
<div class="imgbox">
<img src="" alt="" title="">
</div>
<div class="btns">
<input type="button" id="left" value="左">
<input type="button" id="right" value="右">
</div>
</div>
(2)CSS部分
.box {
width: 1000px;
height: 300px;
margin: 0 auto;
position: relative;
}
.imgbox {
position: absolute;
}
.imgbox img {
width: 1000px;
height: 300px;
float: left
}
.btns input {
position: absolute;
width: 40px;
height: 40px;
border: none;
background: rgba(200, 200, 200, 0.5);
top: 130px;
}
.btns input:active {
background: #55f;
color: #fff
}
#left {
left: 0
}
#right {
right: 0
}
(3)JS部分
<!-- 引入ajax文件 -->
<script src="./ajax.js"></script>
<!-- 引入move -->
<script src="./move.js"></script>
<script>
class Banner {
constructor() {
this.imgbox = document.querySelector(".imgbox");
this.url = "http://localhost:3000/api"; //走数据请求
this.index = 0;
this.left = document.getElementById("left");
this.right = document.getElementById("right");
this.getData();
this.addEvent();
}
// 3.绑定事件
addEvent() {
const that = this;
this.left.onclick = function() {
that.changeIndex(0);
}
this.right.onclick = function() {
that.changeIndex(1);
}
}
// 4.改变索引
changeIndex(d) { //d表状态 0左1右
if (d === 0) {
if (this.index === 0) { //第0位 索引倒数二 位置倒数1
this.index = this.imgbox.children.length - 2;
this.imgbox.style.left = -(this.imgbox.children.length - 1) * this.imgbox.children[0].offsetWidth + "px";
} else {
this.index--;
}
} else {
if (this.index === this.imgbox.children.length - 1) { //最后位 索引1 位置0
this.index = 1;
this.imgbox.style.left = 0;
} else {
this.index++;
}
}
this.move();
}
// 6.运动
move() {
move(this.imgbox, {
left: -this.index * this.imgbox.children[0].offsetWidth
})
}
// 1.获取数据
getData() {
ajax({
url: this.url,
data: {
type: "getBanner"
},
success: res => {
// console.log(res); 请求后端数据先看是不是json 再解析
res = JSON.parse(res);
if (res.code === 1) {
// code为1表示成功,渲染页面
// 把res.data绑到this上,给rander使用, 不用传参减少耦合
this.data = res.data;
// 渲染页面
this.render();
}
}
})
}
// 2.渲染页面
render() {
// console.log(this.data);
// 2-1.根据数据,拼接页面结构
let str = "";
for (let i = 0; i < this.data.length; i++) { //把所有图片拼接(按banner.json内格式写)
str += `<img src="${this.data[i].src}" alt="${this.data[i].alt}" title="${this.data[i].title}">`
}
// 2-4.为了无缝轮播,添加第一张到最后
const first = this.data[0];
str += `<img src="${first.src}" alt="${first.alt}" title="${first.title}">`
// 2-2.将拼接好的页面结构设置到指定容器(banner图的大容器)
this.imgbox.innerHTML = str;
// 2-3.完善布局 设置imgbox宽度(imgbox子元素个数*任意个宽度)
this.imgbox.style.width = this.imgbox.children.length * this.imgbox.children[0].offsetWidth + "px";
}
}
new Banner();
</script>
(4)服务器
const http = require("http");
const fs = require("fs");
const qs = require("querystring");
const url = require("url");
http.createServer((req, res) => {
const urlObj = url.parse(req.url);
// 服务器的根路由
if (urlObj.pathname.includes("/api")) {
dataHandle(req, res);
} else {
staticHandle(req, res);
}
}).listen(3000, () => {
console.log("服务器开启成功: http://localhost:3000");
})
// 数据请求
function dataHandle(req, res) {
console.log(req.method);
if (req.method === "GET") {
const obj = url.parse(req.url, true).query;
programHandle(req, res, obj);
} else if (req.method === "POST") {
let str = "";
req.on("data", (d) => {
str += d;
})
req.on("end", () => {
const obj = qs.parse(str);
programHandle(req, res, obj);
})
}
}
// 数据请求后功能处理
function programHandle(req, res, data) {
// 功能路由:接收到数据之后,根据数据中携带的功能类型,决定执行那个具体的功能
// data格式 {type:"getGoods",xx:xx,x:xxx}
switch (data.type) {
case "getBanner":
getBanner(req, res, data);
break;
}
}
// 获取banner数据
function getBanner(req, res, data) {
// 服务器先读取数据库banner.json的数据
// utf-8是把后端获取的buffer数据转成字符编码
fs.readFile("./database/banner.json", "utf-8", (err, data) => { //data就是banner.json
const obj = {};
if (err) {
obj.code = 0;
obj.title = "数据查找失败";
obj.data = []; //空数组 没读到数据
} else {
obj.code = 1;
obj.title = "数据查找成功";
obj.data = JSON.parse(data); //data是json 转字符保存下来,[{},{}..]
}
res.write(JSON.stringify(obj)); //obj转json传输给前端
res.end();
})
}
// 后端返回给前端的通用数据格式
// 失败:
// {
// code:0, //状态0 失败
// title:"数据请求失败了",
// data:"not data"
// }
// 成功:
// {
// code:1,
// title:"恭喜,数据请求成功了",
// data:json数据
// }
// 静态资源
function staticHandle(req, res) {
const urlObj = url.parse(req.url);
fs.readFile("./www" + urlObj.pathname, (err, data) => {
if (err) {
res.write("Forbidden \n You don't have permission to access this resource.");
} else {
res.write(data);
}
res.end();
})
}
(5)json文件
[{
"title": "1",
"alt": "1",
"src": "./imgs/banner1.jpg"
}, {
"title": "2",
"alt": "2",
"src": "./imgs/banner2.jpg"
}, {
"title": "3",
"alt": "3",
"src": "./imgs/banner3.jpg"
}, {
"title": "4",
"alt": "4",
"src": "./imgs/banner4.jpg"
}, {
"title": "5",
"alt": "5",
"src": "./imgs/banner5.jpg"
}, {
"title": "6",
"alt": "6",
"src": "./imgs/banner6.jpg"
}]
三、jq版本-轮播图插件(抽屉式实现无缝轮播)
(1)HTML部分
<div class="banner1">
<!-- <div class="imgbox">
<img src="" alt="1">
<img src="" alt="2">
<img src="" alt="3">
<img src="" alt="4">
</div>
<div class="btns">
<input type="button" value="<" id="left">
<input type="button" value=">" id="right">
</div>
<div class="list">
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
</div> -->
</div>
<div class="banner2"></div>
(2)CSS部分
.banner1 {
width: 1000px;
height: 300px;
margin: 0 auto;
position: relative;
/* overflow: hidden; */
}
.banner1 .imgbox {
width: 1000px;
height: 300px;
}
.banner1 .imgbox img {
width: 1000px;
height: 300px;
position: absolute;
left: 1000px;
top: 0;
}
/* 第一个放在可视区,其余叠放在不可视区 */
.banner1 .imgbox img:first-child {
left: 0;
}
.banner1 .btns * {
position: absolute;
top: 130px;
width: 40px;
height: 40px;
border: none;
background: rgba(200, 200, 200, 0.6);
}
.banner1 #left {
left: 0
}
.banner1 #right {
right: 0
}
.banner1 .list {
display: flex;
background: rgba(200, 200, 200, 0.6);
height: 30px;
line-height: 30px;
text-align: center;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
.banner1 .list span {
flex: 1;
border-left: solid 1px #fff;
border-right: solid 1px #fff;
cursor: pointer
}
.banner1 .list span.active {
background: red;
color: #fff;
}
/* 第二套 */
.banner2 {
width: 500px;
height: 150px;
margin: 0 auto;
position: relative;
/* overflow: hidden; */
}
.banner2 .imgbox {
width: 500px;
height: 150px;
}
.banner2 .imgbox img {
width: 500px;
height: 150px;
position: absolute;
left: 500px;
top: 0;
}
.banner2 .imgbox img:first-child {
left: 0;
}
.banner2 .btns * {
position: absolute;
top: 60px;
width: 30px;
height: 30px;
border: none;
background: rgba(200, 200, 200, 0.6);
}
.banner2 #left {
left: 0
}
.banner2 #right {
right: 0
}
.banner2 .list {
display: flex;
background: rgba(200, 200, 200, 0.6);
height: 30px;
line-height: 30px;
text-align: center;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
.banner2 .list span {
background: rgba(200, 200, 200, 0.6);
border-left: solid 1px #fff;
border-right: solid 1px #fff;
cursor: pointer;
width: 20px;
height: 20px;
border-radius: 50%;
justify-content: center;
}
.banner2 .list span.active {
background: red;
color: #fff;
}
(3)JS部分
<script src="./jquery.js"></script>
<script src="./jquery.banner.js"></script>
<script>
const imgs = [{
"title": "1",
"alt": "1",
"src": "./imgs/banner1.jpg"
}, {
"title": "2",
"alt": "2",
"src": "./imgs/banner2.jpg"
}, {
"title": "3",
"alt": "3",
"src": "./imgs/banner3.jpg"
}, {
"title": "4",
"alt": "4",
"src": "./imgs/banner4.jpg"
}, {
"title": "5",
"alt": "5",
"src": "./imgs/banner5.jpg"
}, {
"title": "6",
"alt": "6",
"src": "./imgs/banner6.jpg"
}, {
"title": "7",
"alt": "7",
"src": "./imgs/banner7.jpg"
}]
$(".banner1").banner(imgs, {
// 按钮
btn: false,
// 分页器
list: true,
// 自动播放
autoPlay: true,
index: 0,
// 两张间隔时间
delayTime: 2000,
// 移动时间
moveTime: 200
});
$(".banner2").banner(imgs, {
// 按钮
btn: true,
// 分页器
list: true,
// 自动播放
autoPlay: true,
index: 4,
// 两张间隔时间
delayTime: 1000,
// 移动时间
moveTime: 150
});
</script>
(4)JS部分-功能部分
// 匿名函数前后加分号,避免影响其他没加分号的代码
;
(function($) { //jquery私有化:$就是jQuery
"use strict";
// console.log($);
// 不需要操作dom 全局
// $.xxx = function() {
// console.log("自定义方法1");
// }
// $.extend({
// xxx: function() {
// console.log("自定义方法2");
// }
// })
// 需要操作dom 局部
// $.fn.xxx = function() {
// console.log("自定义方法3");
// }
// $.fn.extend($.fn, {
// xxx: function() {
// console.log("自定义方法4");
// }
// })
// $.extend({
// xxx: function() {
// console.log("自定义方法5");
// }
// })
$.fn.banner = function(data, ops = {}) {
// 1.处理参数
let { btn = true, list = true, autoPlay = true, delayTime = 3000, moveTime = 300, index = 0, currentClass = "active" } = ops;
// 出去img的索引 进入为index
let prev;
// 封装成函数 this要在外面拿到
const that = this;
// 2.根据数据,渲染页面结构
init();
function init() {
// 遍历数据 图片结构拼接
let str = "";
$.each(data, (idx, val) => {
// console.log(val);
str += ` <img src="${val.src}" alt="${val.alt}" title="${val.title}">`;
})
// $(".banner1").banner(imgs,{}) 执行对象-> this获取-> $(".banner1")
// 把拼接的图片数据img放入imgbox,再插入banner1中
$("<div class='imgbox'>" + str + "</div>").appendTo(that);
}
// 3.左右按钮 有btn就是true 要按钮,没有就是不要
if (btn) {
// 左按钮插入btns(btns在banner1) 回到btns添加右按钮
// 给btns的子元素左绑定事件 返回btns 绑定右事件
$("<input type='button' id='left' value='<'>").appendTo($("<div class='btns'></div>").appendTo(this)).parent().append($("<input type='button' id='right' value='>'>")).children("#left").click(leftClick).end().children("#right").click(rightClick)
// 左过程
// console.log(prev, index);
// 左 反方向 正负号改变
// $(".imgbox").children().eq(prev).css({ left: 0 }).stop().animate({
// left: $(".imgbox").children().eq(0).width()
// }).end().eq(index).css({
// left: -$(".imgbox").children().eq(0).width()
// }).stop().animate({
// left: 0
// })
// 右过程
// console.log(prev, index);
// imgbox的子元素
// 第prev往左出去 负一张图宽度 安全起见,出去也设置初始位置
// 第index往左进入 添加动画到可视区(left0)
// 进入之前将图片设置在可视区右边(避免第二轮错误,第一轮图片都出到左边去了)
// $(".imgbox").children().eq(prev).css({ left: 0 }).stop().animate({
// left: -$(".imgbox").children().eq(0).width()
// }).end().eq(index).css({
// left: $(".imgbox").children().eq(0).width()
// }).stop().animate({
// left: 0
// })
}
// 左点击事件
function leftClick() {
if (index === 0) {
index = data.length - 1;
prev = 0
} else {
index--;
prev = index + 1;
}
Move(prev, index, -1); //通过负正确定左右
}
// 右按钮的点击事件函数
function rightClick() {
// console.log("右");
if (index === data.length - 1) { //最后一张,index为0
index = 0;
prev = data.length - 1
} else {
index++;
prev = index - 1;
}
Move(prev, index, 1);
}
// 4.分页器切换轮播图
if (list) {
// 和图片创建差不多
let str = "";
$.each(data, idx => {
// console.log(val);
str += `<span>${idx}</span>`;
})
// $(".banner1").banner(imgs,{}) 执行对象-> this获取-> $(".banner1")
// 放入list中,再插入banner1中,选择子对象 当前索引添加选中样式(currentClass传参 默认active)
// 找所有span绑定点击事件
$("<div class='list'>" + str + "</div>").appendTo(this).children("span").eq(index).addClass(currentClass).end().click(function() {
// console.log(this); //jq的this指向被点击的元素
// console.log($(this).index()); //点击事件中 jq的index方法(是原生this)
//目前的index,点击的是$(this).index()
// 目前走,点击进,这次点的是下一次的上一次,执行完功能后,把索引设置为当前点击的索引
// console.log(index, $(this).index());
// 点击大于上一个索引 往左
if ($(this).index() > index) {
Move(index, $(this).index(), 1);
}
if ($(this).index() < index) {
Move(index, $(this).index(), -1);
}
// 执行完功能后,把索引设置为当前点击的索引
index = $(this).index();
})
}
// 3.封装的运动 (左右按钮 分页)
// 不要直接写$(".imgbox")创建多个会有问题 从顶层开始找imgbox
function Move(prev, next, d) { //上索引 下索引 正负
that.children(".imgbox").children().eq(prev).css({ left: 0 }).stop().animate({
left: -that.children(".imgbox").children().eq(0).width() * d
}, moveTime).end().eq(next).css({
left: that.children(".imgbox").children().eq(0).width() * d
}).stop().animate({
left: 0
}, moveTime)
// 4.处理分页器的选择状态
// list子元素 被选中的索引加样式 兄弟元素移出样式
that.children(".list").children("span").eq(next).addClass(currentClass).siblings().removeClass(currentClass);
}
// 5.自动播放
if (autoPlay) {
let t = setInterval(() => {
// trigger模拟事件执行$("#right").trigger("click"); 但没有按钮就无法执行 直接拿到点击的回调函数
rightClick();
}, delayTime);
// 鼠标移入banner 自动播放停止 this指向banner
this.hover(function() {
clearInterval(t);
}, function() {
t = setInterval(() => {
rightClick();
}, delayTime);
})
}
}
})(jQuery);