H5通用功能点汇总
H5通用功能点汇总
连做三个 h5 项目,或多或少积累一点开发的经验,此篇文章一是记录,二为分享。
开整!
基础环境#
三次项目皆使用 vue 框架开发,使用 vue-cli3.x 生成项目主体已是相当熟稔,vue-cli 传送门。
使用 axios 请求库,也可自行选择顺手的使用。
微信 sdk 的再次封装#
微信官方提供一个 jsbridge 脚本,能够很方便的使用设备的一些功能及微信本身提供的功能。
我们需要做的是在项目中更好的调用它。
-
在
public/index.html模版 html 里的 head 中加入 wxsdk(传送门),或直接复制此条http://res.wx.qq.com/open/js/jweixin-1.4.0.js(微信目前最新版本 js) -
在
src目录下,我们创建一个utils文件夹内创建wxsdk.js,在其中加上
import axios from 'axios';
const wxsdk = { init(url) { // 顾名思义,用于初始化wxconfig的方法 return new Promise((resolve) => { axios.get('请求微信config的后端接口', { params: { url } }).then(({ appId, timestamp, nonceStr, signature }) => { wx.config({ debug: false, // 后面可以用于调试config是否配置成功 appId, timestamp, nonceStr, signature, jsApiList: [ 'chooseImage', 'uploadImage', 'getLocalImgData', 'updateAppMessageShareData', 'updateTimelineShareData', ], // 在此注册你需要使用的api }); resolve(); // 配置完成的promise回调 }); }); },};
export default wxsdk;- 下面把初始化方法放入全局的路由守卫中,使用户在每个页面打开时都注册一次。
// router.js中import Vue from 'vue'import Router from 'vue-router'import wxsdk from '../utils/wxsdk'
Vue.use(Router)
const router = new Router({ // 没看错,使用的是hash, // 使用hash是因为方便后端直接使用链接进行其他的带参操作 // 同时,也避免了在nginx中部署时对前端路由的操控 mode: 'hash', routes: [...]})
router.afterEach((to, from, next) => { // 此处较为关键,由于我们使用的是单页应用,内部路由皆为虚拟路由, // 并且是hash模式,需要获取 # 前的路由作为请求路径 wxsdk.init(location.href.split('#')[0])})
export default router- 加入一个封装上传本地图片方法作为例子
chooseImage (count) { return new Promise(resolve => { wx.ready(() => { wx.chooseImage({ count, // 用于控制上传图片的数量 sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success: ({ localIds }) => { resolve(localIds) } }) }) })},- 若项目中有需要判断微信的 js 是否加载完毕,可以使用下面方法
// wxsdk.js中const wxsdk = { ..., check (cb) { if (typeof WeixinJSBridge === 'object' && typeof WeixinJSBridge.invoke === 'function') { cb() } else { if (document.addEventListener) { document.addEventListener('WeixinJSBridgeReady', cb, false) } else if (document.attachEvent) { document.attachEvent('WeixinJSBridgeReady', cb) document.attachEvent('onWeixinJSBridgeReady', cb) } } },}- 可以在页面的
mounted周期中引入
<script>export default { mounted() { this.$wxsdk.check(() => { console.log('微信js加载完毕,进入页面'); }); },};</script>- 为了方便我们对 wxsdk 的使用,可以在
main.js中将此对象挂载到 vue.prototype 上
import wxsdk from './utils/wxsdk';
Vue.prototype.$wxsdk = wxsdk;微信 jssdk 的封装使用基本完成。
页面适配#
我使用的是 rem 单位布局,根据设配的屏幕宽度与设计稿的宽度比计算出根元素的像素大小,从而使一张设计图适配不同的手机屏幕
// utils/util.js中
const deviceRem = (size) => { const docEl = document.documentElement; const clientWidth = docEl.clientWidth; const reSize = () => (docEl.style.fontSize = clientWidth >= size ? '100px' : 100 * (clientWidth / size) + 'px'); document.addEventListener('DOMContentLoaded', reSize, false);};deviceRem(1125);第三方字体包#
根据 h5 的不同业务特点,可能需要引入各种个样的字体包,比如我的第一个 h5 就使用了站酷快乐体xxx的字体包
@font-face { font-family: 'happyFonts'; src: url('../fonts/站酷快乐体2016修订版.ttf'); font-weight: normal; font-style: normal;}
body { font-family: 'happyFonts';}引入后,打包到线上环境测试发现,即使字体包用font-spider筛选一遍,可真机上打开或多或少会出现字体加载过慢,导致文字区域空白一段时间再出现,或者先出现系统默认字体,之后再突然出现字体包字体。
这种体验…很难受啊!
所以我们急需一个能够监听第三方字体包是否加载完成的方法。
抓耳挠腮,捶胸顿足之下,终于找到一个神器 Font Face Observer!!!
如同描述的一般,它提供两个回调函数,字体加载成功或失败,页面可以在其回调内决定是否出现。
import Font from 'fontfaceobserver';
export default { mounted() { const font = new Font('happyFonts'); font.load().then( () => { console.log('字体加载完成'); this.$router.replace({ name: 'start' }); }, () => { console.log('字体加载失败'); this.$router.replace({ name: 'start' }); }, ); },};BGM!#
大部分活动 h5 少不了会用上 bgm,建议 bgm 使用某一段或几段可以循环播放的音频,越短越好(占用加载时间和服务器资源)。
“音频文件太长了,我只需要其中一小段怎么办”,我给出的方法是:找一个会剪音乐的漂亮妹子,嘿嘿嘿(咽口水)。
或者点击此处
好了,由于 h5 中的 bgm 占用了和字体包差不多的大小,我们也需要一个能够判断音频文件加载完成的事件。
下面是 audio 相关的事件
<body> <audio id="music" src="" loop="loop"></audio> <script> /** * @func play 播放 可判断音频是否正在播放 * @func pause 暂停 可判断音频是否暂停 * @funcs 一下事件是音频在加载过程中依次触发的 * loadstart => durationchange => loadeddata => progress => canplay => canplaythrough */ const audio = document.getElementById('music'); audio.oncanplay = () => { console.log('音乐可以播放,做你想做的吧'); }; </script></body>正在我志得意满打开测试链接时,发现无论如何点击播放都没有音乐声音!😱
着急忙慌打开微信开发者工具调试后发现,我的音乐地址找不到资源,这是为何?分明就放在src/assets/music中啊,找不到资源…难不成!
心头一点灵光乍现,当即打开vue-cli,急急望去
{ video: ['src', 'poster'], source: 'src', img: 'src', image: ['xlink:href', 'href'], use: ['xlink:href', 'href']}果然不出所料,vue-cli 的 loader 配置默认没有 audio 的配置!至此,情绪再次恢复平静,打开vue.config.js,缓缓书写几行代码
module.exports = { assetsDir: 'static', chainWebpack: (config) => { config.module .rule('vue') .use('vue-loader') .loader('vue-loader') .tap((options) => { options.transformAssetUrls = { audio: 'src', }; return options; }); },};重新打包,点开dist文件夹,static/media/xxx.mp3已安稳的进入其中,吾心甚慰。
html2canvas#
h5 的最后推广,同样少不了的一个功能就是,生成图片。
对于生成图片,这里有两个模块可供使用html2canvas和dom-to-image,两个模块的使用方法都很简单,这里使用的是 html2canvas.
随之而来的就是老生常谈的两个问题,一是图片跨域问题,二是生成图片清晰度优化。
-
图片跨域
启用 html2canvas 中的
{ useCORS: true }配置,即可渲染 cdn 的图片和本地图片。若有其他来源图片(比如微信用户头像),要么走接口转化下图片链接,要么将图片转成 base64 格式再行渲染即可。
-
清晰度优化
- 将选区 dom 的布局单位改为 px 像素
- 使用
{ scale: 2 }将 canvas 的宽高扩大为原本 dom 的两倍,或设置为 devicePixelRatio 倍,最后将生成图片的大小设置为原先 dom 大小 - 可以使用 css 绘制的样式,尽量不要使用图片代替
<script>import html2canvas from 'html2canvas';export default {// 获取可视区域的宽高getClientSize() {const app = this.$refs.app;return [app.clientWidth, app.clientHeight];},// 生成图片createPhoto() {const content = document.getElementById('container');const canvas = document.createElement('canvas');const [width, height] = this.getClientSize();const scale = 2;canvas.width = width;canvas.height = height;canvas.getContext('2d').scale(scale, scale);const opts = {logging: true, // 打印生成图片的日志canvas: canvas, // 导入生成的canvaswidth: width,height: height,scale: scale,useCORS: true, // 允许图片跨域timeout: 1000, // 设置图片超时时间};html2canvas(content, opts).then((c) => {this.photo = c.toDataURL('image/png', 1);});},};</script>完美~
图片压缩#
图片压缩有我们熟知的熊猫压缩,这个在线压缩网站可以极大的减少图片的大小同时不会出现失真,但对于工程师来说,拖入本地图片压缩的做法未免略显蠢笨。
于是,有了这么一个图片压缩的工具imageMin,及其 webpack 的扩展imagemin-webpack-plugin。
const ImageminPlugin = require('imagemin-webpack-plugin').defaultconst IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV) // 判断当前环境
module.exports = { configureWebpack: { plugins: { new ImageminPlugin({ disable: !IS_PROD, pngquant: { quality: '80-100', // 控制图片质量区间 progressive: true // 图片加载时的显示方式是否是渐变出现 } }) } }}亲测实用,image文件夹原本 5mb(已被 tinypng 压缩),打包后只有 3.5mb。