微信小程序实现星巴克用星说效果

最近微信小程序应用比较多,前段时间看到了星巴克的星巴克用星说小程序,闲暇时间就把该小程序的效果实现了一下,在此仅作功能介绍,实际效果可以微信小程序搜索:星巴克用星说,查看官方效果。先展示一下最终界面效果:

界面分析

通过上面的效果图,本示例包含两个界面,第一个界面产品列表界面,包含头部Banner,中间的产品列表和底部的Bottom,第二个界面产品详情界面,包含顶部大图、横向滑动列表、商品列表和商品详情弹窗,接下来分别实现相应的界面:

首界面实现

1.布局文件

首先我们要编写首界面的布局文件grid.wxml:

<swiper indicator-dots="{{indicatorDots}}"
  autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}" indicator-color='#dbdbdb' indicator-active-color='#00ae61'>
  <block wx:for="{{imgUrls}}">
    <swiper-item>
      <image src="{{item.toppic}}" class="slide-image" bindtap='toBannerInfo' data-index='{{index}}' width="355" height="150"/>
    </swiper-item>
  </block>
</swiper>

<view class="pro-body">

  <text class='pro-title'>咖啡+祝福 即刻表心意</text>

  <view class='items-list' >
    <block class='pro-item' wx:for="{{proList}}">
      <view class='pro-bodydiv' bindtap='toProListInfo' data-index='{{index}}'>
        <image class='pro-img' src='{{item.toppic}}'/> 
         <view class='pro-name'>{{item.name}}</view> 
      </view>
    </block>
  </view>

  <view class='pro-bottom'>
    <view>
      <view bindtap='toProHistory'>
        <image class='pro-pay' src='/images/img_pay.jpg'/>
      </view>
      <text class='pro-history'>购买历史</text>
    </view>
  </view>

</view>

在该布局文件中,使用微信小程序swiper控件实现首页顶部轮播图,通过该控件的相关属性,设置轮播图自动播放,显示时间,角标颜色等属性信息。

2.布局属性

编辑grid.wxss文件,用于设置grid.wxml中相关控件的属性信息。

swiper {
  width: 100%;
  height: 420rpx;
}

swiper image {
  width: 100%;
  height: 100%;
  display: block;
}

.pro-body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  padding-top: 60rpx;
  background: #f9f9f9;
}

.pro-title {
  font-size: 30rpx;
  color: #333333;
  margin-left: 40rpx;
}

.items-list {
  width: 100%;
  display: flex;
  flex-flow: row wrap;
  align-content: flex-start;
  overflow: hidden;
  padding-left: 2%;
  padding-right: 2%;
}

.pro-item {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}

.pro-bodydiv {
  flex:0 0 44%;
  height: 280rpx;
  margin-top: 5%;
  margin-left: 2%;
  margin-right: 2%;
  border-radius: 20rpx;
  background: white;
  text-align: center;
}

.pro-img {
  left: 0px;
  top: 0px;
  width: 98%;
  height: 220rpx;
  margin-top: 3rpx; 
  border-radius: 20rpx
}

.pro-name {
  font-size: 23rpx;
  color: #666666;
  position: relative;
  width: 100%;
  top: 5rpx;
  align-content: center;
  text-align: center;
}

.pro-bottom {
  width: 100%;
  height: 100%;
  margin-top: 80rpx;
  margin-bottom: 150rpx;
  text-align: center;
}

.pro-pay {
  width: 130rpx;
  height: 130rpx;
  margin-bottom: 10rpx;
}

.pro-history {
  font-size: 33rpx;
  color: #0ca862;
  text-decoration: underline; 
}

其中,为实现Grid布局需设置items-list属性值display: flex;flex-flow: row wrap; align-content: flex-start;

3.JS文件

编辑grid.js用于控件的数据处理:

// pages/grid/grid.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    imgUrls: [
      {
        "toppic": "/images/img_bann1.jpg",
        "name": "玩出我的夏天"
      }, {
        "toppic": "/images/img_bann2.jpg",
        "name": "你真棒"
      }, {
        "toppic": "/images/img_bann3.jpg",
        "name": "毕业季"
      }, {
        "toppic": "/images/img_bann4.jpg",
        "name": "干杯"
      }, {
        "toppic": "/images/img_bann5.jpg",
        "name": "求抱抱"
      }, {
        "toppic": "/images/img_bann6.jpg",
        "name": "宝贝谢谢你"
      }
    ],
    indicatorDots: true,
    autoplay: true,
    interval: 5000,
    duration: 1000,
    proList:[
      {
        "toppic": "/images/img_bann1.jpg",
        "name": "玩出我的夏天"
      }, {
        "toppic": "/images/img_bann2.jpg",
        "name": "你真棒"
      }, {
        "toppic": "/images/img_bann3.jpg",
        "name": "毕业季"
      }, {
        "toppic": "/images/img_bann4.jpg",
        "name": "干杯"
      }, {
        "toppic": "/images/img_bann5.jpg",
        "name": "求抱抱"
      }, {
        "toppic": "/images/img_bann6.jpg",
        "name": "宝贝谢谢你"
      }, {
        "toppic": "/images/img_bann7.jpg",
        "name": "有你真好"
      }, {
        "toppic": "/images/img_bann8.jpg",
        "name": "宝贝 生快"
      }, {
        "toppic": "/images/img_bann9.jpg",
        "name": "为你点赞"
      }, {
        "toppic": "/images/img_bann10.jpg",
        "name": "休息一夏"
      }
    ]
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    this.getImgUrls();
  },

  getImgUrls: function () {
    var self = this;
    wx.request({
      url: '你的服务器地址...',
      method: "GET",
      success(res) {
        console.log(res)
        self.setData({
          // imgUrls: res.data.bannerList,
          // proList: res.data.centerList,
          // bottomItem: res.data.bottom
        })
      }
    })
  },

  toBannerInfo: function (e) {
    var index = e.currentTarget.dataset.index;
    var imgUrls = this.data.imgUrls;
    var name = imgUrls[index].name;
    var toppic = imgUrls[index].toppic;
    // wx.showToast({
    //   title: name + '',
    // })
    wx.navigateTo({
      url: '/pages/gridinfo/gridinfo?name=' + name + '&toppic=' + toppic,
    })
  },

  toProListInfo: function (e) {
    var index = e.currentTarget.dataset.index;
    var proList = this.data.proList;
    var name = proList[index].name;
    var toppic = proList[index].toppic;
    // wx.showToast({
    //   title: name + '',
    // })
    wx.navigateTo({
      url: '/pages/gridinfo/gridinfo?name=' + name + '&toppic=' + toppic,
    })
  },

  toProHistory: function (e) {
    wx.showToast({
      title: '购买历史',
    })
  },

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

  },

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

  },

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

  },

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

  },

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

  },

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

  },

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

  }
})

由于没有服务器相关数据,本示例使用了本地数据来实现,如果有网络数据可直接修改wx.request()方法中的url地址即可,其中

indicatorDots: true,
autoplay: true,
interval: 5000,
duration: 1000,

为顶部轮播图的相关属性信息,

wx.navigateTo({
  url: '/pages/gridinfo/gridinfo?name=' + name + '&toppic=' + toppic,
})

为界面跳转,同时向gridinfo界面传递name和toppic参数。
至此,第一个界面首界面基本实现。

详情界面实现

1.布局文件

在Page目录下创建一个新的目录,命名为gridinfo,在该目录下创建一个Page,命名为gridinfo,编辑gridinfo.wxml布局文件,该布局标题为上个界面的商品名称,该布局文件包含一个顶部图片布局,横向图片列表布局,纵向的商品列表布局,悬浮的底部布局和商品详情的弹窗布局,布局文件如下:

<!--pages/gridinfo/gridinfo.wxml-->
<view class='pro-top'>
  <image src='{{toppic}}'/>
</view>

<view class='pro-mid'>
<scroll-view class='pro-scroll' scroll-x='true' style="white-space: nowrap; display: flex">
  <block class='pro-lists' wx:for="{{proList}}">
    <view class='pro-item' style='display: inline-block' bindtap='toProListInfo' data-index='{{index}}'>
      <image class="{{item.showView?'pro-itempic_show':'pro-itempic_hide'}}" src='{{item.pic}}'/>
      <icon class="{{item.showView?'pro-selectpic_show':'pro-selectpic_hide'}}" type='success' size='20' color='#03a964'/> 
    </view>
  </block>
</scroll-view>
</view>

<view class='pro-selecttitle'><text>选择商品</text></view>

<view class='pro-productlists'>
  <view class='pro-productitem' wx:for="{{productList}}">
    <image class='pro-productpic' bindtap='toProductInfo' data-index='{{index}}' src='{{item.pic}}'/>
    <text class='pro-productname' bindtap='toProductInfo' data-index='{{index}}'>{{item.name}}</text>
    <text class='pro-productprice' bindtap='toProductInfo' data-index='{{index}}'>{{item.price}}</text>
    <image class='pro-productadd' src='/images/img_add.jpg' bindtap='toProductAdd' data-index='{{index}}'/>
  </view>
</view>

<view class='pro-copyright'>
  <view class='pro-copyrightitem'>
    <text class='pro-copyrightmsg' bindtap='toCopyrightMsg'>使用须知</text> 
    <text class='pro-copyrightprivate' bindtap='toCoprightPrivate'>隐私权政策</text>
  </view>
</view>

<view class='pro-bottominfo'>
  <text class='pro-bottomprize'>0份礼品</text>
  <view class='pro-bottompriceinfo'>
    <text class='pro-bottomprice'></text>
    <text class='pro-bottomprice2'>0.00</text>
  </view>
  <view class='pro-bottompay' bindtap='toPayInfo'>
    <text>购买</text>
  </view>
</view>

<view class="pro-dialog {{isShowDialog?'isDialogShow':'isDialogHide'}}" >
  <view class='pro-dialogdiv'>
    <view class='pro-dialog-topdiv'/>
    <view class='pro-dialog-botdiv'/>
    <view class='pro-dialog-content'>
      <image class='pro-dialog-pic' src='{{dialogPic}}'/>
      <image class='pro-dialog-close' src='/images/img_gridinfo_close.jpg' bindtap='toDialogClose'/>
      <text class='pro-dailog-name'>{{dialogName}}</text>
      <text class='pro-dailog-price'>{{dialogPrice}}</text>
      <view class='pro-dialog-line'></view>
      <view class='pro-dialog-scroll'>

      </view>
    </view>
  </view>
</view>

其中,横向布局通过scroll-view控件,设置scroll-x=’true’属性来实现,因为要实现点击横向布局中的Item自动切换顶部图片地址,且被点击的Item添加边框和选择按钮,此处通过

class="{{item.showView?'pro-itempic_show':'pro-itempic_hide'}}"

三元运算符实现两种布局属性的切换。

2.布局属性

编辑gridinfo.wxss文件,用于设置布局文件的相关属性。

/* pages/gridinfo/gridinfo.wxss */
.pro-top {
  width: 100%;
  height: 330rpx;
  margin: 0;
  padding: 0;
  text-align: center
}

.pro-top image {
  width: 75%;
  height: 100%;
  border-radius: 20rpx;
}

.pro-mid {
  width: 100%;
  height: 110rpx;
  padding: 0;
  margin-top: 35rpx;
  text-align: center;
}

.pro-scroll {
  width: 92%;
  height: 140rpx;
  padding: 0;
  margin-left: 4%;
}

.pro-item {
  width: 180rpx;
  height: 100%;
  text-align: center;
}

.pro-itempic_show {
  width: 164rpx;
  height: 100rpx;
  margin-top: 20rpx;
  border-radius: 16rpx;
  border: 3rpx solid #03a964;  
}

.pro-itempic_hide {
  width: 164rpx;
  height: 100rpx;
  margin-top: 20rpx;
  border-radius: 16rpx;
  border: 0;  
}

.pro-selectpic_show {
  position: absolute;
  width: 40rpx;
  height: 40rpx;
  margin-left: 147rpx;
  top: 10rpx;
  display: block;
}

.pro-selectpic_hide {
  position: absolute;
  width: 40rpx;
  height: 40rpx;
  margin-left: 147rpx;
  top: 10rpx;
  display: none;
}

.pro-selecttitle {
  margin-left: 40rpx;
  margin-top: 60rpx;
}

.pro-selecttitle text {
  font-size: 30rpx;
  color: #292929;
}

.pro-productlists {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  margin-top: 10rpx;
}

.pro-productitem {
  width: 90%;
  height: 164rpx;
  margin: auto;
  border-bottom: 3rpx solid #dddddd;
}

.pro-productpic {
  float: left;
  width: 130rpx;
  margin-top: 17rpx;
  height: 130rpx;
}

.pro-productname {
  position: absolute;
  font-size: 32rpx;
  color: #252525;
  margin-top: 36rpx;
  left: 180rpx;
}

.pro-productprice {
  position: absolute;
  font-size: 32rpx;
  color: #252525;
  left: 180rpx;
  margin-top: 92rpx;
}

.pro-productadd {
  float: right;
  width: 60rpx;
  height: 60rpx;
  margin-top: 54rpx;
  margin-right: 15rpx;
}

.pro-copyright {
  width: 100%;
  margin: 0;
  padding: 0;
  text-align: center;
}

.pro-copyrightitem {
  margin: auto;
  padding: 0;
  margin-top: 50rpx;
  margin-bottom: 190rpx;
}

.pro-copyrightmsg {
  font-size: 26rpx;
  color: #03a964;
  margin-right: 40rpx;
  text-decoration: underline;
}

.pro-copyrightprivate {
  font-size: 26rpx;
  color: #03a964;
  text-decoration: underline;
}

.pro-bottominfo {
  position: fixed;
  bottom: 0;
  width: 100%;
  height: 130rpx;
  background: white;
  border-top: 3rpx solid #dddddd;
}

.pro-bottomprize {
  position: absolute;
  font-size: 24rpx;
  color: #b3b3b3;
  left: 40rpx;
  top: 30rpx;
}

.pro-bottompriceinfo {
  position: absolute;
  left: 40rpx;
  top: 60rpx;
  text-align: left;
}

.pro-bottomprice {
  font-size: 24rpx;
  color: #252525;
  font-weight: bold;
}

.pro-bottomprice2 {
  font-size: 30rpx;
  color: #252525;
  font-weight: bold;
}

.pro-bottompay {
  float: right;
  width: 216rpx;
  height: 74rpx;
  background: #f7f7f7;
  margin-top: 30rpx;
  margin-right: 40rpx;
  border-radius: 30rpx;
  text-align: center;
}

.pro-bottompay text {
  height: 100%;
  font-size: 35rpx;
  color: #b3b3b3;
  line-height: 74rpx;
  overflow: hidden;
}

.isDialogShow {
  display: block;
}

.isDialogHide {
  display: none;
}

.pro-dialog {
  position: fixed;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  background: rgba(0, 0, 0, .5);
  z-index: 2;
  text-align: center;
}

.pro-dialogdiv {
  position: absolute;
  width: 100%;
  height: 970rpx;
  bottom: 0;
  text-align: center;
  overflow: hidden;
}

.pro-dialog-topdiv {
  width: 280rpx;
  height: 280rpx;
  background: white;
  margin: auto;
  border-radius: 140rpx;
  overflow: hidden;
}

.pro-dialog-botdiv {
  position: relative;
  width: 100%;
  height: 100%;
  top: -140rpx;
  background: white;
  overflow: hidden;
  border-top-left-radius: 18rpx;
  border-top-right-radius: 18rpx;
}

.pro-dialog-content {
  position: absolute;
  width: 90%;
  height: 100%;
  margin: auto;
  padding: 0;
  left: 5%;
  top: 0;
}

.pro-dialog-pic {
  width: 200rpx;
  height: 200rpx;
  margin: 0;
  padding: 0;
  margin-top: 48rpx;
}

.pro-dialog-close {
  position: absolute;
  width: 60rpx;
  height: 60rpx;
  left: -10rpx;
  top: 170rpx;
}

.pro-dailog-name {
  position: absolute;
  font-size: 32rpx;
  color: black;
  left: 0;
  top: 300rpx;
}

.pro-dailog-price {
  position: absolute;
  font-size: 32rpx;
  color: black;
  left: 0;
  top: 360rpx;
}

.pro-dialog-line {
  width: 100%;
  height: 3rpx;
  margin-top: 190rpx;
  background: #b3b3b3;
}

.pro-dialog-scroll {
  width: 100%;
  height: 100%;
  margin-top: 10rpx;
  overflow: hidden;
}

3.JS文件

编辑gridinfo.js文件用于设置相关控件的数据信息。

// pages/gridinfo/gridinfo.js
Page({

  /**
 * 页面的初始数据
   */
  data: {
    barTitle:null,
    toppic:null,
    proList:[
      {
        "pic": "/images/img_bann1.jpg",
        "name": "玩出我的夏天"
      }, {
        "pic": "/images/img_bann2.jpg",
        "name": "你真棒"
      }, {
        "pic": "/images/img_bann3.jpg",
        "name": "毕业季"
      }, {
        "pic": "/images/img_bann4.jpg",
        "name": "干杯"
      }, {
        "pic": "/images/img_bann5.jpg",
        "name": "求抱抱"
      }, {
        "pic": "/images/img_bann6.jpg",
        "name": "宝贝谢谢你"
      }
    ],
    productList:[
      {
        "pic":"/images/img_product1.jpg",
        "name":"当季特饮",
        "price":40
      }, {
        "pic": "/images/img_product2.jpg",
        "name": "馥芮白",
        "price": 36
      }, {
        "pic": "/images/img_product3.jpg",
        "name": "焦糖玛奇朵",
        "price": 35
      }, {
        "pic": "/images/img_product4.jpg",
        "name": "拿铁",
        "price": 31
      }, {
        "pic": "/images/img_product5.jpg",
        "name": "冷萃冰咖啡",
        "price": 36
      }, {
        "pic": "/images/img_product6.jpg",
        "name": "锦云冷萃",
        "price": 42
      }, {
        "pic": "/images/img_product7.jpg",
        "name": "星礼卡",
        "price": 50
      }, {
        "pic": "/images/img_product7.jpg",
        "name": "星礼卡",
        "price": 100
      }, {
        "pic": "/images/img_product7.jpg",
        "name": "星礼卡",
        "price": 200
      }, {
        "pic": "/images/img_product7.jpg",
        "name": "星礼卡",
        "price": 288
      }, {
        "pic": "/images/img_product7.jpg",
        "name": "星礼卡",
        "price": 520
      }
    ],
    isShowDialog:false,
    dialogPic:"",
    dialogName:"",
    dialogPrice:""
  },

  /**
 * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    var self = this;
    var proList = this.data.proList;
    for (var i = 0; i < proList.length; i++) {
      proList[i].showView = false;
    }
    proList[0].showView = true;
    self.setData({
      barTitle:options.name,
      toppic:options.toppic,
      proList:proList
    })
  },

  toProListInfo: function (e) {
    var index = e.currentTarget.dataset.index;
    var proList = this.data.proList;
    var name = proList[index].name;
    var pic = proList[index].pic;
    var self = this;
    for (var i = 0; i < proList.length; i++) {
      proList[i].showView = false;
    }
    proList[index].showView = true;
    self.setData({
      toppic: pic,
      proList:proList
    });
  },

  toProductAdd:function (e) {
    var index = e.currentTarget.dataset.index;
    var productList = this.data.productList;
    var name = productList[index].name;
    var price = productList[index].price;
    wx.showToast({
      title: name + '  ¥' + price,
    })
  },

  toCopyrightMsg:function (e) {
    wx.showToast({
      title: '使用须知',
    })
  }, 

  toCoprightPrivate:function (e) {
    wx.showToast({
      title: '隐私政策',
    })
  },

  toPayInfo:function (e) {
    wx.showToast({
      title: '购买',
    })
  },

  toProductInfo:function (e) {
    var index = e.currentTarget.dataset.index;
    var productList = this.data.productList;
    var name = productList[index].name;
    var price = productList[index].price;
    var pic = productList[index].pic;
    var self = this;
    self.setData({
      isShowDialog:true,
      dialogPic:pic,
      dialogName:name,
      dialogPrice:price
    })
  },

  toDialogClose: function (e) {
    var self = this;
    self.setData({
      isShowDialog:false
    })
  },

  /**
 * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {
    var barTitle = this.data.barTitle;
    wx.setNavigationBarTitle({
      title:barTitle
    })
  },

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

  },

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

  },

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

  },

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

  },

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

  },

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

  }
})

(1)获取信息,设置标题
首先我们在也没加载时,通过生命周期函数onLoad,获取上个界面传递的信息,同时设置横向列表选中状态(默认选中第一个),并将获取到的数据保存到data。

/**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    var self = this;
    var proList = this.data.proList;
    for (var i = 0; i < proList.length; i++) {
      proList[i].showView = false;
    }
    proList[0].showView = true;
    self.setData({
      barTitle:options.name,
      toppic:options.toppic,
      proList:proList
    })
  }

然后,在onReady方法中调用wx.setNavigationBarTitle()方法,设置当前界面标题信息

onReady: function () {
    var barTitle = this.data.barTitle;
    wx.setNavigationBarTitle({
      title:barTitle
    })
  }

(2)横向列表点击事件处理
横向列表的点击事件,主要处理两个问题,大图的切换和选中状态的处理。

toProListInfo: function (e) {
    var index = e.currentTarget.dataset.index;
    var proList = this.data.proList;
    var name = proList[index].name;
    var pic = proList[index].pic;
    var self = this;
    for (var i = 0; i < proList.length; i++) {
      proList[i].showView = false;
    }
    proList[index].showView = true;
    self.setData({
      toppic: pic,
      proList:proList
    });
  }

(3)商品弹窗的处理
通过isShowDialog设置弹窗的显示隐藏,同时根据弹窗index,设置弹窗内布局信息

toProductInfo:function (e) {
    var index = e.currentTarget.dataset.index;
    var productList = this.data.productList;
    var name = productList[index].name;
    var price = productList[index].price;
    var pic = productList[index].pic;
    var self = this;
    self.setData({
      isShowDialog:true,
      dialogPic:pic,
      dialogName:name,
      dialogPrice:price
    })
  }

至此,列表界面和详情界面的效果基本实现,如有问题,欢迎留言

猜你喜欢

转载自blog.csdn.net/lhy349/article/details/80975590
今日推荐