零、文章目录
微信小程序04-常用API上
1、案例:音乐播放器
(1)案例分析
- 需求:“音乐播放器”微信小程序可以让用户随时随地享受音乐,给用户带来了便捷的音乐体验,且支持后台播放,用户可以在听音乐的同时进行其他操作。
- “音乐播放器”微信小程序的页面由上、中、下共3个部分组成,这3个部分分别是标签栏区域、内容区域和播放器区域。
- ①标签栏区域:该区域有音乐推荐、播放器和播放列表3个标签按钮,通过点击标签按钮可以进行标签页的切换。
- ②内容区域:通过左右滑动可以实现音乐推荐、播放器和播放列表3个标签页的切换。这3个标签页的具体说明如下。
- 音乐推荐:用于向用户推荐一些歌曲。
- 播放器:用于显示当前播放音乐的信息、专辑封面、播放进度和时间。其中,音乐信息包括当前播放音乐的标题和歌手。
- 播放列表:用于显示当前播放的曲目列表,用户可以进行曲目切换。
- ③播放器区域:显示当前播放的音乐信息,并且提供了3个按钮,按钮的功能依次为“切换到播放列表”“播放/暂停”“下一曲”。
(2)知识储备-scroll-view组件
-
scroll-view组件
- 当一个容器中的内容有很多时,如果容器无法完整显示内容,则可以通过滚动操作来查看完整内容。
- 在微信小程序中,可以通过scroll-view组件来实现滚动效果,它支持横向滚动和纵向滚动,默认是不滚动的,需要通过scroll-x和scroll-y属性允许横向和纵向滚动。
<scroll-view>实现可滚动视图区域</scroll-view>
- scroll-view组件的常用属性如下
-
代码演示:
- 在pages/index/index.wxml文件中编写如下代码。
<scroll-view scroll-x="{{ true }}" scroll-y="{{ true }}" style="height: 200px;" bindscroll="scroll"><view style="width: 200%; height: 400px; background-image: linear-gradient(to bottom right, red, yellow);"></view></scroll-view>
- 在pages/index/index.js文件中添加scroll()事件处理函数并输出e.detail的值。
scroll: function (e) {console.log(e.detail) }
- 通过e.detail可以获取滚动时的位置信息。
- scrollLeft:横向滚动条左侧到视图左边的距离。
- scrollTop:纵向滚动条上端到视图顶部的距离。
- scrollHeight:纵向滚动条在y轴上最大滚动距离。
- scrollWidth:横向滚动条在x轴上最大的滚动距离。
- deltaX:横向滚动条的滚动状态。
- deltaY:纵向滚动条的滚动状态。
(2)知识储备-slider组件
-
slider组件
- 在开发中,有时需要在一个固定区间内控制数值的变化,例如音乐的播放进度、音量的大小、亮度的高低等,这些需求可以利用滑动选择器来实现。
- 在微信小程序中,通过slider组件可以定义一个滑动选择器。slider组件是微信小程序表单组件中的一种,用于滑动选择某一个值。用户可以通过拖曳滑块在一个固定区间内进行选择。
- slider组件的常用属性如下
-
代码演示
- 在pages/index/index.wxml文件中编写页面结构。
<slider bindchanging="sliderChanging" show-value="true" />
- 在pages/index/index.js文件中编写事件处理函数sliderChanging()。
sliderChanging: function (e){console.log(e.detail.value) }
(2)知识储备-<include>
标签
-
**
<include>
标签:**用于引用其他文件的代码,相当于把引用的代码复制到<include>
标签的位置。<include>
标签的用途主要有以下两点- **方便地查找代码:**当一个WXML页面中的代码过多时,会给代码的维护带来麻烦,有时为了找到某一处代码可能需要翻阅几百行。这时可以利用
<include>
标签将代码拆分到多个文件中,从而可以更方便地查找代码。 - **代码复用减少维护:**当多个WXML页面中有相同的部分时,可以将这些公共部分抽取出来,保存到一个单独的WXML文件中,然后在用到的地方通过
<include>
标签引入。这样可以减少重复的代码,并且修改时只需要修改一次。
- **方便地查找代码:**当一个WXML页面中的代码过多时,会给代码的维护带来麻烦,有时为了找到某一处代码可能需要翻阅几百行。这时可以利用
-
代码演示
- ①在pages/index/index.wxml文件中编写页面结构。
<!-- index.wxml --> <include src="header.wxml" /> <view>body</view> <include src="footer.wxml" />
- ②在pages/index/header.wxml文件中编写头部的页面结构。
<view>header</view>
- ③在pages/index/footer.wxml文件中编写尾部的页面结构。
<view>footer</view>
- 运行后,实际得到的pages/index/index.wxml文件如下
<view>header</view> <view>body</view> <view>footer</view>
(2)知识储备-背景音频API
-
背景音频API
- 在微信小程序中,使用背景音频API可以实现音频的后台播放。
- 在使用背景音频API前,需要在app.json文件中配置requiredBackgroundModes属性,开启微信小程序后台音频播放功能。
"requiredBackgroundModes": ["audio"]
- 背景音频API的使用方法是,先通过wx.getBackgroundAudioManager()方法获取到一个BackgroundAudioManager实例,然后通过该实例的相关属性和方法实现背景音频的播放。
var audioGbam = wx.getBackgroundAudioManager()
- BackgroundAudioManager实例常用的属性和方法如下
-
代码演示:在pages/index/index.js文件的onReady()函数中编写如下代码。
onReady: function () {// 创建BackgroundAudioManager实例var audio = wx.getBackgroundAudioManager()// 当开始播放音乐时,输出调试信息audio.onPlay(function () {console.log('开始播放')})// 设置背景音频的标题audio.title = '音乐标题'// 设置背景音频的资源地址audio.src = 'http://127.0.0.1:3000/1.mp3'}
(3)案例实现
-
准备工作
- ①创建项目。在微信开发者工具中创建一个新的微信小程序项目,项目名称为“音乐播放器”,模板选择“不使用模板”。
- ②配置页面。清单如下。
- ③配置导航栏。在pages/index/index.json文件中配置页面导航栏。
{"navigationBarTitleText": "音乐","navigationBarBackgroundColor": "#17181a","navigationBarTextStyle": "white" }
- ④配置页面样式和准备图片。在pages/index/index.wxss文件中配置页面样式。在images文件夹中准备图片。
- ⑤启动服务器。切换工作目录到nodejs服务程序目录,打开命令提示符,然后在命令提示符中执行如下命令,启动服务器。
node index.js
-
实现页面结构:
- 在pages/index/index.wxml文件中编写“音乐播放器”微信小程序的页面结构。
<!--index.wxml--> <!-- 标签栏区域的页面结构 --> <view class="tab"><view class="tab-item {{ tab == 0 ? 'active' : '' }}" bindtap="changeItem" data-item="0">音乐推荐</view><view class="tab-item {{ tab == 1 ? 'active' : '' }}" bindtap="changeItem" data-item="1">播放器</view><view class="tab-item {{ tab == 2 ? 'active' : '' }}" bindtap="changeItem" data-item="2">播放列表</view> </view> <!-- 内容区域的页面结构 --> <view class="content"> <!-- 使用swiper组件实现标签页切换 --><swiper current="{{ item }}" bindchange="changeTab"><swiper-item><!-- 音乐推荐 --><include src="info.wxml" /></swiper-item><swiper-item><!-- 播放器 --><include src="play.wxml" /></swiper-item><swiper-item><!-- 播放列表 --><include src="playlist.wxml" /></swiper-item></swiper> </view> <!-- 底部播放器区域 --> <view class="player"><image class="player-cover" src="{{ play.coverImgUrl }}" data-item="1" bindtap="changeItem" /><view class="player-info"><view class="player-info-title" data-item="1" bindtap="changeItem">{{ play.title }}</view><view class="player-info-singer" data-item="1" bindtap="changeItem">{{ play.singer }}</view></view><view class="player-controls"><!-- 切换到播放列表 --><image src="/images/01.png" data-item="2" bindtap="changeItem" /><!-- 播放/暂停 --><image wx:if="{{ state == 'paused' }}" src="/images/02.png" bindtap="play" /><image wx:else src="/images/02stop.png" bindtap="pause" /><!-- 下一曲 --><image src="/images/03.png" bindtap="next" /></view> </view>
- pages/index/info.wxml文件中的代码如下。
<swiper class="content-info-slide" indicator-color="rgba(255,255,255,.5)" indicator-active-color="#fff" indicator-dots circular autoplay><swiper-item><image src="/images/banner.jpg" /></swiper-item><swiper-item><image src="/images/banner.jpg" /></swiper-item><swiper-item><image src="/images/banner.jpg" /></swiper-item> </swiper> <view class="content-info-portal"><view><image src="/images/04.png" /><text>私人FM</text></view><view><image src="/images/05.png" /><text>每日歌曲推荐</text></view><view><image src="/images/06.png" /><text>云音乐新歌榜</text></view> </view> <view class="content-info-list"><view class="list-title">推荐歌曲</view><view class="list-inner"><view class="list-item"><image src="/images/cover.jpg" /><view>山水之间</view></view><view class="list-item"><image src="/images/cover.jpg" /><view>惊鸿一面</view></view><view class="list-item"><image src="/images/cover.jpg" /><view>稻香</view></view><view class="list-item"><image src="/images/cover.jpg" /><view>如果当时</view></view><view class="list-item"><image src="/images/cover.jpg" /><view>清明雨上</view></view><view class="list-item"><image src="/images/cover.jpg" /><view>有何不可</view></view></view> </view>
- pages/index/play.wxml文件中的代码如下。
<view class="content-play"><!-- 音乐信息 --><view class="content-play-info"><text>{{ play.title }}</text><view>—— {{ play.singer }} ——</view></view><!-- 专辑封面 --><view class="content-play-cover"><image src="{{ play.coverImgUrl }}" style="animation-play-state:{{ state }}" /></view><!-- 播放进度和时间 --><view class="content-play-progress"><text>{{ play.currentTime }}</text><view><slider bindchanging="sliderChanging" bindchange="sliderChange" activeColor="#d33a31" block-size="12" backgroundColor="#dadada" value="{{ play.percent }}" /></view><text>{{ play.duration }}</text></view> </view>
- pages/index/playlist.wxml文件中的代码如下。
<scroll-view class="content-playlist" scroll-y><view class="playlist-item" wx:for="{{ playlist }}" wx:key="id" bindtap="change" data-index="{{ index }}"><image class="playlist-cover" src="{{ item.coverImgUrl }}" /><view class="playlist-info"><view class="playlist-info-title">{{ item.title }}</view><view class="playlist-info-singer">{{ item.singer }}</view></view><view class="playlist-controls"><text wx:if="{{ index == playIndex }}">正在播放</text></view></view> </scroll-view>
-
**获取页面数据:**在pages/index/index.js文件中获取页面所需的数据。
// index.js// 格式化时间
function formatTime(time) {var minute = Math.floor(time / 60) % 60;var second = Math.floor(time) % 60return (minute < 10 ? '0' + minute : minute) + ':' + (second < 10 ? '0' + second : second)
}Page({data: {item: 0,tab: 0,// 播放列表数组playlistplaylist: [{id: 1,title: '祝你生日快乐',singer: '小丽',src: 'http://127.0.0.1:3000/1.mp3',coverImgUrl: '/images/cover.jpg'}, {id: 2,title: '劳动最光荣',singer: '小朋',src: 'http://127.0.0.1:3000/2.mp3',coverImgUrl: '/images/cover.jpg'}, {id: 3,title: '龙的传人',singer: '小华',src: 'http://127.0.0.1:3000/3.mp3',coverImgUrl: '/images/cover.jpg'}, {id: 4,title: '小星星',singer: '小红',src: 'http://127.0.0.1:3000/4.mp3',coverImgUrl: '/images/cover.jpg'}],state: 'running',playIndex: 0,play: {currentTime: '00:00',duration: '00:00',percent: 0,title: '',singer: '',coverImgUrl: '/images/cover.jpg',}},// 在页面初次渲染时,自动选择播放列表中的第1个曲目audioBam: null,sliderChangeLock: false,onReady: function () {this.audioBam = wx.getBackgroundAudioManager()// 默认选择第1曲this.setMusic(0)// 播放失败检测this.audioBam.onError(() => {console.log('播放失败:' + this.audioBam.src)})// 播放完成自动换下一曲,监听音频自然播放结束的事件this.audioBam.onEnded(() => {this.next()})// 监听音频播放进度更新事件,获取音乐状态信息var updateTime = 0 // 上次更新的时间this.audioBam.onTimeUpdate(() => {var currentTime = parseInt(this.audioBam.currentTime)if (!this.sliderChangeLock && currentTime !== updateTime) { // // 将更新频率限制在1秒1次updateTime = currentTimethis.setData({'play.duration': formatTime(this.audioBam.duration || 0),'play.currentTime': formatTime(currentTime),'play.percent': currentTime / this.audioBam.duration * 100})}})},sliderChange: function (e) {var second = e.detail.value * this.audioBam.duration / 100this.audioBam.seek(second)setTimeout(() => {this.sliderChangeLock = false}, 1000)},sliderChanging: function (e) {var second = e.detail.value * this.audioBam.duration / 100this.sliderChangeLock = truethis.setData({'play.currentTime': formatTime(second),})},// 设置当前播放的曲目setMusic: function (index) {var music = this.data.playlist[index]this.audioBam.src = music.srcthis.audioBam.title = music.titlethis.setData({playIndex: index,'play.title': music.title,'play.singer': music.singer,'play.coverImgUrl': music.coverImgUrl,'play.currentTime': '00:00','play.duration': '00:00','play.percent': 0,state: 'running' // 新增})},changeItem: function (e) {this.setData({item: e.target.dataset.item})},changeTab: function (e) {this.setData({tab: e.detail.current})},play: function () {this.audioBam.play()this.setData({state: 'running'})console.log(this.data.state) // running},pause: function () {this.audioBam.pause()this.setData({state: 'paused'})console.log(this.data.state) // paused},// 点击播放下一曲的操作next: function () {var index = this.data.playIndex >= this.data.playlist.length - 1 ? 0 : this.data.playIndex + 1this.setMusic(index)console.log(this.data.state)// if (this.data.state === 'running') {// this.play()// }},// 点击播放列表中的某一项时进行该曲目的播放change: function (e) {this.setMusic(e.currentTarget.dataset.index)// this.play()}
})
- 页面实现效果
2、案例:录音机
(1)案例分析
- 需求:录音机是生活中的常用工具,它可以在开会的时候记录说话人的声音,也可以在生活中留下一段美妙歌声。录音机可以记录声音和播放声音,因此其成为新闻工作者工作的重要器材。
- 录音机”微信小程序页面分为顶部区域和按钮控制区域。
- 顶部区域展示录音时长
- 按钮控制区域中从左到右的3个按钮
- “播放录音”按钮
- “开始/暂停录音”按钮
- “停止录音”按钮
(2)知识储备-录音API
-
录音API
- 录音功能在日常生活中使用很广泛,使用该功能可以记录重要的工作内容、优美的歌声等。
- 微信小程序为开发者提供了录音API,使用录音API首先需要通过wx.getRecorderManager()方法获取到一个RecorderManager实例,该实例是一个全局唯一的录音管理器,用于实现录音功能。
var recorderManager = wx.getRecorderManager()
- RecorderManager实例的常用方法如下。
-
代码演示:在pages/index/index.js文件的onReady()函数中编写如下代码。
// 获取全局唯一的录音管理器RecorderManagervar recorderManager = wx.getRecorderManager()// 监听录音开始事件recorderManager.onStart(() => {console.log('录音开始');})// 监听录音停止事件recorderManager.onStop(res => {console.log('录音停止')console.log(res.tempFilePath)})// 开始录音recorderManager.start()// 5秒后自动停止录音setTimeout(() => {recorderManager.stop()}, 5000)
(2)知识储备-音频API
-
音频API
- 在微信小程序中,除了背景音频API可以实现播放音频的功能外,还可以通过音频API来播放音乐。
- 背景音频API与音频API的区别在于背景音频API支持后台播放,而音频API不支持后台播放。
- 在使用音频API时,需要通过以下代码创建一个InnerAudioContext实例。
var audioCtx = wx.createInnerAudioContext()
- InnerAudioContext实例特有的属性和方法如下。
-
代码演示:在pages/index/index.js文件的onReady()函数中编写如下代码。
// 创建InnerAudioContext实例var audioCtx = wx.createInnerAudioContext()// 设置音频资源地址audioCtx.src = 'http://127.0.0.1:3000/1.mp3'// 当开始播放音频时,输出调试信息audioCtx.onPlay(() => {console.log('开始播放')})// 开始播放audioCtx.play()
(3)案例实现
-
准备工作
- ①创建项目。在微信开发者工具中创建一个新的微信小程序项目,项目名称为“录音机”,模板选择“不使用模板”。
- ②配置导航栏。在pages/index/index.json文件中配置页面导航栏。
- ③配置页面样式。在pages/index/index.wxss文件中配置页面样式。
-
**封装录音功能:**在pages/utils/timer.js文件中封装相关功能。
var sec = 0
var timer = null
var pause = false
var callback = nullfunction formatTime(value) {var h = parseInt(value / 60 / 60 % 24)h = h < 10 ? '0' + h : hvar m = parseInt(value / 60 % 60)m = m < 10 ? '0' + m : mvar s = parseInt(value % 60)s = s < 10 ? '0' + s : sreturn h + ':' + m + ':' + s
}function createTimer() {return setInterval(() => {if (!pause) {++sec}callback && callback(formatTime(sec))}, 1000)
}module.exports = {onTimeUpdate(cb) {callback = cb},start() {if (pause) {pause = false}if (!timer) {timer = createTimer()}},pause() {pause = true},reset() {sec = 0pause = falseclearInterval(timer)timer = null}
}
- **实现录音功能:**在pages/index/index.js文件中实现录音相关功能。
// index.js
var timer = require('../../utils/timer.js')var audioCtx = wx.createInnerAudioContext()
var rec = wx.getRecorderManager()var tempFilePath = null
var onStopCallBack = nullrec.onStop(res => {tempFilePath = res.tempFilePathconsole.log('录音成功:' + tempFilePath)onStopCallBack && onStopCallBack(tempFilePath)
})Page({data: {time: '00:00:00', // 录音时长state: 0, // 录音状态,0表示停止,1表示开始,2表示暂停},rec: function () {switch (this.data.state) {case 0:rec.start()timer.onTimeUpdate(time => {this.setData({ time })})timer.start()this.setData({ time: '00:00:00', state: 1 })breakcase 1:rec.pause()timer.pause()this.setData({ state: 2 })breakcase 2:rec.resume()timer.start()this.setData({ state: 1 })break}},stop: function () {rec.stop()timer.reset()this.setData({ state: 0 })},play: function () {if (this.data.state > 0) {// 第1种情况,录音尚未完成onStopCallBack = tempFilePath => {onStopCallBack = nullaudioCtx.src = tempFilePathaudioCtx.play()this.setData({ time: '播放录音' })}this.stop()} else if (tempFilePath) {// 第2种情况,录音已完成audioCtx.src = tempFilePathaudioCtx.play()this.setData({ time: '播放录音' })} else {// 第3种情况,尚未录音this.setData({ time: '暂无录音' })}}
})
- **实现页面结构:**在pages/index/index.wxml文件中编写页面结构。
<!--index.wxml-->
<view class="top"><view class="top-title">录音机</view><view class="top-time">{{ time }}</view>
</view>
<view class="control"><view class="btn btn-play" bindtap="play" hover-class="btn-hover" hover-stay-time="50"></view><view class="btn btn-rec {{ state === 1 ? 'btn-rec-pause' : 'btn-rec-normal' }}" bindtap="rec" hover-class="btn-hover" hover-stay-time="50"></view><view class="btn btn-stop" bindtap="stop" hover-class="btn-hover" hover-stay-time="50"></view>
</view>
- 页面实现效果
3、案例:头像上传下载
(1)案例分析
- 需求:头像上传下载是微信小程序开发中常见的一个功能,一般会出现在用户中心模块中,用于设置用户的头像。
- 头像上传下载”微信小程序展示了头像信息,并提供了3个按钮,依次为“更改头像”“头像上传”“头像下载”。
- 点击“更改头像”按钮,可以重新选择头像图片;
- 点击“头像上传”按钮,可以将头像上传到服务器;
- 点击“头像下载”按钮,可以从服务器中下载头像图片并预览。
(2)知识储备-选择媒体API
-
选择媒体API
- 微信小程序提供了选择媒体API,其用于选择图片或视频,一般用于上传头像、上传照片和上传视频等功能中。
- 通过调用wx.chooseMedia()方法即可使用选择媒体API,该方法执行后,会提示用户拍摄图片或视频,或从手机相册中选择图片或视频。
- wx.chooseMedia()方法的常用选项如下。
- mediaType选项的合法值有3个
- image(只能拍摄图片或从相册选择图片)
- video(只能拍摄视频或从相册选择视频)
- mix(可同时选择图片和视频);
- sourceType选项的合法值有2个
- album(从相册选择)
- camera(使用相机拍摄)。
- mediaType选项的合法值有3个
-
代码演示:
- 在pages/index/index.wxml文件中编写如下代码。
<button bindtap="test">选择图片</button>
- 在pages/index/index.js实现test函数。
test: function () {wx.chooseMedia({count: 9, // 最多可以选择9个文件mediaType: ['image'], // 文件类型为只能拍摄图片或从相册中选图片sourceType: ['album', 'camera'], // 图片来源为从相册选择和使用相机拍摄success (res) {// 获取用户选择的文件const tempFilePath = res.tempFiles[0].tempFilePathconsole.log(tempFilePath)}}) }
(2)知识储备-图片预览API
-
图片预览API
- 微信小程序提供了图片预览API,通过图片预览API可以预览图片,且在预览过程中用户可以进行保存图片、发送给朋友等操作。
- 通过调用wx.previewImage()方法即可使用图片预览API。
- wx.previewImage()方法的常用选项如下。
- urls选项支持http或者https协议的网络图片地址,如果使用本地图片进行预览,会出现黑屏加载不出图片的情况。
-
代码演示:
- 在pages/index/index.wxml文件中编写页面结构。
<image src="{{ url }}" bindtap="previewImage" />
- 在pages/index/index.js文件中实现预览功能。
data: {url: 'http://127.0.0.1:3000/tree.jpg' },previewImage() {wx.previewImage({urls: [this.data.url // 需要预览的图片链接列表]}) }
(2)知识储备-文件上传API
-
文件上传API
- 在生活中,经常需要进行文件上传操作,例如更改头像需要将新的头像上传到服务器中。
- 微信小程序提供了文件上传API,使用文件上传API可以在微信小程序中发起一个POST请求,将本地资源上传到服务器。通过调用wx.uploadFile()方法即可使用文件上传API。
- wx.uploadFile()方法的常用选项如下
-
代码演示
wx.uploadFile({filePath: '文件路径',name: 'image',url: 'http://127.0.0.1:3000/upload',success: res => {console.log(res)}
})
(2)知识储备-文件下载API
-
文件下载API
- 在生活中,经常需要下载一些文件,例如将网络中某个参考资料下载到本地进行查看。
- 微信小程序提供了文件下载API,使用文件下载API可以实现文件下载功能。通过调用wx.downloadFile()方法即可使用文件下载API。
- wx.downloadFile()方法的常用选项如下。
-
代码演示
wx.downloadFile({url: 'http://127.0.0.1:3000/tree.jpg',success: res => {// 判断服务器响应的状态码if (res.statusCode === 200) {console.log(res.tempFilePath)}}
})
(3)案例实现
-
准备工作
- ①创建项目。在微信开发者工具中创建一个新的微信小程序项目,项目名称为“头像上传下载”,模板选择“不使用模板”。
- ②配置导航栏。在pages/index/index.json文件中配置页面导航栏。
- ③配置页面样式和准备图片。在pages/index/index.wxss文件中配置页面样式。在images文件夹中准备图片。
- ④启动服务器。切换工作目录到nodejs服务程序目录,打开命令提示符,然后在命令提示符中执行如下命令,启动服务器。
node index.js
-
**实现页面结构:**在pages/index/index.wxml文件中编写页面结构。
<!--index.wxml-->
<view class="imgbox"><image src="{{ imgUrl }}" mode="aspectFit" /><button type="primary" size="mini" bindtap="changeImg">更改头像</button><button type="primary" size="mini" bindtap="upload">头像上传</button><button type="primary" size="mini" bindtap="download">头像下载</button>
</view>
- **实现页面逻辑:**在pages/index/index.js文件的Page({})中编写逻辑代码。
// index.js
Page({data: {imgUrl: '/images/guest.png',tempFilePath: null},uploadFileUrl: null,// 图片选择changeImg: function () {wx.chooseMedia({count: 1,mediaType: ['image'],sourceType: ['album', 'camera'],success: res => {var tempFilePath = res.tempFiles[0].tempFilePaththis.setData({tempFilePath: tempFilePath,imgUrl: tempFilePath})}})},upload: function () {// 如果没有更改照片则提示更改后再上传if (!this.data.tempFilePath) {wx.showToast({title: '请您更改头像之后再进行上传操作',icon: 'none',duration: 2000})return}// 确认更改头像之后再上传wx.uploadFile({filePath: this.data.tempFilePath,name: 'image',url: 'http://localhost:3000/upload',success: res => {this.uploadFileUrl = JSON.parse(res.data).fileconsole.log('上传成功')}})},// 图片的下载download: function () {if (!this.uploadFileUrl) {wx.showToast({title: '请您上传头像之后再进行下载操作',icon: 'none',duration: 2000})return}wx.showLoading({title: '图片下载中,请稍后……',})wx.downloadFile({url: this.uploadFileUrl,success: res => {wx.hideLoading()console.log('下载完成')wx.previewImage({urls: [res.tempFilePath]})}})}
})
- 页面实现效果
4、案例:模拟时钟
(1)案例分析
- 需求:“模拟时钟”微信小程序是一个简约风格的动态时钟,该时钟时间与系统时间一致,且时针、分针、秒针会与系统时间同步更新,用户可以很方便地查看时间。
- “模拟时钟”微信小程序利用canvas组件绘制时钟,刻度为12个刻度,需要分别画出中心圆、外层大圆、时针、分针、秒针。
(2)知识储备-canvas组件
-
canvas组件
- 在HTML中,
<canvas>
标签可用于图形的绘制,也可用于创建图片特效和动画。 - 在微信小程序中,canvas组件也起着类似作用,可用于自定义绘制图形,该组件支持2D和WebGL的绘图。
<canvas></canvas>
- canvas组件的常用属性如下。
- 在HTML中,
-
代码演示:
- 在pages/index/index.wxml文件中编写页面结构。
<canvas id="myCanvas" type="2d"></canvas>
- 在pages/index/index.wxss文件中编写canvas组件的页面样式。
#myCanvas {display: block;width: 300px;height: 150px;position: relative;border: 1px solid red; }
(2)知识储备-画布API
-
画布API
- 通过canvas组件创建画布后,要想在画布中绘制图案,需要通过画布API来完成。
- 若要使用画布API,需要先获取Canvas实例,然后通过Canvas实例获取RenderingContext(渲染上下文)实例,最后通过RenderingContext实例的属性和方法完成绘图操作。
wx.createSelectorQuery() .select('#myCanvas') // 页面中<canvas>标签的id .fields({ node: true, size: true }) .exec(res => {// 获取Canvas实例const canvas = res[0].node// 调用getContext()方法获取RenderingContext实例const ctx = canvas.getContext('2d') })
- RenderingContext实例的常用属性和方法如下
-
代码实现
- 在pages/index/index.wxml文件中编写页面结构。
<!--index.wxml--> <canvas id="draw" type="2d"></canvas>
- 在pages/index/index.js文件中编写代码绘制图形。
// index.js Page({onReady: function () {wx.createSelectorQuery().select('#myCanvas') // 页面中<canvas>标签的id.fields({node: true,size: true}).exec(res => {// 获取Canvas实例const canvas = res[0].node// 调用getContext()方法获取RenderingContext实例const ctx = canvas.getContext('2d')})wx.createSelectorQuery().select('#draw').fields({node: true,size: true}).exec(res => {const canvas = res[0].nodeconst ctx = canvas.getContext('2d')this.drawRect(ctx)this.drawSmile(ctx)})},drawRect: function (ctx) {ctx.fillStyle = 'rgba(0, 0, 200, 0.5)'ctx.fillRect(10, 10, 150, 50)},drawSmile: function (ctx) {// 设置线条颜色为红色,线条宽度为2pxctx.strokeStyle = '#f00'ctx.lineWidth = '2'// 移动画笔坐标位置,绘制外部大圆ctx.moveTo(160, 80)ctx.arc(100, 80, 60, 0, 2 * Math.PI, true)// 移动画笔坐标位置,绘制外部嘴巴线条ctx.moveTo(140, 80)ctx.arc(100, 80, 40, 0, Math.PI, false)// 移动画笔坐标位置,绘制左眼圆圈ctx.moveTo(85, 60)ctx.arc(80, 60, 5, 0, 2 * Math.PI, true)// 移动画笔坐标位置,绘制右眼圆圈ctx.moveTo(125, 60)ctx.arc(120, 60, 5, 0, 2 * Math.PI, true)ctx.stroke()} })
(3)案例实现
- 准备工作
- ①创建项目。在微信开发者工具中创建一个新的微信小程序项目,项目名称为“模拟时钟”,模板选择“不使用模板”。
- ②配置导航栏。在pages/index/index.json文件中配置页面导航栏。
-
初始化画布
- ①在pages/index/index.wxml文件中定义canvas组件。
<canvas id="myCanvas" type="2d"></canvas>
- ②在pages/index/index.wxss文件中编写canvas组件的样式。
#myCanvas {width: 100%;height: 100%;position: fixed;}
-
**绘制代码封装:**在项目根目录下创建utils文件夹,将绘制功能封装到utils/drawClock.js文件中
// 将角度转换为弧度 const D6 = 6 * Math.PI / 180 const D30 = 30 * Math.PI / 180 const D90 = 90 * Math.PI / 180module.exports = canvas => {const ctx = canvas.getContext('2d')// 计算表盘半径,留出30px外边距var radius = canvas.width / 2 - 30return () => {// 在绘制时钟前先清除画布ctx.clearRect(0, 0, canvas.width, canvas.height);// 设置坐标轴原点为画布的中心点ctx.translate(canvas.width / 2, canvas.height / 2)// 绘制表盘drawDial(ctx, radius)// 绘制指针drawHand(ctx, radius)// 绘制完成后将画布恢复成初始状态ctx.rotate(D90)ctx.translate(-canvas.width / 2, -canvas.height / 2)ctx.restore()} } // 表盘整体部分,包括外层大圆和中心圆部分 function drawDial(ctx, radius) {// 绘制外层大圆ctx.lineWidth = '2' // 设置线条宽度为2pxctx.beginPath() // 开始一条路径ctx.arc(0, 0, radius, 0, 2 * Math.PI, true) // 画弧线ctx.stroke() // 绘制// 绘制中心圆ctx.lineWidth = '1'ctx.beginPath()ctx.arc(0, 0, 8, 0, 2 * Math.PI, true) // 中心圆半径为8pxctx.stroke()// 绘制大刻度盘ctx.lineWidth = '5'// 从三点钟开始,转圈进行绘制for (var i = 0; i < 12; ++i) {// 以原点为中心顺时针旋转,多次调用旋转的角度会叠加,从而画出倾斜的线ctx.rotate(D30) // 大刻度盘绘制12个线条ctx.beginPath()// 设置起始点,现在原点是在中心点,调用moveTo()方法将线条移动到外层大圆上ctx.moveTo(radius, 0)// 设置终点ctx.lineTo(radius - 15, 0) // 大刻度长度15pxctx.stroke()}// 绘制小刻度盘ctx.lineWidth = '1'for (var i = 0; i < 60; ++i) {ctx.rotate(D6)ctx.beginPath()ctx.moveTo(radius, 0)ctx.lineTo(radius - 10, 0) // 小刻度盘长度10pxctx.stroke()}// 绘制数字ctx.font = '22px sans-serif'ctx.textBaseline = 'middle' // 文本垂直居中// 文本距离时钟中心点半径,让文字与表盘线有距离var r = radius - 30// 文本位置是绕外圈圆的,所以要计算文本坐标for (var i = 1; i <= 12; ++i) {// 利用三角函数计算文本坐标var x = r * Math.cos(D30 * i - D90)var y = r * Math.sin(D30 * i - D90)// console.log(x, y)// 位置进行调整if (i > 10) {// 在画布上绘制文本,fillText(文本, 左上角x坐标, 左上角y坐标)ctx.fillText(i, x - 12, y) // 绘制11和12} else {ctx.fillText(i, x - 6, y) // 绘制1~10}} } // 绘制指针 function drawHand(ctx, radius) {var t = new Date() // 获取当前时间var h = t.getHours() // 小时var m = t.getMinutes() // 分var s = t.getSeconds() // 秒h = h > 12 ? h - 12 : h // 将24小时制转化为12小时制// 时间从3点开始,逆时针旋转90°,指向12点ctx.rotate(-D90)// 绘制时针ctx.save() // 记录旋转状态ctx.rotate(D30 * (h + m / 60 + s / 3600))ctx.lineWidth = '6'ctx.beginPath()ctx.moveTo(-20, 0) // 线条起点(针尾留出20px)ctx.lineTo(radius / 2.6, 0) // 线条长度ctx.stroke()ctx.restore() // 恢复旋转状态,避免旋转叠加// 绘制分针ctx.save()ctx.rotate(D6 * (m + s / 60))ctx.lineWidth = '4'ctx.beginPath()ctx.moveTo(-20, 0)ctx.lineTo(radius / 1.8, 0)ctx.stroke()ctx.restore()// 绘制秒针ctx.save()ctx.rotate(D6 * s)ctx.lineWidth = '2'ctx.beginPath()ctx.moveTo(-20, 0)ctx.lineTo(radius / 1.6, 0)ctx.stroke()ctx.restore() }
-
**绘制代码实现:**在pages/index/index.js实现绘制代码。
// index.js const drawClock = require('../../utils/drawClock.js')Page({timer: null, // 定时器onReady: function () {wx.createSelectorQuery().select('#myCanvas').fields({ node: true, size: true }).exec(res => {const canvas = res[0].nodecanvas.width = res[0].widthcanvas.height = res[0].heightconst draw = drawClock(canvas)draw()this.timer = setInterval(draw, 1000)})},// 在页面卸载时清除定时器onUnload: function () {clearInterval(this.timer)} })
- 页面实现效果