对本文内容的说明:
由于该练习文章其实是笔者刚学习完 Vue 就来写的小项目,所以该文章的实现方式在现在看来,是十分复杂且繁琐的,而且代码写的也十分混乱,页面无法实现兼容性,使用了 HTML、CSS、JavaScript、jQuery、Vue ,因此源码肯定对各位帮助不大。文章内容主要就是记录了当时遇到的一些问题和样式上的内容。因此该文章主要就是个练习和总结文,方便之后遇到相关问题后能迅速找到解决方案。由于大家还是想要源码和相关资源,所以最后还是决定给大家贴出来。
但是重要的事情说三遍,该源码真的仅供各位参考,仅供参考!仅供参考!仅供参考!写的真的很差。所以推荐大家用 Vue项目 + ElementUI 去写。
文中的静态资源,各位如有需要可去评论区自行下载(除了在游戏安装路径下的图之外,都是自己在游戏上截图的)。
本文免费源代码点击👉链接👈即可进入下载页面 (再次说明:该源码写的真的很差,仅供参考,到各位的浏览器上不一定能正常显示)。
欢迎大家前往 我的 Vue 专栏 了解更多关于 Vue 的内容。
在仿写客户端的过程中,我使用了 HTML、CSS、JavaScript、jQuery、Vue
其中JS和jQuery用来实现DOM操作(主要还是用jQuery)
由于在写本文章的时候,笔者还处于学习状态,模仿客户端界面是一时兴起,想着做点什么练练手,看看各种复杂的功能该如何实现,也没有接触过项目,不知道规范,因此才混合起来使用这些技术。但从目前来看,确实是太稚嫩了。所以推荐大家不要像源码中那样写(多去学习使用 Vue 或者 React + 各种组件)。
而且在编写代码前,我对Vue的理解是很片面的,认为Vue最大的用处就是编写单页面应用,而我在思考的过程中觉得仿写客户端需要多页面的跳转,因此最终没有选择采用Vue项目的形式。后来查询了一下,Vue项目应该也能实现多页面应用,不过这方面的问题只能以后尝试了。
因此我没有搭建Vue项目,那么做DOM操作,我还是觉得jQuery更简单,因此使用jQuery。然后用Vue来绑定事件及实现一些功能(在文章中会提到)。
然后说回到代码本身,它是有浏览器兼容性问题的,而且解决浏览器兼容性这方面是我的弱项。在代码中我尝试使用%的方式,来确保内容可以根据浏览器的宽高来等比例缩小或放大,但从结果上来看…em…差强人意,但至少一多半的内容都能正常展现。而且最开始的感觉还挺良好的,以为找到了还算不错的解决方案。不过能出现差错也代表着编写的代码是有一定问题的。以后再尝试的话,会采用rem试试看,并尽量编写更好的代码。
不过游戏客户端本身就是无法拉伸的,本着这样的想法,最后还是用%完成了在Chrome浏览器的,指定页面大小下的布局(其他浏览器我在尝试的时候,确实是有兼容性问题)
(也就是说,我考虑了兼容性问题,结果在本次编写代码过程中,还是无法解决…)
我在编写代码过程中的相关素材取自:
Iconfont-阿里巴巴矢量图标库
觅元素
主要素材来源:
游戏下载路径\英雄联盟\TCLS\ui\res\test
游戏下载路径\英雄联盟\TCLS\ui\servers
游戏下载路径\英雄联盟\TCLS\ui\movie_tpf\img
游戏下载路径\英雄联盟\TCLS\ui\media
游戏下载路径\英雄联盟\Air\assets\images
游戏下载路径\英雄联盟\Air\assets\storeImages
说到这些素材,当我编写登录界面的时候,以为所有样式内容都需要用css来实现,结果到了后面感觉很多样式都编写不出来时才发现,原来…我之前辛辛苦苦自己做的样式,lol都是有相关素材的。因此,我们只要能找到素材,那我们要做的就只是定位,绑定事件即可,其实还是蛮简单的。除此以外,因为我切图手法比较笨拙,所以后期有一些都是我直接从客户端上拿下来的。
进入内容前,直接上演示视频:(只实现了 登录 到 主界面)
【前端】仿写LOL客户端界面(演示视频)
前端模仿英雄联盟客户端界面
帐号登录界面
对于页面设计,我个人的经验之谈就是,先划分区域并开启绝对定位,然后让每个区域都处在对应的位置上。比如该部分的页面,我把它划分成了三个部分。划分好区域后,所有的内容都会在各自的区域中排布。
左上区域涉及到的用法:img和background-image的用法、按钮渐变效果
左下方区域的文字内容,本来我是自己贴图 + 用三个div设计的,后来发现lol素材里,它有一整张图片(除了红色边框中的内容外),那就直接拿来用了。红色边框内的文字,是一段超链接,我们只需要给它定位就可以了。
右侧区域涉及到的用法:加载效果(转动)、区域内容的转化、音乐的开关、登录功能
在右侧区域,下面看到的logo的动态效果是lol的素材。
注:img可以加载gif图片,因此这里才是动态效果,而如果是webm格式的,就比较麻烦了,在之后会提到这方面的问题。
背景图(img和background-image)
<!--左侧区域-->
<div class="left">
<!--联盟图标-->
<div class="logo">
<img src="./img/login-icon/logo-public.png"/>
</div>
</div>
/*左侧显示区域的整体样式*/
.left{
position: absolute;
width: 80%;
height: 94%;
left: 0;
background-image: url("./img/login-icon/login_page.jpg");
background-repeat: no-repeat;
background-size: cover;
}
/*英雄联盟图标样式*/
.logo{
position: absolute;
width: 95%;
border-bottom: 1px darkgoldenrod solid;
left: 2.5%;
}
最开始学习的时候,我觉得img是使用图片的最好方法,但是当遇到越来越多的需求的时候,我发现background-image的方式更应该被灵活运用,而且可以解决很多麻烦。
比如,在这个部分我就分别用了这两种方式。在我看来,它们的使用条件几乎没什么区别。
如果该图片是和文字在一行中或者一列中排列的,那么使用img是比较好的(从代码量上来看,性能方面未知)。因为用div设置背景,除去定位,一定要设置宽度高度,background-size和background-image。而对于img,除去定位,只需要设置宽度 / 高度 其中一项,图片就可以等比例缩放、放大。
(对于行内元素,只有在标签内写了一些内容,背景才会显示,而且无法设置背景相关信息。因此如果想把行内元素变成块元素,那不如直接用div设置)
比如:
在这种用法里,外部容器div被我用红色背景显示。这个外部容器在这种情况下,可以不用设置height,除非项目中指定高度。那么这样一来,div的高度会被内容撑起。这里能设置的样式就很多了,比如文字和图片之间有间距,只需要设置margin;文字和图片使用居中方案;又或是修改图片大小并定位。
如果是把图片当作背景,确实是使用background会更好,因为外部容器div设置了背景,那么内部所有元素都只需要在这外部容器中操作。但是当然img也能当背景,只不过这些元素肯定是不能放在img标签内了,只能再另建一个容器,把内容放在img上,让img的z-index更小,那显然这种方式不利于维护。
也就是说,其实这两种在任何情况下都还算是可以转换使用的,但是使用background显然是可以设置更多属性的去解决很多问题的。
区别还是有一些的:
如果图片事先知道尺寸大小,且想完整显示,那么就需要设置根标签的宽度、高度。如果不知道却依然坚持用background,要不就是自己手动调整宽度高度,要不就是用background-size: cover 图片多出的部分会删去,background-size: contain 标签中会出现空白的部分。那么用img就可以设置宽度 / 高度 任一属性,就可以在不知道尺寸大小的情况下,等比例缩放。
如果遇到CSS Sprites(css精灵图),那么只能使用background。
如果遇到能点击的图片,那么只能使用background。
<a href="javascript:;" class="re"></a>
.re{
display: block;
position: absolute;
width: 31px;
height: 31px;
right: 11%;
top: 67%;
background-image: url("./img/login-icon/re.png");
}
.re:hover{
background-image: url("./img/login-icon/re_h.png");
}
.re:active{
background-image: url("./img/login-icon/re_c.png");
}
按钮颜色渐变
这里所谓的“按钮”,其实是一个超链接a。如果真的使用按钮,首先为其设置样式会稍微麻烦一些,而且点击按钮就去展示一个新页面,不如直接使用超链接。
<!--查看详情-->
<a href="https://lol.qq.com/main.shtml" class="detail" target="_block">查看详情</a>
/*查看详情的样式*/
.detail{
position: absolute;
display: block;
width: 100px;
height: 30px;
left: 2.5%;
bottom: 2.5%;
text-align: center;
line-height: 30px;
background-color: #131622;
border: 1.5px darkgoldenrod solid;
color: #CABD8E;
transition-property: all;
transition-duration: 0.5s;
}
.detail:hover{
border: 1.5px #DDC796 solid;
color: #F0E5D3;
background-image: linear-gradient(rgb(23,30,38),rgb(110,100,74));
}
渐变效果采用的是CSS的linear-gradient去设计,这种方式挺简单的。更复杂的可以考虑canvas。
(但是实际上,后来发现lol的按钮是图片效果,在lol素材里可以找到,这就方便很多了)
之后所有的渐变按钮都是这种方式,下面这个a中的图片,就是采用上面提到的img方式。
加载效果(转动)
视频中的加载效果 -> 该效果的教学视频
从 安全检测 转成 登录方式(页面部分内容的变化)
页面转化是根据数字的变化,当安全检测值到100%,就“跳转”。这部分就很简单了,数据绑定 + v-cloak(防止表达式闪烁)
<!--安全检测的进度条-->
<div v-cloak class="finish">{{finish}}%</div>
然后在vue的mounted生命周期中,直接设置定时器,模拟安全检测效果,并在结束时清除定时器。
mounted(){
/*刚进入页面就会直接进行安全检测*/
this.intervalTime = setInterval(()=>{
this.finish += 20
/*当安全检测完成时,跳转到登录页面,且清除定时器*/
if(this.finish === 100){
this.right = false
clearInterval(this.intervalTime)
}
},1000)
},
在vue里设置了一个属性right,其初始值是true。当安全检测结束后,right为false,然后触发v-if:
<!--右侧安全检测后的登录区域-->
<div class="right" v-if="!right">
....
</div>
<!--右侧展示安全登录检测框-->
<div class="right" v-else>
<!-- 实现安全检测的动画 -->
<div class="container">
<!--旋转球效果-->
<div class="rotate">
<!--代表每个球-->
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<!--间隔-->
<div class="gap"></div>
<p>正在进行安全检测:</p>
<!--安全检测的进度条-->
<div v-cloak class="finish">{{finish}}%</div>
</div>
</div>
那么这种用法将会是之后所有 部分区域页面内容变化的实现方式 (不是v-if就是用的v-show),也就是模拟出了类似单页应用的效果。而将这两个div都设置同一个类,也就保证了切换后,它们最外围容器的位置、样式都是一样的。
但是这种方式肯定不能和vue-router相提并论。首先这种是有切换损耗的,并且不可能实现多次的转换(比如v-if只能用作两部分内容的切换,v-show也类似,但是v-show可以让多个内容同时显示或隐藏)
图标控制音乐开关
<!--右上角相关图标-->
<div class="right-icon">
<!--看不见的播放器-->
<audio id="player" loop="loop">
<source src="./img/login-icon/sound.mp3"/>
</audio>
<a href="javascript:;" v-if="ok" @click="music"><img src="./img/login-icon/laba-off.png"/></a>
<a href="javascript:;" v-else @click="music"><img src="./img/login-icon/laba.png"/></a>
<a href="javascript:;"><img src="./img/login-icon/suoxiao.png"/></a>
<a href="javascript:;"><img src="./img/login-icon/guanbi.png"/></a>
</div>
音频采用的是audio,只要不给其添加controls属性,那么在页面中就看不到这个音乐播放器,我们只要在音乐的图标中,添加一个开关的v-if(切换图片)和@click事件,就完成了这部分内容。
methods: {
music(){ /*音乐相关信息*/
this.ok = !this.ok
if(this.ok === false){
player.src = './img/login-icon/sound.mp3';
player.play();
}else{
player.src = '';
player.pause();
}
}
}
登录功能
首先这里 QQ登录 - 微信登录、切换至快速登录 - 使用其它帐号登录 ,部分内容的转换采用的都是v-if。
<!--右侧安全检测后的登录区域-->
<div class="right" v-if="!right">
<!-- 选择登录方式之后 -->
<div class="login-on" v-if="login">
<!--上方的两个选项-->
<!--QQ选项-->
<a href="javascript:;" :class="{classA: hasLogin, classB: !hasLogin, classC: true}" @click="qqLogin">
<img src="./img/login-icon/qq.png"/>
QQ登录
</a>
<!--微信选项-->
<a href="javascript:;" :class="{classA: !hasLogin, classB: hasLogin, classD: true}" @click="chatLogin">
<img src="./img/login-icon/wechat.png"/>
微信登录
</a>
<!--设定登录小三角-->
<div :class="{triangleQQ: hasLogin, triangleChat: !hasLogin}"></div>
<!--QQ登录区-->
<div class="QQAction" v-if="hasLogin">
<!--正常输入帐号区域-->
<div v-if="quickLogin">
<div class="QQAction-color1">QQ帐号</div>
<input type="text" title="安全提示:腾讯英雄联盟不会在登录或游戏过程中询问与您帐号有关的任何密保信息,请谨防骗取密保的盗号木马" v-model="account"/>
<div :style="{height: '10px'}"></div>
<div class="QQAction-color1">QQ密码</div>
<input type="password" title="安全提示:腾讯英雄联盟不会在登录或游戏过程中询问与您帐号有关的任何密保信息,请谨防骗取密保的盗号木马" v-model="password"/>
<br/>
<div :style="{height: '10px'}"></div>
<input type="checkbox"/><span class="QQAction-color2">记住账号</span>
<br/>
<input type="checkbox" :checked="hasCheck" @click="hasCheck=!hasCheck"/><span class="QQAction-color2">我已详细阅读并同意</span>
<br/>
<a href="https://game.qq.com/contract.shtml" class="QQAction-color3" target="_blank">腾讯游戏用户协议</a>
<span class="QQAction-color2">和</span>
<a href="https://game.qq.com/privacy_guide.shtml" class="QQAction-color3" target="_blank">隐私保护指引</a>
<div :style="{height: '10px'}"></div>
<a href="javascript:;" class="QQAction-color4" @click="gameStart">进入游戏</a>
<a href="javascript:;" class="QQAction-color5" @click="quickLogin=!quickLogin">切换至快速登录</a>
</div>
<!--快速登录账号区域-->
<div v-else>
<div class="QQAction-color1">检测到您已登录QQ帐号,请选择登录</div>
<div :style="{height: '10px'}"></div>
<a href="javascript:;" class="Quick-btn">只爭朝夕不負韶華(54*****57)</a>
<div :style="{height: '70px'}"></div>
<input type="checkbox" :checked="hasCheck" @click="hasCheck=!hasCheck"/><span class="QQAction-color2">我已详细阅读并同意</span>
<br/>
<a href="https://game.qq.com/contract.shtml" class="QQAction-color3" target="_blank">腾讯游戏用户协议</a>
<span class="QQAction-color2">和</span>
<a href="https://game.qq.com/privacy_guide.shtml" class="QQAction-color3" target="_blank">隐私保护指引</a>
<div :style="{height: '10px'}"></div>
<a href="javascript:;" class="QQAction-color4" @click="quickStart">确定</a>
<a href="javascript:;" class="QQAction-color5" @click="quickLogin=!quickLogin">使用其它帐号登录</a>
</div>
</div>
<!--微信登录区-->
<div class="wechatAction" v-else>
<div class="QQAction-color2">使用 微信扫码 安全登录</div>
<div :style="{height: '10px'}"></div>
<img src="./img/login-icon/QR.png"/>
<a href="javascript:;" class="re"></a>
<div :style="{height: '20px'}"></div>
<input type="checkbox" /><span class="QQAction-color2">我已详细阅读并同意</span>
<br/>
<a href="https://game.qq.com/contract.shtml" class="QQAction-color3" target="_blank">腾讯游戏用户协议</a>
<span class="QQAction-color2">和</span>
<a href="https://game.qq.com/privacy_guide.shtml" class="QQAction-color3" target="_blank">隐私保护指引</a>
</div>
</div>
<!-- 进入页面选择登陆方式 -->
<div v-else>
<!--选择QQ-->
<a href="javascript:;" class="QQLogin" @click="qqLogin">
<img src="./img/login-icon/qq.png"/>
QQ登录
</a>
<!--选择微信-->
<a href="javascript:;" class="wechatLogin" @click="chatLogin">
<img src="./img/login-icon/wechat.png"/>
微信登录
</a>
</div>
</div>
然后是Vue中全部内容:
<script type="text/javascript">
new Vue({
el: '#lol',
data: {
ok: true, //用来判断喇叭是否开启
right: true, //用来判断是否在进行安全检测
finish: 0, //用来判断安全检测的进度
login: false, //选择一种登录方式,且login为true后永远不变回去
hasLogin: true, // true是QQ登陆,false是微信登录,默认QQ
account: '', //记录帐号
password: '', //记录密码
hasCheck: false, //判断相关说明是否已经框选
quickLogin: true //判断是否进行快速登录
},
mounted(){
/*刚进入页面就会直接进行安全检测*/
this.intervalTime = setInterval(()=>{
this.finish += 20
/*当安全检测完成时,跳转到登录页面,且清除定时器*/
if(this.finish === 100){
this.right = false
clearInterval(this.intervalTime)
}
},1000)
},
methods: {
music(){ /*音乐相关信息*/
this.ok = !this.ok
if(this.ok === false){
player.src = './img/login-icon/sound.mp3';
player.play();
}else{
player.src = '';
player.pause();
}
},
qqLogin(){ /*选择QQ登录*/
if(this.login === false){
this.login = true
}
if(this.hasLogin === true){
}else{
this.hasLogin = true
}
},
chatLogin(){ /*选择微信登录*/
if(this.login === false){
this.login = true
}
if(this.hasLogin === false){
}else{
this.hasLogin = false
}
},
gameStart(){ //正则表达式,判断账号密码是否符合规范
let reg = /[0-9]{6,}/ig
let reg2 = /\S{6,}/ig
if(reg.test(Number(this.account))&®2.test(this.password)){
if(this.hasCheck){
alert('登录成功')
/*登录成功后进入选择服务器页面*/
window.location.href="./chooseServer.html"
}else{
alert('请仔细阅读内容后,勾选按钮')
}
}else{
alert('帐号或密码输入错误')
}
},
quickStart(){ /*快速登录功能*/
if(this.hasCheck){
alert('登录成功')
/*登录成功后进入选择服务器页面*/
window.location.href="./chooseServer.html"
}else{
alert('请仔细阅读内容后,勾选按钮')
}
}
}
})
</script>
那么通过之前的说明,我们要改变部分区域的内容,我们使用v-if就可以实现。也就是当我们点击某个按钮后,修改了data中的数据,从而导致v-if v-else触发,在同一个区域就展示了不同的内容。
想要实现:我已详细阅读并同意 被选择后才能进入下一步,就只需要绑定checked:
<input type="checkbox" :checked="hasCheck" @click="hasCheck=!hasCheck"/><span class="QQAction-color2">我已详细阅读并同意</span>
那么设置成:当点击按钮,导致hasCheck为true时,才能跳转到下个页面就完成了该功能。
然后点击“进入游戏”或“确定”(超链接a)后,去判断是否hasCheck为true,以及如果选择账号登陆,账号密码是否符合正则表达式即可。最后如果满足了条件,使用window.location.href"
跳转页面即可。
这里还有一个用法就是实现三角形,这个就是面试中常问到的问题了。
三角形很容易实现,只需要宽高设为0,给一定的border大小,将其设为透明,然后根据三角形的朝向设置color就实现了。比如下面给border-top-color,那就是箭头朝下。border-bottom-color,那就是箭头朝上。
然后设计一个,当点击某登录方式时,让箭头偏移位置就可以实现箭头也跟着切换的效果了。
/*实现三角形*/
/*三角的通用样式*/
.triangleQQ,
.triangleChat{
position: absolute;
width: 0px;
height: 0px;
border: 5px solid transparent;
border-top-color: #776947;
top: 37.5%
}
/*两种方式三角的位置不同*/
.triangleQQ{
left: 25%;
}
.triangleChat{
right: 25%;
}
除此以外,第一个页面就都是定位和设置样式了。
选择服务器界面
整体效果:
该界面比较简单,如何划分区域都可以,把它看成一个区域也行,上下两个区域也行。
在这里先说一说我没实现的功能:
(1)无法点击大区图标旁的两个按钮实现换区功能;
(2)在服务器列表中无法点击服务器,去实现换区功能。
这个功能如果不构建Vue项目,感觉实现起来会十分繁琐,单页应用效果肯定会是最佳选择,因为这不是单纯v-if就能实现的。
在每个服务器名字前的绿点,是这么实现的:(后来发现,也有这个素材)
/*给所有服务器前加上绿色标志*/
.open a::before{
content: '◆';
color: #1FAE69;
margin-right: 10px;
}
那么该部分除了定位和样式外,只用到了Vue的过渡效果
Vue的过渡效果(transition标签)
<transition name="serviceOpen">
<div class="open" v-show="openService" v-cloak>
<div class="server-font" :style="{top: '1%'}">全网络大区</div>
<a href="javascript:;" class="blueServer" :style="{top: '5%'}">男爵领域</a>
<a href="javascript:;" class="blueServer" :style="{top: '5%', left: '15%'}">峡谷之巅</a>
<div class="server-font" :style="{top: '15%'}">电信</div>
<a href="javascript:;" class="yellowServer active" :style="{top: '19%'}">艾欧尼亚</a>
<a href="javascript:;" class="yellowServer" :style="{top: '19%', left: '15%'}">祖安</a>
<a href="javascript:;" class="yellowServer" :style="{top: '19%', left: '27%'}">诺克萨斯</a>
<a href="javascript:;" class="yellowServer" :style="{top: '19%', left: '39%'}">班德尔城</a>
...
</div>
</transition>
transition标签是Vue中的方法,被该标签包裹的内容,就会在内容改变时,产生过渡效果。使用方法很简单,而且频繁切换也是没问题的。如果是css中设置的过渡和动画效果,是没办法实现返程的,也就是说显示需要触发一次,关闭再触发一次。而Vue的transition在设置的时候,就可以设计显示效果和隐藏效果,很方便。
/*服务器列表动画*/
.serviceOpen-leave-active,
.serviceOpen-enter-active{
transition: all 1s;
}
.serviceOpen-leave-to,
.serviceOpen-enter{
opacity: 0;
}
排队加载界面
整个背景采用的是素材,该部分只是向其中添加了文字和进度条。上面两个场景的变换,依然采用v-if。排队时间变化依然采用 在mounted生命周期中添加定时器的方式来实现。取消按钮也和以前一样。
在这里主要是进度条的实现
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>League of Legends</title>
<!--设置网页图标-->
<link rel="icon" href="./img/icon/lol2.ico" type="image/x-icon">
<style>
/*通用设置*/
*{
margin: 0;
text-decoration: none;
list-style: none;
}
/*防止表达式闪烁*/
[v-cloak]{display:none;}
/*整体架构设计*/
#game{
position: absolute;
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-size: contain;
background-image: url("img/new-icon/wait_icon.png");
}
/*正在排队等候*/
.wait{
position: absolute;
color: #F0E7D6;
top: 57%;
left: 49.5%;
transform: translateX(-50%);
font-size: 18px;
}
/*设置所有span颜色*/
span{
color: #DDC796;
}
/*当前位置和等待时间通用样式*/
.currentPosition,
.waitTime{
position: absolute;
color: #F7F0E0;
left: 45%;
font-size: 13px;
}
.currentPosition{
top: 62%;
}
.waitTime{
top: 65%;
}
.currentPosition span{
margin-left: 25px;
}
/*取消和进行栏*/
.cancel{
position: absolute;
display: block;
width: 8%;
height: 5%;
bottom: 23%;
left: 45.2%;
text-align: center;
line-height: 35px;
background-color: #131622;
border: 1.5px darkgoldenrod solid;
color: #CABD8E;
transition-property: all;
transition-duration: 0.5s;
}
.cancel:hover{
border: 1.5px #DDC796 solid;
color: #F0E5D3;
background-image: linear-gradient(rgb(23,30,38),rgb(110,100,74));
}
/*排队完成后,进入加载页面*/
/*进度条样式*/
.progressContainer{
position: absolute;
width: 15%;
height: 1.2%;
top: 62%;
left: 42%;
border: 1px solid #C4A76C;
border-radius: 10px;
}
#progress{
width: 0%;
height: 100%;
background-image: linear-gradient(to right, rgb(0,90,130),rgb(108,200,210));
}
</style>
</head>
<body>
<div id="game">
<!--正在载入-->
<div v-if="loading" v-cloak>
<div class="wait">正在载入</div>
<div class="progressContainer">
<div id="progress"></div>
</div>
</div>
<!--正在排队-->
<div v-else v-cloak class="waiting">
<div class="wait">正在排队等候</div>
<div class="currentPosition">当前位置:<span v-cloak>{{queueNum}}</span></div>
<div class="waitTime">预计等待时间:<span v-cloak>{{minute}}分钟</span></div>
<a class="cancel" href="./chooseServer.html">取消</a>
</div>
</div>
</body>
<script type="text/javascript" src="js/vue.js"></script>
<script src="js/jquery-1.12.3.js" type="text/javascript"></script>
<script type="text/javascript">
new Vue({
el: '#game',
data: {
queueNum: 77777, //当前排队位置
minute: 7, //预计等待时间
loading: false, /*判断是否开始加载*/
w: 0 /*代表当前加载进度*/
},
mounted(){
/*刚进入页面就排队*/
this.intervalTime = setInterval(()=>{
this.queueNum -= 11111
this.minute -= 1
if(this.queueNum === 0){
this.loading = true;
/*现在转为加载界面*/
this.intervalLoad = setInterval(()=>{
this.w += 1
$('#progress').css('width', this.w +'%')
if(this.w === 100){
window.location.href="./LOLGame.html";
clearInterval(this.intervalLoad)
}
},100)
clearInterval(this.intervalTime)
}
},1000)
}
})
</script>
</html>
进度条的实现
this.intervalLoad = setInterval(()=>{
this.w += 1
$('#progress').css('width', this.w +'%')
if(this.w === 100){
window.location.href="./LOLGame.html";
clearInterval(this.intervalLoad)
}
},100)
首先定时器设计的也很简单,0.1s触发一次,属性w用来记录进度条的进度,然后根据w的值,来修改进度条中的进度。当该值为100时,跳转页面,进入到游戏客户端主页面。
html部分:
<div class="progressContainer">
<div id="progress"></div>
</div>
进度条的css实现:
/*排队完成后,进入加载页面*/
/*进度条样式*/
.progressContainer{
position: absolute;
width: 15%;
height: 1.2%;
top: 62%;
left: 42%;
border: 1px solid #C4A76C;
border-radius: 10px;
}
#progress{
width: 0%;
height: 100%;
background-image: linear-gradient(to right, rgb(0,90,130),rgb(108,200,210));
}
这里的border-radius的单位一定是px,才能是两头圆的样式,不能用%。然后进度条中的颜色渐变依然依靠linear-gradient。然后进度条本身也是div,出现加载的效果,是因为在定时器中不断修改该div的width才出现的效果。
(所以进度条还是很容易实现的,之前以为会是很难的)
游戏客户端主页面
我将该页面分为了三个区域:
首先涉及到初次进入客户端出现的广告效果
对于上方区域,从左往右说,play的按钮本身肯定也是有素材在的,因为我没找到所以就自己截了几张。也就是说play是用a标签加background的形式实现的。然后列表栏(主页、线、云顶…),这部分内容就是所有网页都会涉及的内容了,有一些练习可以参考我之前的【文章】。那么这么多“按钮”点击后,会修改左下区域的内容,去展示各自的信息,那么显然只有Vue项目中才能做到了。除此以外,都是拿到素材,为其定位和设置样式了。
然后上方区域还有一个就是鼠标移动到头像时,相应内容的显现
对于右侧好友区域,因为那些图标都需要素材,我又没有找到的情况下,就去自己截了图。对于好友区域上下方的各按钮的触发就没有实现功能了(根据之前的功能实现,其实这部分很简单了)
然后右侧区域还有一个就是好友列表下拉栏的实现
对于主页内容区域,除去素材的定位和元素样式,重点就是轮播图的实现
最后简单说一下视频的自动播放
初次进入客户端的广告
思路比较简单:在主页内容区域,设置一个z-index优先级很高的div,其宽高为当前区域100%。再将其背景设为黑色并设置透明度,即可实现背景阴影效果。在其中再建一个可以点击的图片和关闭按钮,其z-index高于底部div即可。
<!--刚进入游戏的广告-->
<div class="adver" v-show="advertise"></div>
<a href="https://lol.qq.com/act/a20200828anniversarychest/index.html?e_code=500142&area=1" target="_blank" class="ling" v-show="advertise">
<img src="img/new-icon/lingneng.png"/>
</a>
<a href="javascript:;" class="adverClose" v-show="advertise" @click="advertise=!advertise">X</a>
/*刚进入游戏的广告*/
.adver{
position: absolute;
width: 100%;
height: 100%;
opacity: 0.7;
background-color: black;
z-index: 1000;
}
.ling{
position: absolute;
display: block;
z-index: 1001;
width: 70%;
height: 80%;
left: 45%;
top: 50%;
transform: translate(-50%,-50%);
}
.ling img{
width: 120%;
height: 100%;
}
移入移出效果(mouseenter、mouseleave)
这个部分在通过之前打开服务器列表的功能实现后,也就很简单了。只要鼠标移入就打开,鼠标移出就关闭,然后用transition设计效果即可。
<!--头像区域-->
<a href="javascript:;" class="head" @mouseenter="more" @mouseleave="close"></a>
<!--头像打开内容-->
<transition name="headMore">
<div class="head_more" v-show="open" v-cloak>
<!--打开内容的皮肤背景-->
<div class="background"></div>
<img src="./img/game-icon/level_message.png" class="levelMessage"/>
<!--下方内容-->
<div class="message">
<img src="./img/game-icon/message.png"/>
</div>
<img src="./img/game-icon/name.png" class="name"/>
</div>
</transition>
打开的详细内容区,我又把它分为三个区域。最上方区域就是用background,中间区域的头像框和名字、下面区域的文字内容,因为没有具体的素材,再加上只有定位和样式的设计,所以都是采用截图的方式完成了。
好友列表的展开效果
这部分在我最开始构思的时候觉得很复杂,后来想到了这个很简单的方法。因为div元素是块元素,它会独自占据一行,那么根据这个特性,我们完全不需要设计什么,只需要当我们点击 最近游戏或者是好友列表,触发事件,让隐藏的内容出现,再像之前一样实现三角形的样式变化,该部分就完成了。(因为没有具体素材信息,就直接采用截图的方式,如果有素材,只需要将内容都放在div里,也肯定可以实现该功能)
<!--具体好友区域-->
<div class="friendMessage" v-cloak>
<a href="javascript:;" @click="message=!message" class="synthesize">
<!--箭头-->
<div :class="{classA: message, classB: !message}"></div>
综合 (1/1)
</a>
<div v-show="message" class="onlyFriend"><img src="./img/new-icon/only-friend.png"/></div>
<a href="javascript:;" @click="message2=!message2" class="lastGame">
<!--箭头-->
<div :class="{classA: message2, classB: !message2}"></div>
最近游戏
</a>
<div v-show="message2" class="onlyFriend"><img src="./img/new-icon/only-friend.png"/></div>
</div>
视频的自动播放问题
在主页页面中,这个本来是个视频,而且决定是让它自动播放,但是经过查阅资料发现,只能使用这种方式:(autoplay和muted共同使用)
<video src="img/game-icon/login-page.webm" id="video" autoplay muted loop @click="playVideo"></video>
但是这样使用,也就违背了自动播放视频的初衷,因为它不会播放音乐。除此以外还有更严重的问题,就是在Chrome中,就算是这么设置也有时无法自动播放视频,而这个所谓的有时,就是初次进入页面时。当浏览器缓存了视频后,再次重新进入网页就可以自动播放了。因此不知道有没有什么解决方案。在这里我还实现了一个功能,就是当点击视频的时候可以控制视频播放和暂停。
playVideo(){ /*播放视频*/
if(this.play === false){
video.play();
this.play = true;
}else{
video.pause();
this.play = false;
}
}
轮播图的实现
在具体看代码前,先说一下这部分还存在未解决的问题:刚进入页面的时候,第一次切换图片没有过渡效果,暂时不知道该如何解决。其实是有机会避免的,因为之前做过jQuery的轮播图,但是到了这里用Vue之后,思路有点混乱,所以导致代码部分出现了一些差错,又不想进行过多的修改,所以导致为了掩盖错误,去编写更多代码区弥补,不能很好的实现效果。所以相关思路可以参考我之前的文章【轮播图练习三】(虽然代码比较繁琐,但至少实现了功能👇)
轮播图的相关代码:
<!--轮播图-->
<div class="container">
<!--图片-->
<div id="list">
<img src="./img/new-icon/pic1.jpg" alt="1"/>
<img src="./img/new-icon/pic2.jpg" alt="2"/>
<img src="./img/new-icon/pic3.jpg" alt="3"/>
<img src="./img/new-icon/pic4.jpg" alt="4"/>
<img src="./img/new-icon/pic5.jpg" alt="5"/>
</div>
</div>
<!--两个箭头-->
<a href="javascript:;" class="leftArrow" @click="left"></a>
<a href="javascript:;" class="rightArrow" @click="right"></a>
<!--轮播图圆点-->
<div id="pointsDiv">
<span index="1" class="on" @click="points($event)"></span>
<span index="2" @click="points($event)"></span>
<span index="3" @click="points($event)"></span>
<span index="4" @click="points($event)"></span>
<span index="5" @click="points($event)"></span>
</div>
/*箭头*/
.leftArrow,
.rightArrow{
position: absolute;
display: block;
width: 2%;
height: 7%;
top: 30%;
background-size: contain;
background-repeat: no-repeat;
z-index: 999;
}
.leftArrow{
left: 5%;
background-image: url("./img/game-icon/pre_nromal.png");
}
.rightArrow{
right: 5%;
background-image: url("./img/game-icon/next_normal.png");
}
.leftArrow:hover{
background-image: url("./img/game-icon/pre_hover.png");
}
.leftArrow:active{
background-image: url("./img/game-icon/pre_click.png");
}
.rightArrow:hover{
background-image: url("./img/game-icon/next_hover.png");
}
.rightArrow:active{
background-image: url("./img/game-icon/next_click.png");
}
/*图片内容区域*/
.container{
position: relative;
width: 100%;
height: 88%;
top: 0;
overflow: hidden;
}
/*包含所有图片的<div>*/
#list {
width: 500%;
height: 100%;
position: absolute; /*绝对定位*/
z-index: 1;
transition-property: all;
transition-duration: 0.5s;
}
/*所有的图片<img>*/
#list img {
width: 20%;
height: 120%;
float: left;
}
/*包含所有圆点按钮的<div>*/
#pointsDiv {
position: absolute;
height: 10px;
width: 100px;
z-index: 2;
bottom: 6%;
left: 50%;
transform: translateX(-50%);
}
/*所有的圆点<span>*/
#pointsDiv span {
cursor: pointer;
float: left;
border: 1px solid #A88230;
width: 7px;
height: 7px;
border-radius: 50%;
background: #333;
margin-right: 5px;
}
/*第一个<span>*/
#pointsDiv .on,
#pointsDiv span:hover{
background: #A88230;
}
<script type="text/javascript">
new Vue({
el: '#lolgame',
data: {
/*轮播图的本质是:一个很宽的div中存放着几张图片,每个图片占据同样的大小,*/
/*这个大小就是在主页面区域能显示的大小。那如果要切换到下一张图,*/
/*其实就是修改left的值。即第一张图就是left为0,第二张图就是left为-100%*/
/*或者left是图片的宽度(需要在设计时就考虑好)*/
/*因此num值的范围不是1~5,而是0~4*/
num: 1, /*记录图片位置,但是没有考虑到数组的原因,num应该为0的,出现了错误,导致代码繁琐*/
first: true /*为自己错误买单*/
/*因为向右切换图片后 num比正常下标多1,所以需要标记,即:first为true */
/*所以只要first为true,那么向左切换图片,下标num就要-2,否则-1*/
},
mounted(){
/*自动轮播*/
this.intervalTime = setInterval(()=>{
/*只要向右就将first设置为true*/
this.first = true
/*去调用right方法*/
this.$options.methods.right.bind(this)();
this.$options.methods.right();
/*每5秒图片切换1次*/
},5000)
},
methods: {
left(){ /*点击左按钮*/
if(this.first === true){ /*只要图片向右轮播*/
this.num -= 2; /*那么下标要-2*/
this.first = false; /*向左轮播后,first为false*/
}else{ /*正在向左轮播,只需要下标-1*/
this.num -= 1;
}
/*如果下标减成负数,那就意味着需要切换到最后一张图片*/
if(this.num === -1){
this.num = 4
}
/*获取到图片列表,将图片根据num的值,决定该显示哪张图片*/
let $list = $('#list');
let per = -(this.num * 100);
$list.css('left', per+'%');
/*切换图片后,需要修改下面圆标点的样式,需要对应*/
let index = this.num; /*获取到当前下标,此时num没问题*/
let preIndex; /*代表着前一个图片的点*/
if(index === 4){ /*如果向左切换到最后一张图片,那么前一张图片必然是第一张*/
preIndex = 0
}else{ /*其他情况,前一张图片就是num + 1*/
preIndex = this.num + 1
}
/*然后获取所有的点*/
let $points = $('#pointsDiv>span');
/*将前一张图片的点的样式清除,给当前的点添加样式*/
$points[preIndex].className = '';
$points[index].className = 'on'
},
right(){ /*点击右按钮*/
this.first = true /*只要向右切换,first就为true*/
/*之前提到num的范围是0~4,但是这里向右轮播在设计时没考虑到,出现了差错*/
/*因为num初始值是1,向右轮播四张图,每次num都+1了,显然这里num为5才是最后一张图*/
if(this.num === 5){ /*如果向右切换到最后一张图片*/
this.num = 0; /*回到第一张图*/
}
/*获取到图片列表,将图片根据num的值,决定该显示哪张图片*/
let $list = $('#list');
let per = -(this.num * 100);
$list.css('left', per+'%');
this.num += 1;
/*切换图片后,需要修改下面圆标点的样式,需要对应*/
let index = this.num;
let preIndex;
if(index === 1){ /*如果此时index为1,也就是说上一张图是最后一张图*/
preIndex = 4
}else{ /*否则上一张图就会是num-2*/
preIndex = this.num - 2
}
/*然后获取所有的点*/
let $points = $('#pointsDiv>span');
/*将前一张图片的点的样式清除,给当前的点添加样式*/
$points[preIndex].className = '';
$points[index - 1].className = 'on';
},
points(e){ /*点击圆点切换图片*/
/*获取span的index值*/
let targetIndex = e.target.getAttribute('index')
// 只有当点击的不是当前页的圆点时才翻页
// 这么实现是有bug的,因为左切换图片会导致num错乱,这时点击圆点切换图片就会出现问题
// 但是我个人暂时就不想再从num为0重新设计了,因此就先这么办了
// 如果全程不点左箭头,是不会出现bug的
if(targetIndex != this.num) {
// 先把上一个点的样式取消
this.num += 1;
let index = this.num;
let preIndex;
if(index === 1){
preIndex = 4
}else{
preIndex = this.num - 2
}
let $points = $('#pointsDiv>span');
$points[preIndex].className = '';
// 然后给这一个点增加样式
this.num = targetIndex - 1
let $list = $('#list');
let per = -(this.num * 100);
$list.css('left', per+'%');
this.num += 1;
index = this.num;
$points[index - 1].className = 'on';
}
}
}
})
</script>
以上就是所有内容。除此以外,在游戏客户端主页面的基础上,再去实现其他标签的功能的话,涉及到更多英雄素材,比较费时,也不容易实现,如果以后有机会再来尝试实现吧。