前端-JS学习笔记-王者荣耀实战js补充

图片轮播实现,按钮点播实现

1. 互斥代码抽取

1
2
3
4
5
6
7
8
9
10
11
// 导航栏互斥-只能有一个元素有active类
function setActive(navs, len, num) {
for (let i = 0; i < len; i++) {
if(i === num){
navs[i].classList.add("active");
}
else {
navs[i].classList.remove("active");
}
}
}

2. 图片轮播

2.1. 思路

  • 图片轮播实际是

    • 图片列表中所有图片水平排布,每个图片宽度都是100%
    • 而可视区宽度只有100%,除了第一张图片,其余图片只能被隐藏
    • 图片列表每向左移动100% translateX(-100%),可视区显示的图片就变下一张
    • 再加上移动过渡,实现图片轮播
  • ⚠️但是,等到最后一张图片,图片列表应该移动回第一张图片 translateX(0)

    • 这里还是用移动过渡的话,从最后一张图片移动到第一张图片的过渡像是视频的倒带
    • 为了显示的连贯,复制第一张图片放到最后一张图片后
    • 移动到复制的第一张图片后,无动画的移动回到真正的第一张,再进行图片列表下一轮的前移
  • 设置静止

    • 当鼠标在图片列表上时,定时器关闭,不再进行图片的轮播
    • 鼠标离开图片列表,定时器重启

2.2. html骨架原型

王者荣耀CSS实现main-top.left部分

2.3. 轮播

  • count变量全程控制列表的移动

  • 为什么移动到复制的最后一张图片后要定时500ms后移动到真正的第一张,而不是立即移动?

    • 是使用定时器每2s执行一次move函数
    • 等到photos移动至-500%时,说明到达复制的第一张图片,此时视觉上完成了“最后一张到第一张的移动”
    • 如果立即执行移动至translateX(0),那可视区显示的还是第一张图片,要再等2s,移动至第二张图片。视觉上就是“第一张图片”出现了4s,重复显示,而不是连贯的轮播
    • 将第一张的移动加到定时器中,就变为在等待移动到第二张的2s中,使用了0.5s先无过渡移动到第一张,1.5s后移动过渡到第二张,视觉上就是“最后一张 - 第一张 - 第二张”
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
// main-top 部分 图片列表
const photos = document.querySelector(".main-top .photos");
// 图片数组长度
const len = photos.children.length;

// 图片索引 代表当前是第几张图片 0代表第一张图片
let count = 0;
let timer = null;

// 克隆第一张图片
let clonefirstImg = photos.children[0].cloneNode(true);
// 将第一张图片添加至图片列表的末尾
photos.append(clonefirstImg);

function move(){
count++;
photos.style.transform = `translate(-${count*100}%)`;
// 为什么要加过渡? 因为切换到了最后一张假图时会将过渡去掉
photos.style.transition = "transform 0.5s linear";
// 到达最后一张后无动画回到第一张
if(count==len){
count=0;
setTimeout(()=>{
// 取消过渡 500毫秒之后切换第一张
photos.style.transform = "translateX(0)";
photos.style.transition = "none";
},500);
}
}

// 自动轮播
timer = setInterval(()=>{
move();
setActive(btns,len,count);
},2000);

2.4. 静止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 鼠标在图片上时定时器停止
photos.parentElement.addEventListener("mouseenter",()=>{
// console.log("enter");
clearInterval(timer);
timer = null;
});
// 鼠标不在图片上时定时器重启
photos.parentElement.addEventListener("mouseleave",()=>{
// console.log("leave");
timer = setInterval(()=>{
move();
setActive(btns,len,count);
},2000);
});

3. 图片轮播下的按钮点播

3.1. 思路

  • 按钮的不同选中状态

    • 每个按钮的选中状态互斥

    • 随图片轮播而不停被选中

    • 当鼠标选中第几个按钮,可视区显示第几张图

3.2. html骨架原型

王者荣耀CSS实现main-top.left部分

3.3. js代码

  • count依然控制图片列表的移动,按钮指向第几张图,下一个2s后就应该播下一张
1
2
3
4
5
6
7
8
9
10
11
12
// 按钮数组
const btns = document.querySelectorAll(".main-top .btns a");

// 底部对应信息鼠标进入事件
for (let i = 0; i < len; i++) {
btns[i].addEventListener("mouseenter",()=>{
count = i;
setActive(btns,len,i);
photos.style.transform = `translate(-${count*100}%)`;
photos.style.transition = "none";
});
}

4. 绝对定位下的图片轮播

4.1. 改变样式

  • 将所有图片设置为绝对定位。第一张图片left:0,其余图片left:100%,都在第一张图片右边

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    .main-top .photos {
    position: relative;
    display: flex;
    height: 298px;
    }
    .main-top .photos .item {
    position: absolute;
    left: 100%;
    width: 100%;
    /* 禁止缩放 */
    flex-shrink: 0;
    }
    .main-top .photos .item:first-child {
    left: 0;
    }

4.2. 实现思路

  • 比显示图片索引小的在左边,比显示图片索引大的在右边

    • 让需要展示第二张图片时,第一张图片left:-100%,第二张位置left变为0,其余的还是left:100%
    • 让需要展示第三张图片时,第一,二张图片left:-100%,第三张位置left变为0,其余的还是left:100%
    • 让需要展示最后一张(len-1)图片时,第一,二,…,(len-2)张图片left:-100%,最后一张位置left变为0,复制的第一张图片的left:100%
  • 需要设置 previousIndex,记录原来的展示图片,实现移动效果

    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
    let count = 0;
    let previousIndex = 0;
    let activeBtnEl = btns[0];

    function move(){
    photos.children[previousIndex].style.transition = "left 0.5s linear";
    if(count == len){
    activeButton(0);
    }
    else {
    activeButton(count);
    }
    for (let j = 0; j <= len; j++) {
    var nowItem = photos.children[j];
    // 选中的索引图片
    if(j === count){
    nowItem.style.left = 0;
    nowItem.style.transition = "left 0.5s linear";
    }
    else if( j < count){
    nowItem.style.left = "-100%";
    if(previousIndex !== j) {
    nowItem.style.transition = "none";
    }
    }
    else {
    nowItem.style.left = "100%";
    if(previousIndex !== j) {
    nowItem.style.transition = "none";
    }
    }
    }
    // 到达最后一张后无动画回到第一张
    if(count == len){
    count = 0;
    setTimeout(()=>{
    // 取消过渡 500毫秒之后切换第一张
    for (let i = 0; i < len; i++) {
    photos.children[i].style.left = "100%";
    photos.children[i].style.transition = "none";
    }
    photos.children[0].style.left = "0";
    photos.children[0].style.transition = "none";
    },500);
    }
    previousIndex = count;
    }

4.3. 导航点播

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function activeButton(index) {
activeBtnEl.classList.remove("active");
btns[index].classList.add("active");
activeBtnEl = btns[index];
}

// 底部对应信息鼠标进入事件
for (let i = 0; i < len; i++) {
btns[i].addEventListener("mouseenter",()=>{
count = i;
activeButton(i);
move();
});
}

5. 导航点播列表-左移

5.1. 思路

  • 导航中每项的不同选中状态

    • 每项的选中状态互斥

    • 当鼠标选中第几项,可视区显示第几个列表

5.2. html骨架原型

王者荣耀CSS实现main-top.center部分

王者荣耀CSS实现content-center的子导航部分

王者荣耀CSS实现hero-skin的hero-content部分

5.3. js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
// 导航栏鼠标悬停-内容转变-向左
function changeTranslateX(navs, len, list, flag){
// console.log(navs,len,list);
for (let i = 0; i < len; i++) {
navs[i].addEventListener("mouseenter",()=>{
setActive(navs,len,i);
list.style.transform = `translateX(-${i*100}%)`;
if(flag) {
list.style.transition = "transform 0.5s linear";
}
});
}
}
  1. 得到导航中的项数组
  2. 得到包裹多个列表的元素,每个项绑定该元素的前移距离
    • true 表示添加移动过渡
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
// 中间部分 - 导航栏
const navs = document.querySelectorAll(".main-top .nav .item");
// 中间部分 - 内容列表
const news = document.querySelector(".main-top .news");
// 中间导航栏互斥
changeTranslateX(navs,navs.length,news,true);


// 内容中心每个内容中的小标题
// 精品栏目
const contentGreatSubNavs = contentList.querySelectorAll(".great-part .caption .item");
const contentGreatContentList = contentList.querySelector(".great-part .sub-content-list");

// 赛事精品
const contentGameSubNavs = contentList.querySelectorAll(".game-part .caption .item");
const contentGameContentList = contentList.querySelector(".game-part .sub-content-list");

// 英雄/皮肤导航栏
const heroSkinNavs = document.querySelectorAll(".hero-skin .item_nav .item");
// 英雄/皮肤内容列表
const heroSkinContent = document.querySelector(".hero-skin .hero-content");

// 精品推荐
changeTranslateX(contentGreatSubNavs,contentGreatSubNavs.length,contentGreatContentList);
// 赛事精品
changeTranslateX(contentGameSubNavs,contentGameSubNavs.length,contentGameContentList);
// 英雄/皮肤导航内容移动
changeTranslateX(heroSkinNavs,heroSkinNavs.length,heroSkinContent,true);

6. 导航点播列表-上移

6.1. 思路

  • 导航中每项的不同选中状态

    • 每项的选中状态互斥

    • 当鼠标选中第几项,可视区显示第几个列表

6.2. html骨架原型

王者荣耀CSS实现content-center的dropdown部分

6.3. js代码

1
2
3
4
5
6
7
8
9
10
11
function changeTranslateY(navs, len, list, flag){
for (let i = 0; i < len; i++) {
navs[i].addEventListener("mouseenter",()=>{
setActive(navs,len,i);
list.style.transform = `translateY(-${i*100}%)`;
if(flag) {
list.style.transition = "transform 0.5s linear";
}
});
}
}
  1. 得到导航中的项数组 A
  2. 得到包裹多个列表的元素 B
  3. A中每个项绑定B的上移距离,不添加移动过渡
1
2
3
4
5
const contentHeroSubNavs = contentList.querySelectorAll(".hero-part .hero-type .item");
const contentHeroContentList = contentList.querySelector(".hero-part .hero-list");

// 英雄推荐
changeTranslateY(contentHeroSubNavs,contentHeroSubNavs.length,contentHeroContentList);

7. 导航点播列表-透明度

7.1. 思路

  • 导航中每项的不同选中状态

    • 每项的选中状态互斥

    • 当鼠标选中第几项,可视区是逐渐显示第几个列表

7.2. html骨架原型

王者荣耀CSS实现content-center的content-list部分

王者荣耀CSS实现match-center的match-content部分

7.3. js代码

  • 透明度的变换要先去除移动过渡,添加上自身的透明度过渡
    • 不然会变为空白->内容显示的过渡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function setOpacity(navs, len, num) {
for (let i = 0; i < len; i++) {
// console.log(navs[i]);
navs[i].style.transition = "none";
if(i==num){
navs[i].style.opacity = 1;
navs[i].style.transition = "opacity 0.6s linear";
}
else {
navs[i].style.opacity = 0;
}
}
}
// 导航栏鼠标悬停-内容透明度改变
function changeOpcaity(navs, len, list) {
// console.log(navs,len,list);
for (let i = 0; i < len; i++) {
navs[i].addEventListener("mouseenter",()=>{
setActive(navs,len,i);
setOpacity(list.children,len,i);
list.style.transform = `translateX(-${i*100}%)`;
});
}
}
  1. 得到导航中的项数组 A
  2. 得到包裹多个列表的元素 B
  3. A中每个项绑定B的前移距离,不添加移动过渡
  4. A中每个项绑定B中每个项的透明度,对应项透明度为1,其余为0,添加透明度过渡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 内容中心导航栏
const contentNavs = document.querySelectorAll(".content-center .item_nav .item");
// 内容中心内容列表
const contentList = document.querySelector(".content-center .content-list");

// 赛事中心导航
const matchNavs = document.querySelectorAll(".match-center .item_nav .item");
const matchList = document.querySelector(".match-center .match-content");

// 内容中心导航内容移动
changeOpcaity(contentNavs,contentNavs.length,contentList);

// 赛事中心
changeOpcaity(matchNavs,matchNavs.length,matchList);

7.4. 消除元素

  • 点击该元素使父元素不可见

  • 原html骨架

    1
    2
    3
    <div class="down-flow">
    <a href="#" class="close">x</a>
    </div>
1
2
3
4
5
6
const closeBtn = document.querySelector(".down-flow .close");

closeBtn.addEventListener("click",function(e) {
e.preventDefault();
this.parentElement.style.display = "none";
});
本文结束  感谢您的阅读