摘要:微信小程序小米事先聲明,這是一個高仿小米的微信小程序。寫完之后查文檔才發現,微信小程序官方提供了自定義組件的方法有需要的可以查看微信小程序文檔寫完這個組件后我總解了一下需要注意的問題選中了的當前頁面,再次點擊因該無效。
微信小程序-小米Lite
事先聲明,這是一個高仿小米Lite的微信小程序。
我呢現在是一個大三快大四的學生,這個小程序花了我很長時間,把能寫的功能基本上都寫了。我秉著分享開源的心理,盡量把我寫的這個小程序怎么寫的,為什么這樣寫,詳細的告訴大家。為什么是盡量?這是因為,我不太會說,可能說的不是很清楚,所以只能盡量.
項目預覽
ok實現的效果就是這樣。
easy-moke
VSCode
微信小程序開發者工具
(阿里巴巴矢量圖標庫)
文件目錄├│ ├ ├ │ ├ │ ├ │ ├ │ ├ ├ │ ├ │ ├ │ ├ │ ├ │ ├ │ ├ │ ├ │ ├ ├ │ └util.js ├ │ └weui.wxss ├ │ ├Api.js │ ├main.js │ └mock.js
對于初學者來說,可能拿到設計圖就立馬寫,其實這樣很不好,寫出來的代碼會有很多重復的代碼,這樣不利于之后的維護。所以應該把一些公用的代碼封裝,之后直接調用就行了,之后維護起來也更加的方便。
API封裝我們前端想要獲取頁面的數據,就需要發送HTTP請求后端提供給我們的API接口,從API接口中獲取我們想要的數據。在微信小程序中,微信官方給我們提供了一個方法wx.request來請求.
一個程序,需要的HTTP請求會很多,如果我們每個請求都去寫一個wx.request,這樣寫出來的代碼,看起來會很冗長,他人看我們的代碼時也會很累,也不利于我們之后的修改。因此為了代碼的整潔,和之后的修改方便。我就把所有的API請求請求封裝在wxapi文件目錄下。
// Api.js const banners = "https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/banners" const navdata = "https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/navdata" const goodList = "https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/goodList" const category = "https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/category" const findData = "https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/findData" const userData = "https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/userData" const goodDetail = "https://www.easy-mock.com/mock/5cf9c392006feb28c7eedf28/goodDetail" const QQ_MAP_KEY = "NNFBZ-6DRCP-IRLDU-VEQ4F-TXLP2-PFBEN" const MAPURL = "https://apis.map.qq.com/ws/geocoder/v1/" module.exports = { banners, navdata, goodList, category, findData, userData, goodDetail, QQ_MAP_KEY, MAPURL }
import * as MOCK from "./mock" import * as API from "./Api" const request = (url,mock = true,data) => { let _url = url return new Promise((resolve, reject) => { if (mock) { let res = { statusCode: 200, data: MOCK[_url] } if (res && res.statusCode === 200 && res.data) { resolve(res.data) } else { reject(res) } } else { wx.request({ url: url, data, success(request) { resolve(request.data) }, fail(error) { reject(error) } }) } }); } // showLoading const showLoading = () => { wx.showLoading({ title: "數據加載中", mask: true, }); } // 獲取地理位置 const geocoder = (lat, lon) => { return request(API.MAPURL,false,{ location: `${lat},${lon}`, key: API.QQ_MAP_KEY, get_poi: 0 }) } module.exports = { getBanners: () => { // return request("banners") return request(API.banners,false) //首頁 banners }, getNavData: () => { // return request("navdata") return request(API.navdata,false) //首頁 navdata }, getGoodList: () => { // return request("goodList") return request(API.goodList,false) //首頁 商品列表 }, getCategroy: () => { // return request("category") return request(API.category,false) //分類頁面 }, getFindData: () => { // return request("findData") return request(API.findData,false) //發現 頁面 }, getUserData: () => { // return request("userData") return request(API.userData,false) // 我的 頁面 }, getGoodDetail: () => { // return request("goodDetail") return request(API.goodDetail,false) //商品詳情 }, showLoading, geocoder }
看到這里,可能就會有一些疑問,為什么我在每個請求后面都加上了一個false?
這是因為,我在寫這個微信小程序開始時,沒有使用easy-mock來模擬http請求數據。我是把假數據都放在mock.js文件中。然后使用 return request("banners")這種方式就可以獲取我想要的數據。
API封裝完了,該怎么調用呢?我就以首頁的banners數據為例
// index.js const WXAPI = require("../../wxapi/main") onLoad: function (options) { WXAPI.showLoading() this.getBanners() }, getBanners() { WXAPI .getBanners() .then(res => { wx.hideLoading() this.setData({ banners: res.data }) }) },
記住,如果想要發送HTTP請求數據的頁面,都必須加上這一句const WXAPI = require("../../wxapi/main")
定義的組件開始準備OK,現在開始寫頁面。第一步要寫的是tabBar部分。
tabBar組件看起來是不是有點奇怪,為什么有點透明的感覺?因為這個tabBar組件是我自己寫的。
一般來將,直接在把tabBar組件寫在app.json中,就可以了。
但是我覺得不是那么好看,所以就自己擼了一個tabBar組件出來。
寫完之后查文檔才發現,微信小程序官方提供了自定義tabBar組件的方法,有需要的可以查看微信小程序文檔
寫完這個組件后我總解了一下,需要注意的問題.
選中了的當前頁面,再次點擊因該無效。
所以我在app.js中存入了一個page屬性,來存儲當前頁面,然后在點擊事件goToPage()方法中加入判斷去解決。
首頁 分類 發現 購物車 我的
// components/tabbar/tabbar.js // 全局里面存了一個page 表示當前 路由 const app = getApp(); Component({ /** * 組件的屬性列表 */ properties: { // 是否選中 on:{ type: String, value: "" } }, /** * 組件的初始數據 */ data: { }, /** * 組件的方法列表 */ methods: { // 跳轉到相應的頁面 // 加了一個判斷 // 因為如果現在顯示的是當前頁面就不需要再跳轉 goToPage(e) { let page = e.currentTarget.dataset.page || "user"; if(app.globalData.page === page) { return ; } wx.redirectTo({ url: `/pages/${page}/${page}`, }); app.globalData.page = page; } } })
/* components/tabbar/tabbar.wxss */ .tabbar { width: 100%; height: 100rpx; background-color: #ffffff; display: flex; position: fixed; bottom: 0; font-size: 26rpx; z-index: 99; } .shouye,.fenlei,.faxian,.gouwuche,.wode { flex: 1; display: flex; flex-direction: column; justify-content: center; text-align: center; opacity: 0.5; } .icon { height: 60rpx; } .on { color:#f96702; }
關于如何使用的問題,只需要在,需要tabBar功能的頁面底部加上就可以.
比如index頁面
記得在json中引入組件
{ "usingComponents": { "goodList": "../../components/goodList/goodList", "tabbar": "../../components/tabbar/tabbar" } }icon組件
tabBar組件中需要圖標,我使用的是阿里的圖標庫
如何使用?
先去圖標庫中找到你所需要的圖標,加入購物車。
然后添加至項目,自己新建一個就可以了。
在然后下載之本地,把其中的CSS文件里面所有的內容都復制到wxss文件中就行了.
這個組件很簡單,定義出來就可以直接使用,并且修改顏色和大小.
properties: { type: { type: String, value: "" }, color: { type: String, value: "#000000" }, size: { type: Number, value: "45" } },goodList組件
在首頁中存在很多這樣的商品列表,一個一個寫,這樣寫出來的代碼會導致首頁的代碼顯得很多,并且不好維護,所以我就封裝成了一個組件.
{{name}} {{brief}} ¥{{price}} ¥{{oldPrice}}
properties: { // 圖片鏈接 url: { type: String, value: "" }, // 名稱 name: { type: String, value: "" }, // 信息 brief: { type: String, value: "" }, // 新的價格 price: { type: String, value: "" }, // 舊的價格 oldPrice: { type: String, value: "" } },
/* components/goodList/goodList.wxss */ .goodList-good { position: relative; width: 100%; height: 100%; } .goodList-good-img { width: 100%; height: 312rpx; position: relative; } .goodList-good-img image { width: 100%; height: 100%; } .goodList-good_detail { padding: 26rpx 23rpx; } .good_detail_name { width:100%; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; } .good_detail_brief { width:100%; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; font-size: 25rpx; color: #8c8c8c; } .good_detail_price { display: flex; justify-content: flex-start; align-items: flex-end; } .good_detail_price .price { color: #a36d4a; font-size: 28rpx; padding-right: 16rpx; } .good_detail_price .oldPrice { font-size: 24rpx; color: #8c8c8c; text-decoration: line-through; }
你們會發現,我的邊框為什么那么細。
0.5px邊框如何畫初學者,可能會說,0.5px邊框,不就是border: 0.5px嗎,其實這是錯的。
瀏覽器會把任何小于1px的邊框都解析成1px,所以你寫0.5px其實瀏覽器會解析成1px,這樣就實現不了效果。
其實也很簡單,使用偽類去畫。
例如,goodList組件的右部邊框.
在index頁面的html中,我在包裹goodList的view標簽的class中加上了rightBorder這個類來表示畫出上邊框。
.rightBorder::after { content: ""; position: absolute; height: 200%; width: 1rpx; right: -1rpx; top: 0; transform-origin: 0 0; border-right: 1rpx solid#e0e0e0; transform: scale(.5); z-index: 1; }
其實畫0.5px邊框就記住。
使用偽類,并且 position: absolute;
如果想畫上下的邊框,就把width設置成200%,,左右就把height設置成200%
然后使用transform: scale(.5); 寬高都縮小0.5倍就成了0.5px邊框.
其它還有一些 細節問題就按照所需寫就行。
userList組件只是我的頁面中的一個列表信息,這個很簡單
{{List.text}} ({{List.state}})
/* components/userList/userList.wxss */ .main { width: 100%; height: 120rpx; display: flex; align-items: center; background-color: #fff; padding: 40rpx; box-sizing: border-box; } .main image { width: 80rpx; height: 80rpx; } .main .text { font-size: 32rpx; padding: 0 10rpx 0 5rpx; } .main .state { font-size: 26rpx; color: #8c8c8c; }
properties: { List: { type: Object } },頁面和功能介紹 首頁搜索部分
我這里引入的是weui組件的搜索樣式。如何用?
下載WEUI小程序版,可以在微信開發者工具中打開。
(推薦)把styly文件夾下的weui.wxss 放到自己項目中weui文件夾下
在app.wxss中@import "./weui/weui.wxss".就可以使用了
首頁輪播部分這是微信小程序提供的一個組件swiper,微信小程序開發者文檔
swiper和swiper-item組合起來就可以實現,一些配置信息,請查看官方文檔
具體代碼
data: { banners: [], indicatorDots: true, autoPlay: true, interval: 3000, duration: 1000, navdata: [], goodList: [], goodListOne: {}, name:"", },一個常用的功能
在商城小程序中經常要做一個這樣的功能.例如:
功能要求:
點擊左邊的商品列表,右邊的商品信息會滑動到對應位置.
滑動右邊的商品信息,左邊的商品列表顯示的高亮會對應發生變化.
功能要求并不難,但是對于初學者而言,可能會有些問題。我就直接說功能該怎么做
首先:分析一下,頁面結構是左右布局。并且兩邊都可以滑動.所以可以使用微信給我們提供的
scroll-view組件.兩個組件就可以采用float,分布在左右兩邊.
{{item[0].name}} {{item[0].name}} {{product.desc}}
/* miniprogram/pages/category/category.wxss */ /*定義滾動條高寬及背景 高寬分別對應橫豎滾動條的尺寸*/ ::-webkit-scrollbar { width: 0px; height: 0px; background-color: pink; } .categroy-left { height: 100%; width: 150rpx; float: left; border-right: 1px solid #ebebeb; box-sizing: border-box; position: fixed; font-size: 30rpx; padding-bottom: 100rpx; box-sizing: border-box; } .categroy-left .cate-list { height: 90rpx; line-height: 90rpx; text-align: center; border: 2px solid #fff; } .categroy-left .cate-list.on { color: #ff4800; font-size: 34rpx; } /* 右邊的列表 */ .categroy-right { width: 600rpx; float: right; height: 1334rpx; /* height: 100%; */ padding-bottom: 100rpx; box-sizing: border-box; overflow: hidden; } .right-title { width: 100%; text-align: center; position: relative; padding-top: 30rpx; /* font-size: 30rpx; */ padding-bottom: 30rpx; } .right-title text::before, .right-title text::after { content: ""; position: absolute; width: 60rpx; /* height: 1px; */ top: 50%; border-top: 1px solid #e0e0e0; /* transform: scale(.5); */ } .right-title text::before { left: 30%; } .right-title text::after { right: 30%; } .right-list { /* height: 100%; */ background-color: #fff; } .right-content { width: 100%; height: 100%; display: flex; flex-wrap: wrap; } .right-content .list-detail { flex-shrink: 0; width: 33.3%; height: 100%; font-size: 26rpx; text-align: center; } .right-content .list-detail image { width: 120rpx; height: 120rpx; padding: 10rpx; /* background-color: pink; */ }
這個功能的難點就在于js邏輯
先貼出data中需要的數據
data: { categroy:[], //商品信息 curIndex: "A", //當前的選中的標簽 toView: "A", //去到的標簽 // 存入每個list的高度疊加 heightArr: [], // 最后一個list,就是最后一個標簽的id endActive: "A" },點擊左邊右邊滑動
這一功能很簡單就能實現
只需要在右邊scroll-view組件中添加事件scroll-into-view="{{toView}}",toView就是商品顯示的id
注意,右邊每個商品信息,頁面渲染時必須加上id屬性
然后左邊的scroll-view組件只需添加一個點擊事件去修改toView的值就行了
// 點擊左邊標簽要修改的信息 switchTab(e) { this.setData({ curIndex: e.target.dataset.index, toView: e.target.dataset.index }) },滑動右邊左邊高亮對應改變
首先需要計算出 右邊商品每一塊占據的高度,并且存入數組中,可以放入onReady生命周期中
// 計算出右邊每個商品占據的高度 getPageMessage() { // console.log(4) let self = this let heightArr = [] let h = 0 const query = wx.createSelectorQuery() query.selectAll(".right-list").boundingClientRect() query.exec( res => { res[0].forEach( item => { h += item.height heightArr.push(h) }) self.setData({ heightArr: heightArr }) }) },
在右邊的scroll-view組件中加上事件。bindscroll="scrollContent,這是scroll-view提供的事件,在滑動時就會觸發.
// 頁面滑動時觸發 scrollContent(e) { const scrollTop = e.detail.scrollTop const scrollArr = this.data.heightArr const length = scrollArr.length - 1 let endChar = String.fromCharCode(65 + length) let curChar = this.getCurrentIndex(scrollTop) if(this.data.endActive != endChar) { this.setData({ curIndex: curChar }) } else { this.setData({ endActive: "A" }) } },
// 判斷curIndex應該是那個 getCurrentIndex(scrollTop) { const scrollArr = this.data.heightArr let find = scrollArr.findIndex(item => { // 提前10rpx觸發效果 return scrollTop < item - 10 }) let curChar = String.fromCharCode(65 + find) return curChar },
以上就可以實現所有的功能要求了。
但是這樣的滑動并不是很完美,右邊滑動到最下面,左邊高亮卻不是最后一個
相對完美效果就是這樣
想要完美一點就要在,右邊scroll-view添加一個事件:bindscrolltolower="scrollEnd"
// 頁面滑動到底部觸發 scrollEnd() { const scrollArr = this.data.heightArr const length = scrollArr.length - 1 let endChar = String.fromCharCode(65 + length) this.setData({ curIndex: endChar, endActive: endChar }) },購物邏輯
想要實現這樣的效果并不困難。
邏輯順序: 首頁點擊商品信息 -> 商品詳情頁面顯示對應的商品詳情信息 -> 購物車頁面顯示商品購買的商品信息. -> 修改之后,商品詳情頁面顯示修改的信息。
想要實現這樣的功能,必須有一個 id 在頁面跳轉時,傳入給跳轉的頁面,跳轉的頁面再通過id值獲取頁面所需的數據
例如:首頁 -> 商品詳情頁
這是一條商品的列表的信息,通過點擊事件bindtap="goDetails",跳到對應的頁面.
goDetails(e) { const id = e.currentTarget.dataset.id wx.navigateTo({ url: `/pages/goodDetails/goodDetails?id=${id}`, }); },
商品詳情頁:
傳入的id值可以再onLoad生命周期的options參數上獲取
onLoad: function (options) { WXAPI.showLoading() // 獲取用戶的地址信息 const address = wx.getStorageSync("Address"); this.setData({ id: options.id, address }) this.getGoodDetail() },數據的存儲邏輯
我是把數據直接存在本地緩存中(也可以直接存入到云數據庫中),使用的是 wx.setStorage()
在本地存入了兩個數據,一個是所有購買的商品信息,一個是總的商品購買數量
// 添加到購物車 toAddCart() { let cartData = wx.getStorageSync("goods") || []; let data = { id: this.data.id, name: this.data.goodData.name, memory: this.data.memory, color: this.data.color, price: this.data.price, num: this.data.selectNum, img: this.data.imgSrc, select: true } // wx.removeStorageSync("goods"); cartData.push(data) const allNum =this.getAllNum(cartData) wx.setStorage({ key: "goods", data: cartData, success: (res) => { console.log(res) let pageIndex = getCurrentPages() let backIndex = pageIndex.length - 2 wx.navigateBack({ delta: backIndex }) }, fail: () => {}, complete: () => {} }); // 存儲數量到storage wx.setStorageSync("allNum", allNum); // 寫到外面就可以讓showToast 顯示在前一個頁面 setTimeout(()=>{ wx.showToast({ title: "已加入購物車", icon: "success", duration: 2000 }); },500) }, // 獲取所有的數量 getAllNum(cartData) { return cartData.reduce((sum, item) => { return sum + (+item.num) },0) },概述的參數切換
實現這個功能只需要加一個狀態,點擊時就修改狀態的值,并且修改相關渲染的數據就行。
data: { state: "details_img", //判斷概述 參數 }
概述 參數
// 改變概述和參數 changeState() { let state = this.data.state if(state === "details_img") { state = "param_img" } else { state = "details_img" } this.setData({ state }) },購物數據改變,商品詳情數據修改
對比一下上面兩張圖的區別.
在購物頁面中選的商品數量和具體的商品信息,之后跳轉回商品詳情頁面中,對應的數據會修改
已選 {{default_change.name}} {{default_change.memory}} {{default_change.color}} × {{default_change.num}}
{{allNum}}
上面時兩塊修改的html結構
在購物頁面點擊確認之后,我默認就把商品添加到購物車中,并且位于數據的最后一條
返回商品詳情頁面時,會重新觸發onShow生命周期的函數。
所以我只需要,在onShow中觸發修改方法就行.
// 改變默認的版本數據 default_change changeDefauleChange() { const goods = wx.getStorageSync("goods") || []; if(goods.length === 0) { return } const id = this.data.id const default_change = goods[goods.length - 1] let memory = default_change.memory.toString() memory = memory.substring(0,memory.length - 4) default_change.memory = memory this.setData({ default_change }) },畫一個三角形
這一個三角形是使用CSS畫出來的,并不是圖標。
使用CSS畫出一個三角形,也不是那么困難。使用的是偽類和border屬性
.right:before, .right:after { content: ""; position: absolute; top: 35%; right: 0; border-width: 8px; /* transform: translateY(10000rpx); */ border-color: transparent transparent transparent transparent; border-style: solid; transform: rotate(90deg); } .right:before { border-bottom: 8px #aaaaaa solid; } .right:after { right: 1px; /*覆蓋并錯開1px*/ border-bottom: 8px #fff solid; }修改商品數量
可以直接使用微信小程序提供的picker組件,具體配置請查看文檔
使用騰訊地圖獲取地理位置先搜索騰訊地圖,并且注冊開發者信息,申請一個密鑰key.
// 獲取地理位置 const geocoder = (lat, lon) => { return request(API.MAPURL,false,{ location: `${lat},${lon}`, key: API.QQ_MAP_KEY, get_poi: 0 }) }
然后把密鑰復制,因為我封裝了所有的API接口。所以使用了 API.QQ_MAP_KEY代替,這里就填寫申請的密鑰就行.
想要獲取用戶的經緯度信息,可以使用wx.getLocation(),就可以獲取用戶的經緯度信息了.
getLocation() { wx.getLocation({ type: "gcj02", success: this.getAddress, fail: () => { this.openLocation() } }) }, getAddress(res) { let { latitude: lat, longitude: lon} = res WXAPI.geocoder(lat, lon) .then(res => { if(res.status !== 0 || !res.result) { return } let {address_component } = res.result const Address = { city: address_component.city, district: address_component.district } wx.setStorageSync("Address", Address); }) },
因為我未讓用戶授權,所以直接把獲取的地理位置,保存在本地storage中.
獲取的地理位置,就可以在商品詳情頁面的送至顯示出來
結語做這個項目的過程來說是快樂的,沒有使用云函數(頁面數據并不多,我覺得不需要就可以寫出來了),所以總共加起來寫的時間也很短,不到一個星期就寫完了。寫完之后的那一刻的成就感也很好。如果你覺得這篇文章有幫到你的地方,不妨給個贊吧!同時也非常希望在下方看到給出的建議!最后奉上源碼.如果有需要就自取吧!
最后,說點題外話,因為我是2020屆的畢業生,現在面臨實習壓力。因為需要話時間去看面試題,所以后面寫的一段文章,我只是簡要的把重要的功能邏輯寫了出來,如果寫的不清楚,請見諒。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105297.html
摘要:嗯這句話就是作為第一次做仿小程序項目的我,歷經磨難得出來的肺腑之言。作為想要走上代碼這條不歸路的程序員,心浮氣躁就是為以后整個項目給自己挖坑奠定了良好的基礎。 前言 ?????關于小程序,在這里有一句話送給正準備閱讀的你-世界上本沒有坑,路走的多了就有了;世界上本沒有路,坑填的多了就有了。嗯~~~這句話就是作為第一次做仿小程序項目的我,歷經‘磨難’得出來的肺腑之言。好了,不多說,進入正...
摘要:嗯這句話就是作為第一次做仿小程序項目的我,歷經磨難得出來的肺腑之言。作為想要走上代碼這條不歸路的程序員,心浮氣躁就是為以后整個項目給自己挖坑奠定了良好的基礎。 前言 ?????關于小程序,在這里有一句話送給正準備閱讀的你-世界上本沒有坑,路走的多了就有了;世界上本沒有路,坑填的多了就有了。嗯~~~這句話就是作為第一次做仿小程序項目的我,歷經‘磨難’得出來的肺腑之言。好了,不多說,進入正...
摘要:在現如今的游戲市場寒冬中,擁有微信龐大的用戶量以及更好兼容性的小程序游戲,優勢就顯得格外明顯。掃描二維碼即可報名您在現場將有這些體驗來自騰訊云云開發團隊與微信團隊聯合打造干貨分享,內容包括微信小游戲首發經驗分享。 有人說微信小程序游戲的百花齊放 活像十幾年前的4399小游戲稱霸互聯網的景象 歪,斗地主嗎,三缺二, 不用下app,小程序就能玩,我保證不搶地主讓你搶! ...... ‘...
閱讀 1552·2021-09-22 15:52
閱讀 3459·2021-09-22 14:59
閱讀 2843·2021-09-02 15:12
閱讀 971·2021-08-20 09:35
閱讀 1578·2019-08-30 14:09
閱讀 2709·2019-08-30 13:56
閱讀 1646·2019-08-26 18:27
閱讀 3363·2019-08-26 13:37