谁是最强王者?云开发与云缨一起上号

2,464 阅读10分钟

1111.png

仿王者荣耀小程序-攻略页面

前言

"上号上号!!!你在干什么呢"
无论在何时何地身边总是会响起这熟悉的话语,这貌似在大学生生活中很普遍。
可惜可惜,本人什么游戏都玩,什么都不精,那我就必须在学习、工作方面努力了。

QQ图片20210818163048.jpg 作为一名在校生,小程序是一种对全栈项目最好的练习,也是一个必备的知识点。正好王者荣耀小程序是一个不错的小程序,其中也有一个攻略页面。

开冲! 16C343A9.png

  • 更新记录 2021/8/22更新新闻点赞效果

IDE

总体架构

  • 该项目是微信小程序云开发项目,使用wxml+wxss+js+component+云数据库结合,命名格式采用BEM格式。
|-wzry 项目名 
    |-cloudfunctions 云函数模块
    |-miniprogram 项目模块 
        |-components 自定义组件 
            |-herosType 英雄信息排列组件
            |-navBarTitle 自定义topbar组件 
            |-newsPoster 新闻文章组件
        |-data 自定义数据
            |-allHeros.js 封装获取云数据
            |-acticleData.js 用户文章数据
            |-circleList.js 新闻文章数据和新闻类型列表
            |-herosMap.js 英雄图鉴中最新英雄
            |-herosRank.js 3个上分英雄
            |-segment.js 段位排行
            |-upScoreData.js 上分页面数据
        |-miniprogram_npm  构建有赞组件库 
        |-pages 页面
            |-introduction 攻略主页面
            |-upScore 上分宝典页面
            |-heros 全部英雄页面 
            |-search 搜索页面 
        |-style 样式库
            weui.wxss   weui样式库
        app.js 全局js 
        app.json 全局json配置 
        app.wxss 全局wxss

其中的data文件下全为固定数据,采用module.exports暴露,在需要使用的地方,可以使用const {getHeroOrderByType} = require("../../data/allHeros")进行引入,这里{}使用的是es6 的解构,当然也可以直接用一个常量接受使用。

关于云数据库,我采用的是csv格式进行导入小程序云数据库表中,csv格式制作,可以先创建一个excel文件进行编辑数据,然后另存为csv格式,需要注意的一点,文件中的中文需要转换为utf-8编码,可以在另存为csv格式前,先修改文件格式为txt,然后通过vscode进行修改编码格式,接着在改回excel格式,最后另存为csv格式。

image.png

项目规划

将项目的架构搭建后,分析各页面的功能,思考相互页面之间的联系,清楚如何进行动态数据绑定,如何获取数据,各页面有哪些细节需要注意等等。

项目解构

以下就是我的所有页面

image.pngimage.png
image.pngimage.png
image.pngimage.png
image.png

tabbar

由于微信自带的tabbar格式就可以满足王者荣耀小程序的tabbar就没有再去自定义tabbar,而是直接使用默认格式了。写在app.json中,pagePath只有一个页面,其他页面由团队其他人负责,我这就没有修改成他们的。

 "tabBar": {
    "selectedColor": "#a68f5c",
    "borderStyle": "white",
    "backgroundColor": "#fff",
    "list": [
      {
        "text": "主页",
        "iconPath": "images/tabbar/home.png",
        "pagePath": "pages/Introduction/Introduction",
        "selectedIconPath": "images/tabbar/home_on.png"
      },
      {
        "text": "精选",
        "iconPath": "images/tabbar/jingxuan.png",
        "pagePath": "pages/Introduction/Introduction",
        "selectedIconPath": "images/tabbar/jingxuan_on.png"
      },
      {
        "text": "攻略",
        "iconPath": "images/tabbar/hero.png",
        "pagePath": "pages/Introduction/Introduction",
        "selectedIconPath": "images/tabbar/hero_on.png"
  
      },
      {
        "text": "我",
        "iconPath": "images/tabbar/wo.png",
        "pagePath": "pages/Introduction/Introduction",
        "selectedIconPath": "images/tabbar/wo_on.png"
      }
    ]
  },

精选主页

  • 对页面分析

topbar

最上面的是topbar,王者荣耀小程序和默认的topbar格式不一样,所有我选择自定义组件, 这里取名字有点错误,当时以为topbar也属于navBar的一种。o(╥﹏╥)o

<!-- introduction.wxml -->
<navBar isback="{{false}}" title="王者荣耀"></navBar>
<!-- navBarTitle.wxml   组件名为navBar-->
<view class="topbar-wrapper">
  <view class="topbar-body">
    <view wx:if="{{isback}}"  bindtap="_goBack" class="topbar-back">
      <van-icon name="arrow-left"  />
      <view class="verticalLine"></view>
    </view>
    <view class="topbar-title">{{title}}</view>
  </view>
</view>

这里让王者荣耀四个字和右边胶囊平齐没有使用类似wx.setNavigationBarTitle这样的接口,而是直接使用csspadding-top一点一点调。从父组件中传入两个参数,isback控制是否显示回退按钮(<),title为页面标题,_goback函数控制回到上一个页面,使用的是 wx.navigateBack API。

中间部分

新英雄排列我做成了一个组件,因为在全部英雄中复用了两次。但组件是为全部英雄分类而做的,新英雄排列没有进行修改。

<view class="heros__type">{{type}}</view>
<view class="heros__headImg heros-type_line">
    <view class="heros" wx:for="{{arr}}" wx:key="index">
            <image src="{{item.img_url}}"></image>
            <text>{{item.name}}</text>
    </view>
</view>

其结构很简单,使用了弹性布局,开启了自动换行flex-wrap: wrap;,在css布局中对于各英雄之间的空白,我使用的是margin-right,但给第5个.heros:nth-child(5)设置为0。

上分排行榜: 给整个盒子绑定了一个点击事件跳转到上分界面,同样使用的是弹性布局,使用了3次弹性布局,整个大盒子是一个弹性盒,左边的文字也是一个,右边图片也是,图片右边的箭头是用伪类来做的,通过transform: rotate(45deg);旋转45度达到效果

.heros-upImg::after{
  content: "";
  width: 16rpx;
  height: 16rpx;
  position: absolute;
  right: -16rpx;
  border-top:1px solid grey ;
  border-right:1px solid grey;
  transform: rotate(45deg);
}

点击全部英雄可以跳转到全部英雄界面

用户文章和直播推荐

文章类型有三种情况,我都使用假数据完成了效果

  1. 文章没有图片
  2. 文章只有一张图片
  3. 文章有3张及以上的图片

其中我将有图片和无图片分为两种情况书写(建议可以看看注释)

     <!-- 带图片的文章 -->
 <view class="acticle" wx:for="{{article}}" wx:key="id" wx:for-item="act"> 
      <view class="title">{{act.title}}</view>
       <!-- 判断文章的类型,有两类,一类是文章,一类是直播 -->
     <block wx:if="{{act.type=='pic'}}">
         <!--  文章的内容  -->
        <view class="content">{{act.content}}</view>
         <!-- 判断文章的图片是否大于1-->
        <block wx:if="{{act.imgUrl.length>1}}">
          <view class="imgList">
           <!--点击事件:开启遮罩层,效果为图片放大 -->
            <view class="img" wx:for="{{act.imgUrl}}" wx:key="id" catch:tap="onClickShow" data-id="{{act.id}}" data-index="{{item.id}}">
               <image src="{{item.src}}" mode='aspectFit'  />
                <!-- 判断图片的数量 -->
              <block wx:if="{{index+1>=3}}">
                <view class="img_total">共{{act.imgUrl.length}}张</view>
              </block>
            </view>
          </view>
        </block>
         <!-- 图片为1张的情况  和上边不同之处  mode='widthFix' widthFix:缩放模式,宽度不变,高度自动变化,保持原图宽高比不变-->
        <block wx:else>
            <view class="img__one" wx:for="{{act.imgUrl}}" wx:key="id" data-id="{{act.id}}"  data-index="{{item.id}}"  catch:tap="onClickShow" >
              <image src="{{item.src}}" mode='widthFix' />
            </view>
         </block>
         
         <!-- 遮罩层 -->
                             <!-- custom-style为自定义样式 -->
        <van-overlay show="{{ show }}" bind:click="onClickHide"  custom-style="background-color:black" lock-scroll="flase" z-index="200">
         <!-- 使用transparent 为了让swiper 自带的指示点隐藏 -->
          <swiper indicator-dots="false"  indicator-color="transparent" indicator-active-color="transparent" 
          class="overlay-swiper" current="{{current}}">
            <swiper-item  class="overlay-swiper-item" wx:for="{{nowArry}}" wx:key="id">
              <view class="swiper-current">{{item.id}}/{{nowArry.length}}</view>
              <view class="overlay-img">
                <image src="{{item.src}}" mode='aspectFit' />
              </view>
            </swiper-item>
          </swiper>
        </van-overlay>
     </block>
 </view>

image.png

  • 文字部分,使用了文字超出省略,如果是文字是单行省略,则可以使用white-space: nowrap;overflow: hidden;text-overflow:ellipsis;如果是固定行数省略,则可以使用 overflow: hidden;display: -webkit-box;-webkit-line-clamp: 2; -webkit-box-orient: vertical;

image.png

  • 图片是排列用的是flex布局,为了获取到图片中件部分,在这里还使用了定位,left: 50%;top:50%;transform: translate(-50%,-50%);做到绝对居中。

遮罩层演示.gif

  • 有图片的文章设置了有赞组件遮罩层,通过点击事件修改show数据为true将图片放大,在组件基础上添加可以进行滑动图片,缺点是没发将右上角胶囊覆盖住。

  • 遮罩层如何完成交互的呢~~~ 我给每张图片的父容器绑定了一个捕获事件(防止冒泡)onClickShow,然后使用data-id="{{act.id},data-index="{{item.id}}将数据中每篇文章的唯一标识id和每个图片的唯一标识id传入函数中(id为文章标识,index为图片标识),可以通过函数的默认参数event,打印event参数可以得到下图。传过来的id放在event.currentTarget.dataset中,显示图片和滑动图片并不难,但要保证所选的文章和图片要保持一致,(如最上方的数字)这是就需要用到传入的数据,通过this.setData方法将index-1作为swipercurrent属性、id-1作为文章数组的下标。(这里减一的原因是我数据中都是从1开始的)。

image.png

  <!-- 带直播的文章 -->
 <block wx:if="{{act.type=='video'}}">
     <video poster="{{act.image}}" objectFit="cover" duration="{{act.duration_raw}}" src="{{act.video}}"></video>
        <view class="video_bottom">
          <view class="video__zhubo">
              <view class="video__avatar">
                  <image src="{{act.avatar}}"></image>
              </view>
              <text>{{act.name}}</text>
          </view>
          <view class="video_type">{{act.desc}}</view>
     </view>
 </block>
  • 直播推荐:这里本来是点击跳转到另一个小程序的,但我不知道其他小程序的appid,所有就放弃了跳转而是使用了一个视频代替,使用了video标签

上分宝典页面

值得一讲的地方就是这里也使用了遮罩层,同样 这里还使用了radio单选框组件,对于交互功能,也通过引入data中segment.js段位数据,这里点击相应段位就会切换段位,通过对radio绑定点击事件传入name值修改data{}展示到页面的相关数据。

|image.png| |image.png|

最下面使用了一个小动画,使用方法为css动画@keyframes,修改定位中的right起到移动的效果,期间慢慢的修改字体图标opacity透明度

箭头动画.gif

最强上分榜

最强上分榜、输出狂魔榜、开始上分都是上分宝典页面的一部分,写在siwper中的swiper-item。两个页面布局是一样的,写两个是为了展示展示。他们的布局很普通,使用了大量的弹性布局,然后在讲数据引入,使用它主要是因为好用,当然使用其他布局手段也可以。

全部英雄

image.png

  • 搜索框使用的是weui自带的搜索框,做了一点修改,点击搜索框会跳转到搜索页面进行搜索,减少全部英雄页面的负担。

  • 该页面重点是下面的英雄分类,使用了云数据库。

  1. 建立好数据表
  2. 引入数据表,进行筛选
  3. 将筛选好的数据分类好分别插入wxml

image.png

  • 建立数据表,我在开头已经写了方法,可以使用导入的方式,英雄数据.csv,也可以自己一条一套记录添加
  • 引入数据表,筛选分类,我将代码封装在data文件下的allHeros.js,注释很重要!!!!
//引入云数据库
const db = wx.cloud.database()
const allHeros = db.collection("allHeros")
// 将db.command设置为一个字符,方便使用
const _ = db.command
// 设置每次获取数据记录的限制20条
const limit = 20
// 将获取数据封装为一个函数,然后只需要暴露函数就可
const getHeroOrderByType = async (type) => {

    // where  对云数据库进行筛选
  let {data} = await allHeros.where(_.or([{   // _.or()或语句,不同字段不同值,符合一个就ok
      hero_type: _.eq(type)
    },
    {
      hero_type2: _.eq(type)
    }
  ])).orderBy("hero_id", "desc").get()     // orderBy()  排序,desc表示降序,asc表示升序
  
  // 因为知道所有类型的数据都不超过40条 所以只要重复一次
  // 如果超过40条,可以修改判断语句改为  while(data.length==limit*index)   需要定义一个变量index储存筛选次数
  if(data.length ==20){
    let arr = await allHeros.where(_.or([{
      hero_type: _.eq(type)
    },
    {
      hero_type2: _.eq(type)
    }
  ])).skip(limit).orderBy("hero_id", "desc").get();
      data = [...data,...arr.data]   // 将两次查询到的结果合并为一个数组
  }
  //返回数组
  return data
}

const allOccupation = {

  getHeroOrderByType
}

module.exports = allOccupation
  • 在heros.js 引入封装好的筛选js,使用es7 的asyncawait 来处理异步
const {getHeroOrderByType} = require("../../data/allHeros")
const type=["战士","法师","坦克","刺客","射手","辅助"]
// 设置数据封装为函数
async getHeroType(){
    this.setData({
        zhanshi:await getHeroOrderByType(type[0]),
        fashi:await getHeroOrderByType(type[1]),
        tanke:await getHeroOrderByType(type[2]),
        cike:await getHeroOrderByType(type[3]),
        sheshou:await getHeroOrderByType(type[4]),
        fuzhu:await getHeroOrderByType(type[5]),
    })
},
onLoad(options) {
    this.getNewHeros()
    this.getHeroType()
    this.getnewsPost()
},

赋值后的数组(zhanshi...)可以直接在wxml通过wx:for="{{zhanshi}}"使用

新闻点赞

点赞.gif

上图也使用了云数据库,点击修改云数据库中的support(点赞数)和supported(是否点赞过)

  • 上图使用newsPoster组件,交互功能:点击点赞数加1,颜色变红,在点减1,颜色恢复 将更新数据代码封装在allHeros.js,先在父组件页面引入allHeros.js获取新闻数据,然后传入子组件,通过子组件点击后将数据传入父组件,接着在父组件页面引入allHeros.js的更新数据函数:使用where筛选出需要修改的记录,然后使用update更新数据,在调用更新数据函数
//allHeros.js
// 获取数据
const getNewsPost = async ()=>{
  let {data} = await newsPost.orderBy("id","asc").get()
  return data
}
// 更新数据,修改点赞数
const updataNewsPost= async (id,support,supported)=>{
  await newsPost.where({//根据自己设置的id筛选,也可以通过别的唯一字段筛选
    id:_.eq(id)
  }).update({ 
    data: {     //需要修改的数据
      support:support,
      supported:!supported
    }
  })
}
// 子组件
  methods: {
    onSupport(e){
      let {support,id,supported}= e.currentTarget.dataset
      // 将数据传入父组件
      this.triggerEvent("support",{support,id,supported})
    }
  }
// 父组件页面js引入
const {updataNewsPost} = require("../../data/allHeros")
// 修改点赞数
async onSupport(e){
    //获取数据
    let {id,supported,support} = e.detail
    //判断是否点赞过   support在数据库中默认值为false
    if(!supported){
        //点赞+1
        await updataNewsPost(id,support+1,supported)
    }else{
        //点击-1
        await updataNewsPost(id,support-1,supported)
    }
    //重新让页面获取所有数据,达到渲染页面效果
    this.getnewsPost()
},

踩坑系列

image.png

  • 自定义topbar的时候回会导致整个页面会往上移动,需要设置topbar的高度,同样也要修改页面相关的高度。
  • 遮罩层确定点击的当前文章以及滑动的图片,需要区分它们的唯一标识,this.setData要对应好数据。
  • 只有一张图片的文章,当时用css搞了很久,一直都不对劲,一直查找资料,才发现修改image中mode属性就ok,心态直接起飞。
  • 云数据库筛选的时候,let {data} 一定要加{},因为函数返回的是一个promise对象,对象之中才是我们想要的数组。

个人体会

写完这些页面,个人感觉挺累的,虽然我负责的这些页面比较简单,交互功能较少,(云函数没用上o(╥﹏╥)o),第一次写小程序的精力,希望以后越来越好。

image.png

源码

源码 欢迎大家给项目一个star, 鼓励开源更多的项目, 谢谢。