2.3 媒体组件和导航组件
媒体组件和导航组件是微信组件中非常零散的一个部分。尤其是导航组件,可能是从小程序发布以来改动最多的一个组件。
2.3.1 导航组件:navigator
navigator组件存在两种应用:一种是实现应用内的跳转;另一种是实现小程序之间的跳转。对于应用内的跳转,支持5种跳转方式,分别对应着API中的5种跳转方式,如表2-5所示。
表2-5 应用内的跳转方式
应用之间的跳转现在也必须要使用navigator组件实现,最新的小程序更新不再支持从任何API直接跳转至其他小程序。为了防止大量不合规的小程序矩阵,跳转的任何小程序都必须在app.json中进行全局配置,指定需要跳转的AppID。而现在,每个小程序可跳转的目的小程序的数量限制为10个,超过的小程序将会无法通过上传和审核。
navigator组件的使用方法如下所示。
<navigator url="跳转地址和参数" open-type="redirect" hover-class="other-navigator-hover" >单击此处跳转</navigator >
应用之间的跳转方法如下所示。
<navigator target="miniProgram" open-type="navigate" app-id="" path="" extra-data="" version="release" >打开绑定的小程序</navigator >
2.3.2 图片组件:image
顾名思义,image就是为了显示一张图片而存在的组件,当然该组件还支持绑定单击事件。同时,为了方便用户的使用和适应各种不同横纵比的图片,微信为开发者准备了方便的缩放和裁剪模式,通过更改mode的有效值就可以实现。mode的有效值如表2-6所示。
表2-6 mode的有效值
应用中大量使用图片制作的UI组件,则需要使用image组件显示在一个页面中。基于屏幕显示分辨率的差异,甚至横纵比例不同,要想在不同的手机端达到类似的显示效果是一件非常困难的事情。这时有一个思路,就是使用大量重复性的背景或者图片。
如果一些单页的场景为了美观需要大量使用图片作为UI背景,需要采用<image mode="widthFix"></image>这样的方式调用图片。这种调用方式会自动设置图片的高度。如果以宽度为标准,则需要指定图片的宽度,这时可以使用相对的百分比或者vw、vh这种相对于屏幕的宽度或者高度。
如果想实现图2-2这样的应用效果并在每个手机中都保证其基本的显示不出现大的变化,则需要拆分页面,使用相对化布局进行微调。为了不使图片本身的比例出现问题,应尽量使用mode="widthFix"这个模式。
图2-2 实例小程序
这里通过一个完整的小程序来使用image组件,也会用到button组件和form组件。该小程序的页面部分代码如下所示。
<view class="christmasPage"> <!--用户的树--> <view class="christmasTree"> <!-背景--> <image src=" url " mode="widthFix" style="position: absolute;width: 100vw;bottom: 0"></image> <!-标题词--> <image src=" url " mode="widthFix" style="position: absolute;top:13vw;width: 80vw;left: 10vw"></image> <!-圣诞树--> <image src="url" mode="widthFix" style="position: absolute;bottom: 2vw;width: 100vw"></image> </view> </view>
注意:在同样的在image组件上也可以绑定事件,即单击图片时触发单击事件。
虽然在image组件上也可以绑定事件,但在一般情况下,用户单击image组件时虽然会触发事件,但该事件并不会触发携带一个用户的formId。这时需要将图片和按钮结合在一起使用,参考下面的image页面。
image页面中的一张图的外部嵌套了一个按钮组件,但如果直接嵌套在image组件的外部,如以下代码所示,则整个页面布局会因为按钮组件自带的样式而发生错乱,如图2-3所示。
图2-3 显示问题
<button> <image src="../public/testImg.png" mode="widthFix" style="width: 80%"></image> </button>
如图2-3所示,该按钮的宽度依旧是100%,其本身的边框依旧存在,甚至还留下了一些padding造成的间距。所以,应该对这类按钮组件进行样式的覆盖,并加上button组件外部嵌套的form组件,这样单击图片按钮时,就可以在执行事件时收获一个用户的formId。
对小程序而言,微信开发时获得的几乎所有的开放能力,包括用户的数据,都必须要由用户完成一个单击事件,因此我们同时在这个按钮上加上open-type属性。也就是说,当用户单击一个图片时,实际上触发了按钮组件上3个事件,而在实例中这3个事件会在控制台中打印。
本实例页面和样式部分的代码如下所示,其中样式common_img_btn和button[class^="common"]::after是去除按钮本身样式的自定义样式。
<style lang="less"> .common_img_btn { border: none !important; padding: 0 !important; background-color: rgba(0,0,0,0)!important; } button[class^="common"]::after { border: 0; } .pageButton { position: fixed; width: 60vw; top: 0; left: 20vw; } </style> <template> <form bindsubmit="formSubmit" report-submit="true"> <!--绑定按钮上的3个事件--> <button class="common_img_btn pageButton" @tap="someFun()" formType="submit" open-type="getUserInfo" bindgetuserinfo="onGotUserInfo"> <image src="../public/testImg.png" mode="widthFix" style="width: 100%"></image> </button> </form> <!--<button>--> <!--<image src="../public/testImg.png" mode="widthFix"--> <!--style="width: 80%"></image>--> <!--</button>--> </template>
页面的监听函数写在了methods对象中,完整的代码如下所示,分别是在控制台中打印和按钮的原本监听方法的弹窗。
methods = { someFun(){ wepy.showModal({ title: '提示', content: '用户单击图片事件' }) }, onGotUserInfo(e){ console.log(e.detail) }, formSubmit(e){ console.log(e.detail.formId) } }
本例最终效果如图2-4所示,当单击事件触发时会提示用户获取资料的权限。这个弹窗在现在的所有微信小程序中是不能避免的,而用户选择后在一段时间内都不会再次出现。
图2-4 权限获取提示
当用户单击“允许”按钮后,可以在控制台打印出formId并且弹出原本绑定按钮中的弹出框,如图2-5所示。
图2-5 打印用户资料和formId
2.3.3 视频组件:video和API:wx.createVideoContext
视频类的应用在之前的小程序中并不多见,但这几年短视频和直播类应用普遍火热,使用小程序制作的视频类应用也越来越多了,小程序中video组件正好为我们提供了这个功能。
video组件是一个原生组件,也就是说,video组件并不能覆盖cover-*以外的其他组件。当然,一般使用video组件时也不会在其上覆盖其他组件。video组件不仅为我们提供了视频播放的功能,还提供了包括弹幕、全屏、进度控制等功能,这些功能只需要简单配置即可。video组件的属性如表2-7所示。
表2-7 video组件的属性
除了上述属性,video还包括了多个绑定事件,可以说video组件是所有组件中属性和内容最多的一个组件。支持的绑定事件包括:
● Bindplay:当开始/继续播放时触发play事件。
● Bindpause:当暂停播放时触发pause事件。
● Bindended:当播放到末尾时触发ended事件。
● Bindtimeupdate:播放进度变化时触发,event.detail = {currentTime,duration}。触发频率为250ms一次。
● Bindfullscreenchange:视频进入和退出全屏时触发,event.detail = {fullScreen,direction},direction取vertical或horizontal。
● Bindwaiting:视频出现缓冲时触发。
● Binderror:视频播放出错时触发。
● Bindprogress:加载进度变化时触发,只支持一段加载。event.detail = {buffered},百分比。
video除了视频播放功能,本身的可用性和难度并不高,只需要配置src就可以实现视频的播放和控制了。服务器端的video视频内容会导致自身服务器大量带宽被占用,出现播放卡顿等,所以推荐使用腾讯云文件方式进行播放。
注意:<video>默认宽度为300px、高度为225px,可通过wxss设置宽度和高度。
在video组件的使用中,不仅用户可以在界面上进行基本的操作,开发者通过JavaScript脚本也可以实现对视频的控制和操作,但这需要使用专门为video提供的一个API—wx.createVideoContext(string id,Object this)。其中的id为video组件的id,Object表示如果在自定义组件中使用该元素,需要指定其实例的this。
这个API会返回一个JavaScript对象VideoContext,它包含了一些视频的控制方法,如表2-8所示。
表2-8 VideoContext包含的视频控制方法
2.3.4 拍照组件:camera和API:wx.createCameraContext
使用拍照组件会调用系统的相机操作,调用camera组件时会在界面上显示相机的拍照界面。
camera组件是系统原生组件,会悬浮在一般组件的最上层。在微信客户端达到了6.7.3之后可以使用扫二维码的功能,但是使用该组件会默认要求用户的授权,如果用户不允许则不会出现该组件。
注意:camera组件在隐藏时设置成hidden或者display:none,或者使用fixed定位将整个组件移出屏幕,在部分手机或者系统中可能会出现无法隐藏的情况,而官方暂时没有给出解决方案,可以使用跳转页面的方式进行拍照。
一般而言,camera组件用于需要拍照的业务,如果只是处理图片或从用户相册中选择照片的应用场景,并不推荐使用camera组件,而是直接使用wx.chooseImage(Object object)这个API,直接使用系统的相册和相机来选择照片或拍照。
使用camera组件也可以实现拍照和摄像功能,下面在2.3.2节的小程序中添加camera页面。
页面代码如下所示,这里除camera组件以外,还在相机上覆盖了一个基础的cover-view组件,用来绘制一个方形的框,这使得用户的头像可以根据我们的意愿出现在该方形区域中。当然,camera组件并不能直接截取或仅获取该区域的图像,截取工作需要在后端或者小程序中调用Canvas组件截取。
<template> <view style="position: fixed;width:100vw;"> <!-添加相机组件--> <camera device-position="front" flash="off" binderror="error" style="width: 100vw; height: 100vh;"> <!—覆盖在相机组件上的组件--> <cover-view class="controls"> <cover-view class="coverImg"> </cover-view> <cover-view style="bottom: 20vh;text-align:center;position: fixed;color: #fff;font-size: 28rpx;width: 100vw"> 您框内的图片将会作为您的抽奖头像 </cover-view> <!-拍照事件的监听按钮--> <button @tap="takePic" class="input" style="bottom:10vh;color: #000">单击 拍照</button> </cover-view> </camera> </view> </template>
上述代码实现了一个全屏幕的相机(配置使用了前置摄像头并且不使用闪光灯的效果),并且在相机上绘制了一个方形的框、一行提示文字,以及一个拍照的按钮,此按钮已经绑定了takePic方法用于获取照片。页面的基本样式代码如下所示。
<style lang="less"> .controls { width: 100vw; height: 100vh; } .coverImg { position: fixed; left: 1vw; top: 25vh; width: 96vw; height: 96vw; border: 1vw solid #fff7cc; } .input { position: fixed; width: 60vw; left: 20vw; border-radius: 5vw; height: 10vw; text-align: center; color: #fff; font-size: unit(28,rpx); } </style>
下面要做的就应该是对用户拍照事件的监听和使用Camera组件获取照片了。这里采用存储在缓存文件的形式,也可以仅获取临时地址,或者在拍照结束后直接将图片上传到服务器端。
这里介绍一个新的API方法wx.createCameraContext,用于获取一个相机的实例,从而实现拍照功能。该实例拥有3个方法:CameraContext.takePhoto用于拍摄照片,CameraContext.startRecord和CameraContext.stopRecord用于开始和结束录像。在CameraContext.takePhoto方法中应用success回调可以获得拍摄照片的临时地址,而使用CameraContext.stopRecord可以获得拍摄视频的临时文件地址。这里简单地采用了CameraContext.takePhoto这个API方法来拍摄照片,其使用效果和方法如以下逻辑代码所示。
<script> import wepy from 'wepy' // 页面代码 export default class camera extends wepy.page { components = {} // 页面数据内容 data = { imgPath: '', imgWidth: 0, imgHeight: 0 } computed = {} // 页面监听方法 methods = { takePic(){ const that = this const ctx = wx.createCameraContext() wx.showLoading() // 拍照API的使用 ctx.takePhoto({ // quality: 'high', success:(res)=> { that.imgPath = res.tempImagePath that.$apply() wx.getImageInfo({ src: that.imgPath, success:(res)=> { that.imgWidth = res.width that.imgHeight = res.height wx.saveFile({ tempFilePath: that.imgPath, success(res){ wepy.setStorageSync('userImg',{ path: res.savedFilePath, width: that.imgWidth, height: that.imgHeight }) setTimeout(()=> { wx.hideLoading() wepy.reLaunch({ url: '跳转回的路径' }) },2000) } }) // wepy.setStorageSync('userImg', {path: that.imgPath, width: that.imgWidth,height: that.imgHeight}) } }) }, fail(res){ // 失败时的打印 console.log('takePhoto fail res') console.log(res) }, complete(res){ // 最终打印结果 console.log('takePhoto complete res') console.log(res) } } ) }, error(e){ console.log(e.detail) } } events = {} onLoad(){ } } </script>
wx.createCameraContext获得一个基础的Camera的实例,而后调用其takePhoto方法获得用户拍摄的照片。这里选择将文件保存后,获取其路径和使用wx.getImageInfo这个API方法获得该照片的高度和宽度信息并且保存。
页面的调试不一定非要在首页或者底部增加一个跳转至该页面的链接,只需要新建一个编译模式即可,如图2-6所示。
图2-6 新增编译模式
编译模式需要选择启动页面的地址、携带的参数、如何进入场景(用于确定模拟触发当前的页面状态是重新加载,还是从隐藏到显示)。通过该编译模式即可调试任何页面及页面携带的参数,也可以测试二维码和小程序码,显示效果如图2-7所示。
图2-7 camera页面显示效果
注意:由于模拟器的限制,在没有选择专门的设备时都会显示如图2-7所示的效果。在这里可以选真机测试,在手机上运行,如果用户阻止了该组件,则会出现空白页面。