小程序项目文字与电影(一)

一,首页页面功能

pages-->welcome页面组件

 注意;顶部颜色在welcome.json中配置,

2.每个页面组件都有个影藏的page标签包裹在外头,需要给page也设置颜色

welcome.wxml

<!--pages/welcome/welcome.wxml-->

<view class="container">
    <image class="avatar" src="/images/avatar/1.png"></image>

    <text class="motto">Hello,七月</text>
    <!-- <button></button> -->
  <!-- 自定义button -->
  <view bind:tap="onTap" class="journey-container">
    <text  class="journey">开启小程序之旅</text>
  </view>
</view>

<!-- flex 容器布局 -->
<!-- 路由API -->
<!-- 事件的回调函数 -->

welcome.wxss

.container{
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: #b3d4db;
  /* height: 667px; */
}

.avatar{
  200rpx;
  height:200rpx;
  margin-top:160rpx;
}

.motto{
  margin-top: 100rpx;
  font-size: 32rpx;
  font-weight: bold;
}

.journey-container{
  border: 1px solid #405f80;
   200rpx;
  height: 80rpx;
  border-radius: 5px;
  text-align: center;
  margin-top: 200rpx;
}

.journey{
  font-size: 22rpx;
  color:#405f80;
  line-height: 80rpx;
  font-weight: bold;
}

page{
  background-color: #b3d4db;
}

顶部颜色配置和标题,welcome.json

{
  "usingComponents": {},

  "navigationBarBackgroundColor": "#b3d4db",
  "navigationBarTitleText": "首页",
  "navigationBarTextStyle": "black"
}

全局样式在app.wxss中配置

text{
  color:#666;
  font-size: 24rpx;
}

二,引入lin-ui样式组件库,官网:https://doc.mini.talelin.com/start/

lin-ui 是为小程序设计的组件库

打开小程序的项目根目录,在控制台中执行下面的命令 npm init, 此时,会生成一个package.json文件

安装;npm i lin-ui,

然后用小程序官方IDE打开我们的小程序项目,找到 工具 选项,点击下拉选中 构建npm ,等待构建完成即可。

可以看到小程序IDE工具的目录结构里多出了一个文件夹 miniprogram_npm(之后所有通过 npm 引入的组件和 js 库都会出现在这里),

打开后可以看到 lin-ui 文件夹,也就是我们所需要的组件。

在welcome组件中使用第三方lin-ui组件,使用avatar图片组件

welcome.json配置组件路径,自定义组件名称,即可在页面中使用

{
  "usingComponents": {
    "l-avatar": "/miniprogram_npm/lin-ui/avatar/index"
  },

  "navigationBarBackgroundColor": "#b3d4db",
  "navigationBarTitleText": "首页",
  "navigationBarTextStyle": "white"
}

welcome.wxml直接使用

通过在<l-avatar/>设置size属性来设置头像的大小,单位为rpx

<view class="container">
    <!-- <image class="avatar" src="/images/avatar/1.png"></image> -->
  <l-avatar l-text-class="l-avatar-text" class="l-avatar" placement="bottom" open-data="{{['userAvatarUrl','userNickName']}}" size="180"/>
    <text class="motto">Hello,七月</text>
    <!-- <button></button> -->
  <!-- 自定义button -->
  <view bind:tap="onTap" class="journey-container">
    <text  class="journey">开启小程序之旅</text>
  </view>
</view>

welcome.wxss

.l-avatar{
  margin-top: 160rpx;
}

2.2, 设置启动页组件

第一种;可在app.json全局配置页面组件启动顺序,通过enteyPagePath属性配置

{
  "pages": [
    "pages/welcome/welcome",
    "pages/posts/posts"
  ],
  "entryPagePath": "pages/posts/posts",
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "winxin",
    "navigationBarTextStyle": "white"
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}

第二种,在微信IDE中添加编译模式中配置,建议在这里配置,配置启动页面路劲

2.3.点击开启小程序之旅按钮,跳转到posts页

welcome.wxml,绑定事件bind

<view class="container">
    <!-- <image class="avatar" src="/images/avatar/1.png"></image> -->
  <l-avatar l-text-class="l-avatar-text" class="l-avatar" placement="bottom" open-data="{{['userAvatarUrl','userNickName']}}" size="180"/>
    <text class="motto">Hello,七月</text>
    <!-- <button></button> -->
  <!-- 自定义button -->
  <view bind:tap="onTap" class="journey-container">
    <text  class="journey">开启小程序之旅</text>
  </view>
</view>

welcome.js

注,普通页面跳转到带有tarbar页面,不能用wx.redirect跳转,使用wx.switchTab

Page({

  /**
   * 页面的初始数据
   */
  data: {

  },

  onTap:function(params) {
    wx.switchTab({
      url:"/pages/posts/posts"
    })
  },

底部tabbar配置,app.json配置,官网:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#tabBar

 "tabBar": {
    "borderStyle": "white",
    "selectedColor": "#333333",
    "position": "bottom",
    "color": "#999999",
    "list": [
      {
        "text": "阅读",
        "pagePath": "pages/posts/posts",
        "iconPath": "/images/tab/post.png",
        "selectedIconPath": "/images/tab/post@highlight.png"
      },
      {
        "text": "电影",
        "pagePath": "pages/movies/movies",
        "iconPath": "/images/tab/movie.png",
        "selectedIconPath": "/images/tab/movie@highlight.png"
      }
    ]
  },

三,阅读页面

3.1,pages-->新建post目录--》posts页面

注;1.vertical="true",是为真, vetical="false", 也是为真,引号里头是字符串。 2.如果该属性要为假,加双花括号,里头为js表达式,这个是小程序语法,如果直接加属性,代表为真

2.顶部为轮播图,底部封装成一个post组件,为信息列表页,点击列表页,跳转到详情页

逻辑,每个信息列表页自定义一个post-id属性,跳转到对应id的详情页,跳转时加上id

wx:key="postId", 直接加属性就可以,不需要item.postId

<view>
    <swiper interval="3000" circular vertical="{{false}}" indicator-dots="{{true}}" autoplay="{{true}}">
        <swiper-item>
            <image data-post-id="3" bind:tap="onGoToDetail"  src="/images/bestplayers.png"></image>
        </swiper-item>
        <swiper-item>
            <image data-post-id="0" bind:tap="onGoToDetail" src="/images/lpl.png">
            </image>
        </swiper-item>
        <swiper-item>
            <image data-post-id="4" bind:tap="onGoToDetail"  src="/images/jumpfly.png">
            </image>
        </swiper-item>
    </swiper>



<block wx:for="{{postList}}" wx:key="postId"  wx:for-item="item" wx:for-index="idx">
    <post res="{{item}}"/>
</block>

</view>

posts.js

注,部分data数据导出

var postList = [
  {
    title: "2020LPL夏季赛季后赛观赛指南",
    content: "8月9号,LPL常规赛收官之战结束,在事关季后赛轮次的比赛中关键对局中,SN战胜了FPX,为本赛季常规赛画上句号。进入季后赛的战队依次为,TES、JDG、IG、SN、V5、LGD、WE、FPX",
    imgSrc: "/images/lpl.png",
    reading: 102,
    detail: "8月9号,LPL常规赛收官之战结束,在事关季后赛轮次的比赛中关键对局中,SN战胜了FPX,为本赛季常规赛画上句号。进入季后赛的战队依次为,TES、JDG、IG、SN、V5、LGD、WE、FPX。既有传统四强,又有新崛起的黑马。本文主要是从上路的大改动展开,引发对所有其他的影响。牵一发而动全身,上路一旦回归carry上单版本,对野区和中路的影响是显而易见的。而下路在艾希大砍一刀之后,女警的过于强势,使她只能出现在BAN位上,因此主流下路还是会回归功能性下路英雄。由此,可以对应各位选手的英雄池,对应各支战队的战术储备,漫长的季后赛,考验的就是各队适应版本的能力。",
    collection: 92,
    dateTime: "24小时前",
    headImgSrc: "/images/lpl.png",
    author: "猫是猫的猫",
    date: "Nov 20 2020",
    avatar: "/images/avatar/5.png",
    postId: 0,
    music: {
      url: "http://music.163.com/song/media/outer/url?id=1372060183.mp3",
        title: "空-徐海俏",
        coverImg: "https://y.gtimg.cn/music/photo_new/T002R300x300M000002sNbWp3royJG_1.jpg?max_age=2592000",
    }
},

export {
  postList
}
 

this.setData的数据会加入到page.data中,用于数据绑定

event中可以获取自定义属性postid

import { postList } from "../../data/data.js";

Page({
  /**
   * 页面的初始数据
   */
  data: {},

  /**
   * 生命周期函数--监听页面加载
   * 钩子函数 hook function
   * 顺序
   * 条件渲染 列表渲染
   */
  async onLoad(options) {
    // setData
    // 更新
    // 创建+更新
    // JSON
    // ES6

    // 同步存储
    wx.setStorageSync("flag", 2);
    // 异步存储,返回的是一个promise
    const flag = await wx.getStorage({
      key: "flag",
      // success(value){
      //   console.log(value.data)
      // }
    });

    // flag.then((value)=>{
    //   console.log(value)
    // })

    console.log(flag);

    this.setData({
      postList,
    });
  },

  onGoToDetail(event) {
    // 获取自定义属性
    const pid = event.currentTarget.dataset.postId 
    wx.navigateTo({
      url: "/pages/post-detail/post-detail?pid=" + pid,
    });
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {
    // console.log("onready")
  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {
    // console.log("onshow")
  },

  /**
   * 生命周期函数--监听页面隐藏
   * 条件触发
   */
  onHide: function () {
    // console.log("onhide")
  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {
    // console.log("onunload")
  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {},

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {
    console.log("onreach");
  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {},
});

posts.json中导入子组件post,以及第三方的lin-ui库的组件

{
  "usingComponents": {
      "l-icon":"/miniprogram_npm/lin-ui/icon/index",
      "post":"/components/post/index"
  },
  "navigationBarBackgroundColor": "#C22A1E",
  "navigationBarTitleText":"文与字"
}

新建components-->post-->post

post.wxml

有个假设需求,如果点击整个view父容器,跳转到信息详情页,但是点击图片头像需要跳转到个人中心页,此时该如何做呢

给view父容器绑定bindtap事件,他是可以冒泡的, 给image绑定catchtap事件,他是阻止冒泡的,避免传递给父容器

  <view bind:tap="onGODetail"  data-post-id={{res.postId}} class="post-container">
        <view class="post-author-date">
            <image catch:tap="onMaxImage" class="post-author" src="{{res.avatar}}"></image>
            <text class="post-date">{{res.date}}</text>
        </view>
    

        <text class="post-title">{{res.title}}</text>

        <image class="post-image" src="{{res.imgSrc}}"></image>

        <text class="post-content">{{res.content}}</text>

        <view class="post-like">
            <!-- <image class="post-like-image" src="/images/icon/chat.png"></image> -->
            <l-icon class="post-like-image" color="#666" size="28" name="favor" />
            <text class="post-like-font">{{item.collection}}</text>
            <!-- <image class="post-like-image" src="/images/icon/view.png"></image> -->
            <l-icon class="post-like-image" color="#666" size="32" name="eye" />
            <text class="post-like-font">{{item.reading}}</text>
    

        </view>
    </view>

post.wxss

.post-container{
  display: flex;
  flex-direction: column;
  margin-top: 20rpx;
  margin-bottom: 40rpx;
  background-color: #fff;
  border-top:1px solid #ededed;
  border-bottom:1px solid #ededed;
  padding-bottom: 10rpx;
}

.post-author-date{
  /* margin-top:10rpx;
  margin-bottom: 20rpx;
  margin-left: 10rpx; */
  margin: 10rpx 0 20rpx 10rpx;
  display: flex;
  flex-direction: row;
  align-items: center;
}

.post-author{
  60rpx;
  height:60rpx;
  /* vertical-align: middle; */
}

.post-date{
  margin-left:20rpx;
  font-size: 26rpx;
  /* vertical-align: middle; */
}

.post-title{
  font-size: 34rpx;
  font-weight: 600;
  margin-bottom: 20rpx;
  margin-left: 20rpx;
  color:#333;
}

.post-image{
   100%;
  height:340rpx;
  margin-bottom: 30rpx;
}

.post-content{
  color: #666;
  font-size:28rpx;
  margin-bottom: 20rpx;
  margin-left:20rpx;
  line-height: 40rpx;
  letter-spacing: 2rpx;
}

.post-like{
  display: flex;
  flex-direction: row;
  align-items: center;
  margin-left:20rpx;
}

.post-like-image{
  /* height:32rpx;
  32rpx; */
  margin-right:16rpx;
}
/* html */
.post-like-font{
  margin-right: 40rpx;
  font-size:26rpx;
}

post.js,接收父组件的数据,点击信息栏,跳转到详情页

// components/post/index.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    res:Object
  },

  methods: {
    onGoToDetail(event) {
      // 获取自定义属性id,
      const pid = event.currentTarget.dataset.postId 
      wx.navigateTo({
        url: "/pages/post-detail/post-detail?pid=" + pid,
      });
    },
 

 第二种方法(跳转到详情页),posts页面组件,监听子post组件的自定义posttap事件,当子组件post点击信息栏后,触发posttap事件,然后父组件posts监听到了,再去跳转到详情页(子向父传递数据).保持组件独立性

posts.wxml

<view>
    <swiper interval="3000" circular vertical="{{false}}" indicator-dots="{{true}}" autoplay="{{true}}">
        <swiper-item>
            <image data-post-id="3" bind:tap="onGoToDetail1"  src="/images/bestplayers.png"></image>
        </swiper-item>
        <swiper-item>
            <image data-post-id="0" bind:tap="onGoToDetail" src="/images/lpl.png">
            </image>
        </swiper-item>
        <swiper-item>
            <image data-post-id="4" bind:tap="onGoToDetail"  src="/images/jumpfly.png">
            </image>
        </swiper-item>
    </swiper>



<block wx:for="{{postList}}" wx:key="index"  wx:for-item="item" wx:for-index="idx">
    <!-- 给post绑定posttap事件,监听 -->
    <post bind:posttap = "onGoToDetail" res="{{item}}"/>
</block>

</view>

posts.js

  onGoToDetail(event) {
    // 获取自定义属性id, event.detail.pid,自定义事件传递过来的id
    const pid = event.currentTarget.dataset.postId | event.detail.pid;
    wx.navigateTo({
      url: "/pages/post-detail/post-detail?pid=" + pid,
    });
  },

post.wxml

  <view bind:tap="onTap" class="post-container">
        <view class="post-author-date">
            <image catch:tap="onMaxImage" class="post-author" src="{{res.avatar}}"></image>
            <text class="post-date">{{res.date}}</text>
        </view>

post.js

// components/post/index.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    res:Object
  },

  /**
   * 组件的初始数据
   */
  data: {

  },

  /**
   * 组件的方法列表
   * 组件的开发者不应该决定
   * 点击之后做什么事情 不应该
   * 组件的使用者
   * 自定义事件
   */
  methods: {
    onTap(event){
      const pid = this.properties.res.postId
      // 触发自定义事件posttap,传递pid
      this.triggerEvent('posttap',{
        pid
      })
    },
  }
})

跳转到详情页面post-detail

 注,传递过来的postid,在onload函数中options中获取

pages--》post-detail->post-detail

post-detail.wxml

<view class="container">
    <image class="head-image" src="{{postData.headImgSrc}}"></image>
  播放图片 <image wx:if="{{!isPlaying}}" bind:tap="onMusicStart" class="audio" src="/images/music/music-start.png" />
   停止图片  <image bind:tap="onMusicStop" wx:else class="audio" src="/images/music/music-stop.png" /> <!-- <image bind:tap="onMusic" class="audio" src="{{isPlaying?'/images/music/music-stop.png':'/images/music/music-start.png'}}" /> --> <view class="author-date"> <image class="avatar" src="{{postData.avatar}}"></image> <text class="author">{{postData.author}}</text> <text class="const-text">发表于</text> <text class="date">{{postData.dateTime}}</text> </view> <text class="title">{{postData.title}}</text> <view class="tool"> <view class="circle"> <image wx:if="{{collected}}" bind:tap="onCollect" class="" src="/images/icon/collection.png"></image> <!-- 未收藏图片 --> <image wx:else bind:tap="onCollect" class="" src="/images/icon/collection-anti.png"></image> <image bind:tap="onShare" class="share-img" src="/images/icon/share.png"></image> </view> <view class="horizon"></view> </view> <text class="detail">{{postData.detail}}</text> </view>

post-detail.wxss

.container{
  display: flex;
  flex-direction: column;
}

.head-image{
   100%;
  height: 460rpx;
}

.author-date{
  display: flex;
  flex-direction: row;
  align-items: center;
  margin-top: 20rpx;
  margin-left: 30rpx;
}

.avatar{
   64rpx;
  height: 64rpx;
}

.author{
  font-size: 30rpx;
  font-weight: 300;
  margin-left:20rpx;
  color:#666;
}

.const-text{
  font-size: 24rpx;
  color: #999;
  margin-left: 20rpx;
}

.date{
  font-size: 24rpx;
  margin-left: 30rpx;
  color:#999;
}

.title{
  margin-left: 40rpx;
  font-size: 36rpx;
  font-weight: 700;
  margin-top: 30rpx;
  letter-spacing: 2px;
  color:#4b556c;
}

.tool{
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin-top:20rpx;
}

.circle{
  display: flex;
   660rpx;
  flex-direction: row;
  justify-content: flex-end;
}

.circle image{
   90rpx;
  height: 90rpx;
}

.share-img{
  margin-left: 30rpx;
}

/* 主轴 交叉轴 */

.horizon{
   660rpx;
  height: 1px;
  background-color: #e5e5e5;
  position: absolute;
  z-index: -99;
}


.detail {
  color: #666;
  margin-left: 30rpx;
  margin-top: 20rpx;
  margin-right: 30rpx;
  line-height: 44rpx;
  letter-spacing: 2px;
}

/* 230 */
.audio{
   102rpx;
  height: 110rpx;
  position: absolute;
  left:50%;
  margin-left: -51rpx;
  top:185rpx;
  opacity: 0.6;
}

post-detail.js

逻辑分析

收藏功能

1.,点击收藏图片,将每个文章的id号和收藏状态组成一个对象,保存在storage中,

2.当页面加载onload函数中,读取storage中的信息,然后通过postid文章id判断是否收藏过该文章

音乐播放功能

1.在页面加载时,就创建背景音乐实例,监听背景音乐的播放和暂停

2.当点击播放音乐的图片,背景音乐播放,当点击暂停音乐图片,背景音乐暂停

3.如果在详情页,背景音乐在播放,退回详情页(背景音乐还在播放状态的),在次进来时,我们发现图片状态不是正在播放状态中,

此时我们需要在app.js中定义全局变量gIspalyingMusic(背景音乐播放状态),gIsplayingPostId(文章id),来判断isplaying的状态

全局变量修改后,但在小程序再次启动后,他的变量会再次变成初始值,不会已经改变的值。

分享功能,点击分享图片,弹出分享框

app.js

App(
  {
    // 小程序启动的函数
    onLaunch(){
      console.log("小程序启动")
    },
    gIsPlayingMusic:false,
    gIsPlayingPostId:-1,

  }
)
// pages/post-detail/post-detail.js
import {postList} from '../../data/data.js'
// 获取app.js的全局变量
const app = getApp()

Page({

  /**
   * 页面的初始数据
   */
  data: {
    postData:{},
    // 收藏的状态
    collected:false,
    // 播放状态,图片状态
    isPlaying:false,
    // 保存文章id
    _pid:null,
    _postsCollected:{},
    // BackgroundAudioManager 实例
    _mgr:null
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    // 
    const postData = postList[options.pid]
    this.data._pid = options.pid
    // 页面加载后,从缓存读取收藏状态
    const postsCollected = wx.getStorageSync('posts_collected')
    console.log(postsCollected)
    // 判断缓存是否有状态,有的话从缓存读取的状态赋值给页面的data
    if(postsCollected){
      this.data._postsCollected = postsCollected
    }
    
    let collected = postsCollected[this.data._pid]
    // 如果第一篇文章收藏过,第二篇文章进来未收藏过,需要判断
    if(collected === undefined){
      // 如果undefined 说明文章从来没有被收藏过
      collected = false
    }

    this.setData({
      postData,
      collected,
      isPlaying: this.currentMusicIsPlaying()
    })
    // 页面加载,创建BackgroundAudioManager 实例
    const mgr = wx.getBackgroundAudioManager()
    this.data._mgr = mgr
    // 监听背景音乐播放函数
    mgr.onPlay(this.onMusicStart)
    // 监听背景音乐停止函数
    // mgr.onStop(this.onMusicStop)
    // 监听背景音乐暂停函数
    mgr.onPause(this.onMusicStop)
  },
  
  // 记录每篇文章的歌的播放状态,
  currentMusicIsPlaying(){
    if(app.gIsPlayingMusic && app.gIsPlayingPostId === this.data._pid ){
      return true
    }
    return false
  },

  // 开始播放
  onMusicStart(event){
    const mgr = this.data._mgr
    // mgr.onPlay(()=>{
    //   console.log(123)
    // })
    const music = postList[this.data._pid].music
    // 只要赋值了src ,就会播放
    mgr.src = music.url
    mgr.title = music.title
    mgr.coverImgUrl = music.coverImg
    // 改变背景音乐的播放状态
    app.gIsPlayingMusic = true
    app.gIsPlayingPostId = this.data._pid
    
    this.setData({
      isPlaying:true
    })
  },

  // 播放停止
  onMusicStop(event){
    const mgr = this.data._mgr
    // 背景音乐暂停
    mgr.pause()
    app.gIsPlayingMusic = false
    app.gIsPlayingPostId = -1
    this.setData({
      isPlaying:false
    })
    // 音乐停止 - start
    // 音乐播放 - stop
  },

  // 点击分享按钮
  async onShare(event){
    // 菜单弹出
    const result = await wx.showActionSheet({
      itemList: ['分享到QQ','分享到微信','分享到朋友圈']
    })
    // 可以获取到itemList索引
    console.log(result)
  },

  // 点击收藏按钮
  async onCollect(event){
    // 这样会覆盖原来的数据,会新开辟空间建对象
    // const postsCollected ={}
    // 一个对象保存多个状态
    const postsCollected = this.data._postsCollected
// 保存的value值,点击一次取反
    postsCollected[this.data._pid] = !this.data.collected


    this.setData({
      collected:!this.data.collected
    })
    // 将文章的Id和状态保存,postsCollected
    // {
    //   1::true
    // }
    wx.setStorageSync('posts_collected',postsCollected)
    // 弹出框提示
    wx.showToast({
      title: this.data.collected?'收藏成功':'取消收藏',
      duration: 3000
    })

    // 模态对话框,返回的是一个promise
    // const result =await wx.showModal({
    //   title:'是否收藏文章'
    // })
    // if(result.confirm){

    // }
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  }
})
原文地址:https://www.cnblogs.com/fsg6/p/14450679.html