1)预加载
一张图片的加载
let img=new Image();//构造函数
img.addEventListener("load",loadHandler);//事件侦听
img.src="./img/3-.jpg";//标签直接引入
document.body.appendChild(img);//dom操作创建子元素
console.log(img.offsetWidth);
load事件侦听回调函数
function loadHandler(e){
console.log(img.width);
}
创建多张图片:循环创建并添加侦听
for(let i=3;i<80;i++){
let img=new Image();
img.addEventListener("load",loadHandler);
img.src=`./img/${i}-.jpg`;
}
function loadHandler(e){
console.log(e.currentTarget.width,e.currentTarget.src);
}
工作流程:
1.首先加载第一张图片
2.侦听load事件,当完成加载,把图片放在数组中,并且打印图片宽度和地址
3.执行message函数,增加num,通知继续加载
4.收到通知就会继续加载内容,因为num已经改变了,所以加载最新的
//图片初始num
let num=3;
//空数组存放图片地址
let arr=[];
//侦听全局图片加载
document.addEventListener("startLoad",loadImage);
loadImage();
function loadImage(){
let img=new Image();
img.addEventListener("load",loadHandler);
img.src=`./img/${num}-.jpg`;
}
function loadHandler(e){
arr.push(e.currentTarget);
console.log(e.currentTarget.width,e.currentTarget.src);
message();
}
function message(){
num++;
if(num>79){
return;
}
let evt=new Event("startLoad");
document.dispatchEvent(evt);
}
当加载完成,改变num,然后重新调用loadImage
let num=3;
let arr=[];
loadImage();
function loadImage(){
let img=new Image();
img.addEventListener("load",loadHandler);
img.src=`./img/${num}-.jpg`;
}
function loadHandler(e){
arr.push(e.currentaTarget);
num++;
if(num>79){
//这是最后一次展现
arr.forEach(function(item){
console.log(item.width,item.src);
})
init(arr);
return;
}
e.currentTarget.src=`./img/${num}-.jpg`;
}
//把代码写在函数外,意味着刚一开始就会同步执行所有内容
//但是有时候我们需要在代码执行之前先执行某些内容,这些内容可以是异步的
//需要一定的时间处理,不能一开始就同步执行所有内容,因此我们会把这些代码
//放在初始执行函数中,一旦需要异步执行某些内容时,只需要执行完调用init就可以了
function init(){}
2)懒加载
<script>
let width;
let clientWidth;
// list是存储所有li的DOM元素数组
let list=[],
// 每个li里面所有内容的高度
heightList=[],
num=3
const COL=5;
const GAP=10;
init();
function init(){
document.body.style.margin="0px";
let ul=ce("ul",{
listStyle:"none",
margin:"0px",
padding:"0px"
});
clientWidth=document.documentElement.clientWidth;
width=(clientWidth-(COL-1)*GAP-1)/COL;
for(let i=0;i<COL;i++){
let li=ce("li",{
float:"left",
width+"px",
marginLeft:i===0 ? "0px" : GAP+"px",
// backgroundColor:"#"+Math.floor(Math.random()*0x1000000).toString(16).padStart(6,"0")
});
// 初始默认值是0
heightList.push(0);
ul.appendChild(li);
list.push(li);
}
document.body.appendChild(ul);
loadImage();
window.addEventListener("resize",resizeHandler);
document.addEventListener("scroll",scrollHandler);
}
function loadImage(){
let img=new Image();
img.addEventListener("load",loadHandler);
img.src=`./img/${num}-.jpg`;
}
function loadHandler(e){
this.removeEventListener("load",loadHandler);
let scale=this.height/this.width;
let height=scale*width;
this.style.width=width+"px";
this.style.height=height+"px";
this.scale=scale;
let min=Math.min.apply(null,heightList);
let index=heightList.indexOf(min);
list[index].appendChild(this);
heightList[index]+=height;
if(clientWidth!==document.documentElement.clientWidth){
clientWidth=document.documentElement.clientWidth;
resizeHandler();
}
// if(num>79){
// return;
// }
// console.log(document.body.scrollHeight>document.documentElement.clientHeight*2);
if(document.body.scrollHeight>document.documentElement.clientHeight*4){
return;
}
num++;
if(num>79) num=3;
loadImage();
}
function scrollHandler(e){
if(document.documentElement.scrollHeight-document.documentElement.scrollTop<document.documentElement.clientHeight*2){
num++;
if(num>79)num=3;
loadImage();
}
}
function ce(type,style){
let elem=document.createElement(type);
Object.assign(elem.style,style);
return elem;
}
function resizeHandler(e){
width=(document.documentElement.clientWidth-(COL-1)*GAP-1)/COL;
for(let i=0;i<list.length;i++){
list[i].style.width=width+"px";
for(let j=0;j<list[i].children.length;j++){
let img=list[i].children[j];
img.style.width=width+"px";
img.style.height=width*img.scale+"px";
}
}
}
</script>
//瀑布流
<script>
var width;
var clientWidth;
// list是存储所有li的DOM元素数组
var list = [],
// 每个li里面所有内容的高度
heightList = [],
num = 3
const COL = 5;
const GAP = 10;
init();
function init() {
document.body.style.margin = "0px";
document.documentElement.style.fontSize = "100px";
var ul = ce("ul", {
listStyle: "none",
margin: "0px",
padding: "0px"
});
clientWidth = screen.width;
width = (clientWidth - (COL - 1) * GAP - 1) / COL / 100;
for (var i = 0; i < COL; i++) {
var li = ce("li", {
float: "left",
width + "rem",
marginLeft: i === 0 ? "0px" : GAP / 100 + "rem",
// backgroundColor:"#"+Math.floor(Math.random()*0x1000000).toString(16).padStart(6,"0")
});
// 初始默认值是0
heightList.push(0);
ul.appendChild(li);
list.push(li);
}
document.body.appendChild(ul);
loadImage();
window.addEventListener("resize", resizeHandler);
}
function loadImage() {
var img = new Image();
img.addEventListener("load", loadHandler);
img.src = `./img/${num}-.jpg`;
}
function loadHandler(e) {
this.removeEventListener("load", loadHandler);
var scale = this.height / this.width;
var height = scale * width;
this.style.width = width + "rem";
this.style.height = height + "rem";
this.scale = scale;
var min = Math.min.apply(null, heightList);
var index = heightList.indexOf(min);
list[index].appendChild(this);
heightList[index] += height;
if (clientWidth !== document.documentElement.clientWidth) {
clientWidth = document.documentElement.clientWidth;
resizeHandler();
}
num++;
if (num > 79) {
return;
}
loadImage();
}
function ce(type, style) {
let elem = document.createElement(type);
Object.assign(elem.style, style);
return elem;
}
function resizeHandler(e) {
var scale = (document.documentElement.clientWidth - 1) / screen.availWidth;
document.documentElement.style.fontSize = scale * 100 + "px";
}
</script>
4)轮播图
//css 样式
<style>
.leftMove{
animation:leftMove 0.5s;
}
.rightMove{
animation:rightMove 0.5s;
}
@kryframes leftMove{
0%{
left:0
}
100%{
left:-1500px;
}
}
@keyframes rightMove{
0%{
left:-1500px;
}
100%{
left:0px;
}
}
</style>//JS 部分
<script>
// 1、布局
// 2、定义当前第几张图片和应该向什么方向运动
// 3、将需要放置的图片根据方向放在前面或者后面
// 4、将整个容器向左或者向右移动,
// 5、完成后将上一次的图片删除
// imgCon 放置所有图片的容器
// dotList 放置小圆点列表
// bnList 放置左右按钮的数组
// imgList 放置所有图片的数组
// pos是确定当前是第几个图片
// direction 图片运行的方向
// imgSrcList 存储轮播图图片地址的数组
var imgCon, ul, preDot;
var pos = 0,
x = 0,
bool = false,
autoBool = false,
dotList = [],
imgList = [],
bnList = [],
time = 300,
num = 0,
imgSrcList = ["./img/a.png", "./img/b.png", "./img/c.png", "./img/d.png", "./img/e.png"]
direction = "";
const WIDTH = 1500;
const HEIGHT = 500;
const SPEED = 40;
const LEFT = Symbol();
const RIGHT = Symbol();
/* loadImage();
function loadImage(){
var img=new Image();
img.addEventListener("load",loadHandler);
img.src=imgSrcList[0];
}
function loadHandler(e){
// this是当前加载的图片,将这个图片复制一个新的DOM图片
var m=this.cloneNode(false);
m.style.width=WIDTH+"px";
m.style.height=HEIGHT+"px";
imgList.push(m);
num++;
if(num>imgSrcList.length-1){
this.removeEventListener("load",loadHandler);
init();
return;
}
this.src=imgSrcList[num];
} */
Utils.loadImage(imgSrcList, init);
/*
init 初始化函数
1、 创建轮播图的外层容器
2、创建轮播图图片容器
3、创建按钮列表
4、创建小圆点列表
5、将轮播图容器放在body中
6、切换小圆点,因为当前是第0张,所以就会让第0个小圆点变红
7、将小圆点容器水平居中
8、设置时间间隔,每16毫秒执行animation函数一次,因为一秒是1000毫秒
一秒中执行60次,就是60帧频,每次花费的时间是16.6666毫秒
9、给外层的轮播图增加事件侦听,一个鼠标进入,一个是鼠标离开
*/
function init(list) {
imgList = list;
list.forEach(item => {
item.style.width = WIDTH + "px";
item.style.height = HEIGHT + "px";
})
var carousel = ce("div", {
WIDTH + "px",
height: HEIGHT + "px",
position: "relative",
margin: "auto",
overflow: "hidden"
});
createImgCon(carousel);
createButton(carousel);
createDotList(carousel)
document.body.appendChild(carousel);
changeDot();
ul.style.left = (WIDTH - ul.offsetWidth) / 2 + "px";
setInterval(animation, 16);
carousel.addEventListener("mouseenter", mouseHandler);
carousel.addEventListener("mouseleave", mouseHandler);
}
/*
轮播图进入和离开的事件函数
1、如果进入轮播图,设置自动轮播的开关是false,就不会自动轮播
并且重新将计时设为300
2、如果离开轮播图,设置自动轮播开关是true,就会自动轮播了
*/
function mouseHandler(e) {
if (e.type === "mouseenter") {
autoBool = false;
time = 300;
} else if (e.type === "mouseleave") {
autoBool = true;
}
}
/*
创建轮播图容器和图片
参数
parent 父容器, 元素类型 将创建好的容器和图片放在这个父容器内
1、创建图片容器,宽度是2张图片宽度
2、根据所有轮播图地址数组创建所有图片,并且放在数组imgList中
3、将第0张图片放在创建图片容器imgCon中
4、将图片容器放在轮播图容器中
*/
function createImgCon(parent) {
imgCon = ce("div", {
position: "absolute",
2 * WIDTH + "px",
height: HEIGHT + "px",
left: 0
});
imgCon.appendChild(imgList[0]);
parent.appendChild(imgCon);
}
/*
创建左右按钮
参数
parent 父容器, 元素类型 将创建好的按钮放在这个父容器内
1、创建按钮地址数组
2、循环这个地址数组,创建所有图片,并且放置对应的位置
3、设置图片的地址
4、将按钮图片放在轮播图容器中
5、给左右按钮增加点击事件执行函数bnClickHandler
*/
function createButton(parent) {
var arr = ["left", "right"];
for (var i = 0; i < arr.length; i++) {
var img = ce("img", {
position: "absolute",
// (外容器高度-当前图片高度)/2 垂直居中
top: (HEIGHT - 60) / 2 + "px",
// 如果是第0张图片,左边按钮,让他居左50像素,否则是none
left: i === 0 ? "50px" : "none",
// 如果是第1张图片,右边按钮,让他居右50像素,否则是none
right: i === 1 ? "50px" : "none"
});
img.src = `./img/${arr[i]}.png`;
parent.appendChild(img);
bnList.push(img);
img.addEventListener("click", bnClickHandler);
}
}
/*
创建小圆点
参数
parent 父容器, 元素类型 将创建好的小圆点放在这个父容器内
1、创建ul,设置样式
2、根据图片地址的数组,循环若干次,有多少图片就循环多少次创建小圆点
3、将每一个小圆点存在数组dotList中
4、将小圆点放在ul中
5、给ul增加点击事件,事件是点击小圆点,事件委托
*/
function createDotList(parent) {
var ulstyle = {
listStyle: "none",
margin: 0,
padding: 0,
position: "absolute",
bottom: "50px"
}
ul = ce("ul", ulstyle);
for (var i = 0; i < imgSrcList.length; i++) {
var dot = ce("li", {
"28px",
height: "28px",
borderRadius: "50%",
border: "2px solid #FF0000",
float: "left",
marginLeft: i === 0 ? "0px" : "15px"
});
dotList.push(dot);
ul.appendChild(dot);
}
// dotList=Array.from(ul.children);
parent.appendChild(ul);
ul.addEventListener("click", dotClickHandler);
}
/*
点击左右按钮事件函数
e 点击事件 MouseEvent
e.target 是被点击的按钮图片
如果bool是true,也就是当前轮播图正在播放时,点击按钮跳出,不继续执行
1、判断被点击图片的地址里面是否包含有left.png字符串,
如果有,就是点击左侧按钮,反之是右按钮
2、如果点击了左侧按钮,当前图片下标-1,如果小于0,
就让他为当前图片地址数量-1,也就是最大应该有图片下标
并且设置方向是向右运动
3、如果点击了右侧按钮,当前图片下标+1,如果大于前图片地址数量-1,
就让他为0,回到最开始第0张图片
并且设置方向是向左运动
*/
function bnClickHandler(e) {
if (bool) return;
if (e.target.src.includes("left.png")) {
pos--;
if (pos < 0) pos = imgSrcList.length - 1;
direction = RIGHT;
} else {
pos++;
if (pos > imgSrcList.length - 1) pos = 0;
direction = LEFT;
}
createNextImg();
}
/*
小圆点点击事件函数
e 鼠标事件对象 MouseEvent
e.target 是鼠标点击的目标
因为使用时事件委托,因此判断点击目标是不是li,如果不是就跳出
如果bool是true,也就是当前轮播图正在播放时,点击按钮跳出,不继续执行
1、判断点击目标是否是li,不是跳出
2、获取当前点击的小圆点是数组中第几个
3、如果当前的点击小圆点的下标和当前展示图片的下标相同时,跳出不处理
4、如果大于当前展示图片的下标,方向设为向左运动,反之向右
5、将当前点击的下标设为当前应展现图片的下标
*/
function dotClickHandler(e) {
if (bool) return;
// if(e.target.nodeName!=="LI") return;
if (e.target.constructor !== HTMLLIElement) return;
var index = dotList.indexOf(e.target);
if (index === pos) return;
direction = index > pos ? LEFT : RIGHT;
pos = index;
createNextImg();
}
/*
创建下一张需要显示图片
当点击左右按钮和当点击小圆点时,触发该函数
1、如果方向向左运动,给图片容器尾部添加新的图片
2、如果方向向右运动,给图片容器的最头部添加新图片,
但是这时候原图被挤到后面,我们将这个容器向左挪动一个宽度位置
3、设置完成后,设置bool是true,这时候就打开了动画的播放开关
动画就可以完成播放了
4、切换当前小圆点
*/
function createNextImg() {
// console.log(direction,pos,imgList[pos]);
if (direction === LEFT) {
imgCon.appendChild(imgList[pos]);
x = 0;
imgCon.className = "leftMove";
var ids = setTimeout(function () {
clearTimeout(ids);
imgCon.firstElementChild.remove();
imgCon.className = "";
imgCon.style.left = "0px";
}, 490)
} else if (direction === RIGHT) {
imgCon.insertBefore(imgList[pos], imgCon.firstElementChild);
imgCon.style.left = -WIDTH + "px";
x = -WIDTH;
imgCon.className = "rightMove";
var ids = setTimeout(function () {
imgCon.lastElementChild.remove();
imgCon.className = "";
imgCon.style.left = "0px";
clearTimeout(ids);
}, 490)
}
changeDot();
}
/*
切换小圆点
preDot是对上一次小圆点的引用变量
刚开始第一次时,没有这个引用,因此不执行,并且设置第一次设置了第0个小圆点
更改了第0个小圆点的背景色
第二次进来时,上次小圆点的引用时第0个小圆点,所以就将上次小圆点修改背景透明
将本次小圆点设置给这个引用,并且修改背景色
这样再次进入时就可以修改原来的,设置新的
*/
function changeDot() {
if (preDot) {
preDot.style.backgroundColor = "rgba(255,0,0,0)";
}
preDot = dotList[pos];
preDot.style.backgroundColor = "rgba(255,0,0,0.5)";
}
/*
创建元素
参数:
type 创建元素的类型 字符串
style 创建元素的样式 对象 使用对象方式给出所有该元素的样式
1、根据类型创建元素
2、将给入的样式设置给元素的行内样式
3、返回创建好的元素
*/
function ce(type, style) {
var elem = document.createElement(type);
// ES6的方法 将后面的对象中的属性复制到前面对象中
Object.assign(elem.style, style);
return elem;
}
/*
每16毫秒执行该函数一次
1、执行imgConMove这个函数,这是让图片移动方法
2、执行自动轮播
*/
function animation() {
// imgConMove();
autoPlay();
}
/*
每16毫秒让图片移动一次
开始的时候就一直运行,因为有一个bool值判断,如果是false时,一直就不能进入
如果可以进入
1、如果方向向左
不断让变量x递减,每16毫秒减40像素,设置图片容器位置,图片容器就可以移动了
当图片容器的第一张图完全移动到最左侧以后,也就是x小于等于负的图片宽度
设置bool是false,16毫秒后进入时直接跳出,并且删除掉移到最左侧的图片,
这时候后面的图片就会补充到最前面,因此我们设置将这x为0,让整个容器向后
挪回初始位置
2、如果方向向右
x不断增加40像素
如果x大于0,表示左侧的图片已经移到中间位置,原图片移到了右侧
这时候停止运动,bool设为false,x设为初始的0,删除右侧的图片
*/
function imgConMove() {
if (!bool) return;
if (direction === LEFT) {
x -= SPEED;
if (x <= -WIDTH) {
imgCon.firstElementChild.remove();
x = 0;
bool = false;
}
imgCon.style.left = x + "px";
} else if (direction === RIGHT) {
x += SPEED;
if (x >= 0) {
bool = false;
x = 0;
imgCon.lastElementChild.remove();
}
imgCon.style.left = x + "px";
}
}
/*
自动轮播
1、如果自动轮播开关是false时,跳出
2、time不断减少
3、如果time大于0就不继续执行,跳出
4、time值等于0,设置time初始为300
5、创建一个点击事件对象
6、向右侧按钮抛发这个点击事件
*/
function autoPlay() {
if (!autoBool) return;
time--;
if (time > 0) return;
time = 300;
var evt = new MouseEvent("click");
bnList[1].dispatchEvent(evt);
}
</script>