零、文章目录
微信小程序04-常用API上
1、案例:音乐播放器
(1)案例分析
- 需求:"音乐播放器"微信小程序可以让用户随时随地享受音乐,给用户带来了便捷的音乐体验,且支持后台播放,用户可以在听音乐的同时进行其他操作。
- "音乐播放器"微信小程序的页面由上、中、下共3个部分组成,这3个部分分别是标签栏区域、内容区域和播放器区域。
- ①标签栏区域:该区域有音乐推荐、播放器和播放列表3个标签按钮,通过点击标签按钮可以进行标签页的切换。
- ②内容区域:通过左右滑动可以实现音乐推荐、播放器和播放列表3个标签页的切换。这3个标签页的具体说明如下。
- 音乐推荐:用于向用户推荐一些歌曲。
- 播放器:用于显示当前播放音乐的信息、专辑封面、播放进度和时间。其中,音乐信息包括当前播放音乐的标题和歌手。
- 播放列表:用于显示当前播放的曲目列表,用户可以进行曲目切换。
- ③播放器区域:显示当前播放的音乐信息,并且提供了3个按钮,按钮的功能依次为"切换到播放列表""播放/暂停""下一曲"。
(2)知识储备-scroll-view组件
-
scroll-view组件
- 当一个容器中的内容有很多时,如果容器无法完整显示内容,则可以通过滚动操作来查看完整内容。
- 在微信小程序中,可以通过scroll-view组件来实现滚动效果,它支持横向滚动和纵向滚动,默认是不滚动的,需要通过scroll-x和scroll-y属性允许横向和纵向滚动。
html<scroll-view>实现可滚动视图区域</scroll-view>
- scroll-view组件的常用属性如下
-
代码演示:
- 在pages/index/index.wxml文件中编写如下代码。
html<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的值。
jsscroll: 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文件中编写页面结构。
html<slider bindchanging="sliderChanging" show-value="true" />
- 在pages/index/index.js文件中编写事件处理函数sliderChanging()。
jssliderChanging: function (e){ console.log(e.detail.value) }
(2)知识储备-<include>
标签
-
**
<include>
标签:**用于引用其他文件的代码,相当于把引用的代码复制到<include>
标签的位置。<include>
标签的用途主要有以下两点- **方便地查找代码:**当一个WXML页面中的代码过多时,会给代码的维护带来麻烦,有时为了找到某一处代码可能需要翻阅几百行。这时可以利用
<include>
标签将代码拆分到多个文件中,从而可以更方便地查找代码。 - **代码复用减少维护:**当多个WXML页面中有相同的部分时,可以将这些公共部分抽取出来,保存到一个单独的WXML文件中,然后在用到的地方通过
<include>
标签引入。这样可以减少重复的代码,并且修改时只需要修改一次。
- **方便地查找代码:**当一个WXML页面中的代码过多时,会给代码的维护带来麻烦,有时为了找到某一处代码可能需要翻阅几百行。这时可以利用
-
代码演示
- ①在pages/index/index.wxml文件中编写页面结构。
html<!-- index.wxml --> <include src="header.wxml" /> <view>body</view> <include src="footer.wxml" />
- ②在pages/index/header.wxml文件中编写头部的页面结构。
html<view>header</view>
- ③在pages/index/footer.wxml文件中编写尾部的页面结构。
html<view>footer</view>
- 运行后,实际得到的pages/index/index.wxml文件如下
html<view>header</view> <view>body</view> <view>footer</view>
(2)知识储备-背景音频API
-
背景音频API
- 在微信小程序中,使用背景音频API可以实现音频的后台播放。
- 在使用背景音频API前,需要在app.json文件中配置requiredBackgroundModes属性,开启微信小程序后台音频播放功能。
json"requiredBackgroundModes": ["audio"]
- 背景音频API的使用方法是,先通过wx.getBackgroundAudioManager()方法获取到一个BackgroundAudioManager实例,然后通过该实例的相关属性和方法实现背景音频的播放。
jsvar audioGbam = wx.getBackgroundAudioManager()
- BackgroundAudioManager实例常用的属性和方法如下
-
代码演示:在pages/index/index.js文件的onReady()函数中编写如下代码。
js
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文件中配置页面导航栏。
json{ "navigationBarTitleText": "音乐", "navigationBarBackgroundColor": "#17181a", "navigationBarTextStyle": "white" }
- ④配置页面样式和准备图片。在pages/index/index.wxss文件中配置页面样式。在images文件夹中准备图片。
- ⑤启动服务器。切换工作目录到nodejs服务程序目录,打开命令提示符,然后在命令提示符中执行如下命令,启动服务器。
bashnode index.js
-
实现页面结构:
- 在pages/index/index.wxml文件中编写"音乐播放器"微信小程序的页面结构。
html<!--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文件中的代码如下。
html<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文件中的代码如下。
html<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文件中的代码如下。
html<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文件中获取页面所需的数据。
js
// index.js
// 格式化时间
function formatTime(time) {
var minute = Math.floor(time / 60) % 60;
var second = Math.floor(time) % 60
return (minute < 10 ? '0' + minute : minute) + ':' + (second < 10 ? '0' + second : second)
}
Page({
data: {
item: 0,
tab: 0,
// 播放列表数组playlist
playlist: [{
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 = currentTime
this.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 / 100
this.audioBam.seek(second)
setTimeout(() => {
this.sliderChangeLock = false
}, 1000)
},
sliderChanging: function (e) {
var second = e.detail.value * this.audioBam.duration / 100
this.sliderChangeLock = true
this.setData({
'play.currentTime': formatTime(second),
})
},
// 设置当前播放的曲目
setMusic: function (index) {
var music = this.data.playlist[index]
this.audioBam.src = music.src
this.audioBam.title = music.title
this.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 + 1
this.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实例,该实例是一个全局唯一的录音管理器,用于实现录音功能。
jsvar recorderManager = wx.getRecorderManager()
- RecorderManager实例的常用方法如下。
-
代码演示:在pages/index/index.js文件的onReady()函数中编写如下代码。
js
// 获取全局唯一的录音管理器RecorderManager
var 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实例。
jsvar audioCtx = wx.createInnerAudioContext()
- InnerAudioContext实例特有的属性和方法如下。
-
代码演示:在pages/index/index.js文件的onReady()函数中编写如下代码。
js
// 创建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文件中封装相关功能。
js
var sec = 0
var timer = null
var pause = false
var callback = null
function formatTime(value) {
var h = parseInt(value / 60 / 60 % 24)
h = h < 10 ? '0' + h : h
var m = parseInt(value / 60 % 60)
m = m < 10 ? '0' + m : m
var s = parseInt(value % 60)
s = s < 10 ? '0' + s : s
return 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 = 0
pause = false
clearInterval(timer)
timer = null
}
}
- **实现录音功能:**在pages/index/index.js文件中实现录音相关功能。
js
// index.js
var timer = require('../../utils/timer.js')
var audioCtx = wx.createInnerAudioContext()
var rec = wx.getRecorderManager()
var tempFilePath = null
var onStopCallBack = null
rec.onStop(res => {
tempFilePath = res.tempFilePath
console.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 })
break
case 1:
rec.pause()
timer.pause()
this.setData({ state: 2 })
break
case 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 = null
audioCtx.src = tempFilePath
audioCtx.play()
this.setData({ time: '播放录音' })
}
this.stop()
} else if (tempFilePath) {
// 第2种情况,录音已完成
audioCtx.src = tempFilePath
audioCtx.play()
this.setData({ time: '播放录音' })
} else {
// 第3种情况,尚未录音
this.setData({ time: '暂无录音' })
}
}
})
- **实现页面结构:**在pages/index/index.wxml文件中编写页面结构。
html
<!--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文件中编写如下代码。
html<button bindtap="test">选择图片</button>
- 在pages/index/index.js实现test函数。
jstest: function () { wx.chooseMedia({ count: 9, // 最多可以选择9个文件 mediaType: ['image'], // 文件类型为只能拍摄图片或从相册中选图片 sourceType: ['album', 'camera'], // 图片来源为从相册选择和使用相机拍摄 success (res) { // 获取用户选择的文件 const tempFilePath = res.tempFiles[0].tempFilePath console.log(tempFilePath) } }) }
(2)知识储备-图片预览API
-
图片预览API
- 微信小程序提供了图片预览API,通过图片预览API可以预览图片,且在预览过程中用户可以进行保存图片、发送给朋友等操作。
- 通过调用wx.previewImage()方法即可使用图片预览API。
- wx.previewImage()方法的常用选项如下。
- urls选项支持http或者https协议的网络图片地址,如果使用本地图片进行预览,会出现黑屏加载不出图片的情况。
-
代码演示:
- 在pages/index/index.wxml文件中编写页面结构。
html<image src="{{ url }}" bindtap="previewImage" />
- 在pages/index/index.js文件中实现预览功能。
jsdata: { 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()方法的常用选项如下
-
代码演示
js
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()方法的常用选项如下。
-
代码演示
js
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服务程序目录,打开命令提示符,然后在命令提示符中执行如下命令,启动服务器。
bashnode index.js
-
**实现页面结构:**在pages/index/index.wxml文件中编写页面结构。
html
<!--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({})中编写逻辑代码。
js
// 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].tempFilePath
this.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).file
console.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的绘图。
html<canvas></canvas>
- canvas组件的常用属性如下。
- 在HTML中,
-
代码演示:
- 在pages/index/index.wxml文件中编写页面结构。
html<canvas id="myCanvas" type="2d"></canvas>
- 在pages/index/index.wxss文件中编写canvas组件的页面样式。
css#myCanvas { display: block; width: 300px; height: 150px; position: relative; border: 1px solid red; }
(2)知识储备-画布API
-
画布API
- 通过canvas组件创建画布后,要想在画布中绘制图案,需要通过画布API来完成。
- 若要使用画布API,需要先获取Canvas实例,然后通过Canvas实例获取RenderingContext(渲染上下文)实例,最后通过RenderingContext实例的属性和方法完成绘图操作。
jswx.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文件中编写页面结构。
html<!--index.wxml--> <canvas id="draw" type="2d"></canvas>
- 在pages/index/index.js文件中编写代码绘制图形。
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].node const 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) { // 设置线条颜色为红色,线条宽度为2px ctx.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组件。
html<canvas id="myCanvas" type="2d"></canvas>
- ②在pages/index/index.wxss文件中编写canvas组件的样式。
css#myCanvas { width: 100%; height: 100%; position: fixed; }
-
**绘制代码封装:**在项目根目录下创建utils文件夹,将绘制功能封装到utils/drawClock.js文件中
js// 将角度转换为弧度 const D6 = 6 * Math.PI / 180 const D30 = 30 * Math.PI / 180 const D90 = 90 * Math.PI / 180 module.exports = canvas => { const ctx = canvas.getContext('2d') // 计算表盘半径,留出30px外边距 var radius = canvas.width / 2 - 30 return () => { // 在绘制时钟前先清除画布 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' // 设置线条宽度为2px ctx.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) // 中心圆半径为8px ctx.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) // 大刻度长度15px ctx.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) // 小刻度盘长度10px ctx.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实现绘制代码。
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].node canvas.width = res[0].width canvas.height = res[0].height const draw = drawClock(canvas) draw() this.timer = setInterval(draw, 1000) }) }, // 在页面卸载时清除定时器 onUnload: function () { clearInterval(this.timer) } })
- 页面实现效果