- wx.chooseVideo压缩后最大25M?
wx.chooseVideo选择时候后会先压缩,然后提示超过25M,请重新选择 没有其他办法上传更大的视频了吗? [图片]
2018-07-05 - 《王者荣耀周边商城》经验总结
大家好,以下是《王者荣耀周边商城》小程序的一些经验总结,也许能帮到你,也许也帮不到,大家看着办哈,因为时间有点早,所以文中有些实现不是最新的,最终请以官方文档说明为准,废话不多说,直接上高清无码大图。 [图片] [图片] 以上截图是之前的版本,中间做了改版,大家可以直接扫码识别体验: [图片] 项目结构 我们都知道,小程序有自己的一套实现规范,下面我们看下小程序的项目结构,如下图: [图片] 一个入口文件:app.js 一个全局样式:app.wxss 一个全局配置:app.json 页面:pages下,每个页面再按文件夹划分,每个页面4个文件 index.js:实现页面整个生命周期的控制逻辑,置顶显示时的界面交互 index.json:页面配置,一个JSON对象,详细可配置字段见这里 index.wxml:UI结构渲染,可以理解为就是html,主要支持定制标签,更多标签见这里 index.wxss:UI样式渲染,可以理解为就是css,大部分css写法都支持。 当然在4个文件基础之下,还有一些通用的功能组件支撑它们的运行。每一个页面都是这么几个文件组成,非常规范统一,并且每一部分也都限定了内部实现框架和规范,所以在多人协作的时候,产出相对也就比较规范。 注:pages里面还可以再根据模块划分子目录,孙子目录,只需要在app.json里注册时填写路径就行 以上是必须的文件和目录,而实际中我们会增加别的目录,如lib,comm和utils等等目录。 框架设计 Web开发做得多了,你就会发现,大部分工作就是取数据,渲染UI,处理交互这三件事儿,小程序也不例外,所以按照这个框架逻辑,我们基于小程序本身的框架规范又扩展和细化了下,设计了下面的这套可直接应用于项目的开发框架。 [图片] 下面我们继续详细理一下设计时的一些思路、出发点和具体的实现方式,窥其面更要知其理。 注:这里我没有直接使用第三方的框架,因为我个人觉得要学习一套技术,还是需要从原生的模式开始着手,虽然前期会比较痛苦一点,但是这些付出都是值得的,因为你一旦搞明白了本来的逻辑架构和原理,你会发现什么框架都是信手拈来,而且你也更能理解框架这样设计的优点和缺点。 工具类库 Ajax 实现了promise的封装,支持GET POST PUT和DELETE,这里设计的时候就确定为仅满足单项目通用即可,所以实现的时候融入了部分业务层面的逻辑: 接口首次格式化,兼容标准的json和var形式接口(内部有大量var形式的接口) 直接判断返回值在逻辑上是成功还是失败 针对返回未登录的情况,自动跳转登录流程 所以省去了业务调用侧的反复判断处理通用逻辑,使用更简洁。 cache 其实,小程序自带了缓存接口,有同步wx.setStorageSync,异步wx.setStorage的方法,但是实际在使用缓存的场景里,我们一般都是需要设置缓存有效时间的,本cache工具就是对小程序缓存接口的封装,实现了对缓存有效期的支持。 Model实现 model层就不用多说了,主要是把数据处理部分独立出来,便于统一服务和维护,这里重点强调下model内部的实现细节,这里有一个实现技巧可以用在其它别的地方。下面直接上代码部分。 [图片] 上面的代码我们可以看到几个关键点 1. 把参数处理和返回结果处理拆出来放到单独的处理方法里,方法名称保持统一:formatParams,formatResult 2. 同时最外层定义好默认的formatParams,formatResult,如果不做特殊处理,直接使用默认即可(建议不处理也调用下默认方法,规范流程) 3. 还有一点,model里方法命令有统一规范都是已get,add,update,del开头 这个思路其实可以运用到任何场景,特别是在没有任何限定框架的场景,我们只需要按照这个模式去实现,代码一样很清晰漂亮,比如我后面实现LOL内置竞技场道具商店的时候,就是为了减少不必要的框架冗余代码,就直接徒手写的,同样是拆分为model和view层,然后model按上面的规范实现,代码同样很清晰,强烈建议大家实践下,简单实用。 组件模式 在实现王者周边小程序的时候,官方没有开放自定义组件规范,所以我们还不能按照内置组件的实现方式来实现我们业务侧自定义的组件,但是项目里又有公共组件的需求存在,那我们不管怎样还是需要把组件独立出来,不然重复代码很蛋疼,维护成本也比较高,实际我们这里的组件实现模式还是比较简单粗暴的,我们照样把组件拆分为JS,WXML,WXSS三部分(或者只有一部分也行),然后通过不同的import方式引入到需要使用组件的page里就行。 注:虽然这里没有使用官方的自定义组件规范,但是经过自己实现这个,也能大致了解到官方的自定义组件的实现方式和原理。 JS引入:import 或者 require(建议小程序这里引入都用import,跟wxml和wxss比较统一,我对代码有点小洁癖) WXML引入:和标签 WXSS引入:@import 组件很多时候也需要处理页面交互,相应事件,而小程序的事件绑定机制决定,事件处理方法必须是挂载到当前page对象下(实际是Page()方法定义的对象,内部引用是this),而组件的实现是单独的文件,不在Page()方法里定义,那怎么办呢?我这边的实现方式是组件初始化的地方,传递当前的page对象(this)给到组件,然后组件内部的接口方法全部一次性extend到page上,同样数据也是这个道理,WXML里面的数据方法只能是data对象,组件里的数据也需要挂载到这个对象上,这里强烈建议把组件内部的数据定义为一个单独的对象挂载到data上,而不要直接挂载,如我们这里的购物车组件,实现就是下面这样: [图片] 上面setData的时候,定义了shoppingCart对象,在它里面再定义具体的购物车组件需要的数据变量,而下面Object.assign一句就是把组件的方法挂载到当前使用组件的page上面去。 另外还要注意,在WXML里插入组件模块的时候,template标签的data属性里的名称请使用上面setData的名称,比如购物车这里就是shoppingCart。 请大家现在实现的时候,使用官方标准的自定义组件规范 数据共享 小程序开发也涉及到多页面间数据共享,这里针对不同的场景有几种实现方式: 1、基于页面的数据传递:直接在navigate的url后面增加参数即可,然后在接收的页面onLoad方法里,通过参数(对象)接收即可,如下: [图片] [图片] 2、基于内存的数据共享:getApp方法,获取全局的App实例对象,可以设置存取这个实例对象属性来实现数据共享,如下: [图片] 上面这种方式,适合与启动后的短期数据共享,关闭小程序数据会丢失。 3、基于本地缓存的数据共享:可以使用上面的cache组件,也可以使用原生的缓存接口实现,这种方式是可以在小程序关闭后还存在。 4、基于后台服务的接口缓存:这个不多说,就是保持数据到服务器,多页面通过接口调用。 开发规范 详细的JS实现规范这就不讲了,这里大致列一下我们在开发的时候,我们这边简单定义的一些规范,供大家参考。 JS模块引入请使用import关键字,而非require,WXML引入模板用标签,WXSS使用@import语法,三者统一 使用 let 代替var进行变量定义,使用const定义常量,如:let goods = 1,const SEX=’男’ 所有方法和变量名称都使用小写camel模式,一般是动词+名词形式,尽量不要超过5个单词,如:getList,setBackImage 所有用到的常量的地方都使用全大写,下划线分隔的形式,如:EGG_CHE 使用this转换的地方,统一使用that,如:let that = this; 尽量使用箭头函数,可保留this指向 所有自定义方法(onLoad,onShow等系统方法除外)必须使用规范注释语法进行注释 在page和model里定义方法的时候,直接使用getList(){} 即可,中间可以不用加function关键字 小程序声明周期函数里(onLoad,onReady,onShow,onHide,onUnload等)不要直接写复杂业务逻辑,复杂业务逻辑独立成方法,这里只负责方法调用。 所有数据处理必须封装到model里面,包括url地址,参数格式化,返回结果格式化都放到model里面,在page里使用的时候,基本不用做过多数据处理,一般都直接setData皆可,model规范请参考前面的说明。 如果1个功能在超过一个地方出现,那请实现为公用组件,组件实现请参考前面的说明。 在WXML模板里,如果同时有2个循环中都使用到了同样的代码片段,请使用template的方式定义,然后直接使用即可,不能重复写. 所有地方都需要有对接口返回空数据的处理,界面上要有相应的提示和引导。 其它的可以定义规范的loading,成功失败提示等,这里不多说了. 经验分享 大家开发前,可以大致浏览下小程序的官方文档,相对比较完善,遇到问题可以先查文档,然后再去小程序社区里搜索相关帖子,一般问题都能解决,下面是我遇到的一些问题和相应的解决方案,也许你也会碰到,仅供参考。 1、setData相关经验 设置多级对象值:this.setData({‘a.b.c.d’:value}); 设置可变索引的数组值: varkey="array["+index+"].text, data ={key: 'changed data’}; this.setData(data); 2、swiper组件 current 问题 swiper组件切换数据源以后,current属性也需要手动重置,不会默认恢复到第一帧,就可能出现当前current大于新数据源的长度,显示会出问题 3、picker-view 初始值设置无效 picker-view初始化的时候,我们都会设置数据源和初始索引值,结果发现放到一次setData里既然不生效,分成2次setData就可以了,应该是设置数据源的时间点在初始值之后了,因为setData接收的是一个hash对象,而hash对象是没有先后顺序的,所以就可能存在初始值在数据源之前设置了,当然初始值不可能生效。 [图片] 4、android 兼容性问题 从原理上我们知道,小程序本身还是基于不同的JS容器的执行的,所以由于IOS(jscore)和android(v8)上容器不一致,还是需要小程序开发者自己处理两个平台的兼容性,不过新版的微信升级后,目前ios和android的不一致的兼容性问题已经很少了,不要这里需要理解的是小程序虽然有规范,但是并没有帮我们屏蔽底层的兼容问题,我们自己需要注意。 5、cookie 的问题 我们在开发web页面的时候,肯定会使用到cookie,传递登录信息等,但是小程序本身不支持cookie,所以需要应用到cookie的地方,可以转换为参数,放到请求后面,我们上面的小程序登录态就是放到了请求参数里。 但是在调用wx.request的时候,是可以设置cookie header头的,所以如果后台接口验证的需要cookie支持,可以直接在这里设置即可,但是需要注意:android的版本的小程序会把cookie键名自动改为小写,如果后台是通过大写读取的话,可能就取不到值了,暂时还不确认新版是否已修复这个问题。 [图片] 6、https 的问题 小程序要求所有请求接口都必须是https的,而且所有的域名都需要在小程序管理后台去添加,如果碰到没有添加的情况,开发调试阶段可以在小程序开发工具的项目一栏下,把下面这句勾选,不过上线之前是一定要添加的,不然会出现本地怎么调都是好的,到了手机上就是不行。 [图片] 7、关于支付 直接使用微信支付即可,不多讲。 8、关于设计 如果有条件,小程序需要独立的产品设计和规划,照搬App或者H5版本不是最好的方案,因为小程序有自己的一套设计,交互规范,有基于微信的账号体系,消息机制等基础能力,我们都可以充分利用。 写在最后 虽然版本有些老,但是还是希望对大家有些帮助,最后建议,大家如果在开发小程序的过程中遇到问题,可以第一时间在论坛里发帖求助哈,微信官方有开发同学直接回复大家。
2018-01-31 - 每日出击运签小程序“踩坑”总结
最近刚好接手支持了每日出击运签的小程序,在小程序完成以后,整理了一下初次接手小程序的一些体验,所以有了现在这篇小总结。在小程序需求的进行期间,十分感谢泽贤,小苏,俞焕,花花的指导~ 话不多说,立马送上小程序码,大家可以扫码体验一下 [图片] 每日出击运签小程序主要划分为几个功能模块: 摇一摇 抽签并显示运签结果 保存图片 预约功能 签到功能 积分功能 评论功能 下面重点介绍一下摇一摇、抽签并显示运签结果、保存图片三个功能。 关于摇一摇功能 花叔早前已经写过文章(链接如下:http://www.ifanr.com/minapp/880378)介绍过摇一摇的实现思路了,这里简单归纳一下摇一摇功能的几个主要的思路。 准备: 需要设置一组变量,保存摇一摇x,y,z三轴的数值,需要设置一个变量来记录摇一摇的时间。 实现注意事项: 我们需要定一个阈值,作为摇一摇的判断基准。 需要通过公式来计算单位时间内运动的路程,也就是我们想要的速度。如果这个速度超过了我们定下来的阈值,那么就算作用户已经摇一摇 [代码]//计算 公式的意思是 单位时间内运动的路程,即为我们想要的速度[代码][代码]var[代码] [代码]speed = Math.abs(x + y + z - lastX - lastY - lastZ) / diffTime * 10000;[代码] 另外我们需要注意两个小程序提供的API: wx.onAccelerometerChange(CALLBACK):用于监听加速度数据,在发生有加速度的动作的时候,执行摇一摇的判断逻辑 wx.stopAccelerometer:在摇一摇的逻辑执行期间,需要停止监听加速度数据,避免多次触发摇一摇 为了给予用户更好的摇一摇反馈,建议在执行摇一摇以后设置振动反馈,这个小程序API里面也带有相关的接口:wx.vibrateShort(OBJECT) 关于抽签并显示运签结果的功能 每日出击运签的小程序其中有一个需求是抽签并显示运签结果。在用户每天进入小程序的时候,通过摇一摇,得到一个抽签结果,如图所示: [图片] 关于这块功能,这里需要着重介绍vine在实现小程序的过程中比较关注的两个点,一个是随机显示抽签结果,一个是用于显示签纸的动画效果。 随机显示抽签结果 这个小程序中,随机显示抽签结果的需求具体如下: 随机出现五个运签结果,大吉、吉、平、凶、大凶;不同结果对应下面不同文案;每个用户每天只能获取同一个结果。随机文案,每个用户在用完库存前不重复 这里我们可以提炼出两个关键点:一个是每个用户每天获取的结果不变,一个是,在库存用完前不重复出现文案 也就是说,我们需要把用户的抽签结果和用户id关联起来。另外我们需要准备储存抽签结果的数组,每天从数组里面取出一个值作为抽签结果,从而保证在库存用完前不重复出现文案。贴代码: [代码]// 打乱数组顺序[代码][代码] [代码][代码]function[代码] [代码]shuffle(a) {[代码][代码] [代码][代码]var[代码] [代码]j, x, i;[代码][代码] [代码][代码]for[代码] [代码](i = a.length - 1; i > 0; i--) {[代码][代码] [代码][代码]j = Math.floor(random() * (i + 1));[代码][代码] [代码][代码]x = a[i];[代码][代码] [代码][代码]a[i] = a[j];[代码][代码] [代码][代码]a[j] = x;[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]var[代码] [代码]m_w = 123456789;[代码][代码] [代码][代码]var[代码] [代码]m_z = 987654321;[代码][代码] [代码][代码]var[代码] [代码]mask = 0xffffffff;[代码] [代码] [代码][代码]function[代码] [代码]seed(i) {[代码][代码] [代码][代码]m_w = i;[代码][代码] [代码][代码]m_z = 987654321;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]function[代码] [代码]random() {[代码][代码] [代码][代码]m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;[代码][代码] [代码][代码]m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;[代码][代码] [代码][代码]var[代码] [代码]result = ((m_z << 16) + m_w) & mask;[代码][代码] [代码][代码]result /= 4294967296;[代码][代码] [代码][代码]return[代码] [代码]result + 0.5;[代码][代码] [代码][代码]}[代码] [代码]//获取用户id [代码][代码] [代码][代码]var[代码] [代码]userid = parseInt(gbConfig.user_id, 16);[代码][代码] [代码][代码]seed(userid);[代码][代码] [代码][代码]// 随机生成抽签描述[代码][代码] [代码][代码]var[代码] [代码]qian = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30];[代码][代码] [代码][代码]var[代码] [代码]result = [];[代码][代码] [代码][代码]for[代码] [代码]([代码][代码]var[代码] [代码]i = 0; i < 10; i++) {[代码][代码] [代码][代码]shuffle(qian);[代码][代码] [代码][代码]result = result.concat(qian);[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]// 随机生成抽签结果[代码][代码] [代码][代码]var[代码] [代码]luck = [1, 2, 3, 4, 5];[代码][代码] [代码][代码]var[代码] [代码]luckarr = [];[代码][代码] [代码][代码]for[代码] [代码]([代码][代码]var[代码] [代码]i = 0; i < 60; i++) {[代码][代码] [代码][代码]shuffle(luck);[代码][代码] [代码][代码]luckarr = luckarr.concat(luck);[代码][代码] [代码][代码]}[代码] 2.签纸显示的动画效果 这次小程序,在上线前添加了签纸显示的动画效果,以加强“抽签”的用户感受。签纸以滚轴的形式向下展开,慢慢呈现出签纸的结果。 关于动画效果,最容易被使用的实现方式应该就是css3动画以及序列帧。这里尝试了三套方案,最终选择了最后一套。下面分别来介绍一下这三套方案。 第一:使用传统的序列帧方式。尝试使用传统的序列帧,通过逐帧动画,慢慢铺开签纸,逐帧动画如下: [图片] 在通过减帧以后,得到了8张序列帧图,vine发现,8张序列帧图通过拼接得到的雪碧图会非常大(达到了1M左右的大小),所以再次进行减帧得到由4张序列帧图拼接而成的雪碧图(也有500+kb左右)。但是在实际放到页面上面的时候发现,4张序列帧图帧数太少,造成了动画卡顿不流畅的情况,而且在低端安卓机上面,这组序列帧动画完全无法流畅进行。所以第一套方案放弃。 第二:把8张序列帧图分开加载,4张为一组,拼接成一张雪碧图,一共两张雪碧图,如下: [图片] [图片] vine的处理方式是:先加载第一张雪碧图,在第一张雪碧图逐帧动画结束后,切换到第二张雪碧图,无缝连接播放第二组逐帧动画。结果在低端安卓机上面出现了同样的情况,动画播放非常卡顿,而且在图片切换的过程中出现了空白。于是第二套方案失败。 第三:在vine百思不得其解的时候,突然发现,这套序列帧有一个特点:它的签纸和卷轴并不是立体的!也就是说,这里其实可以不需要序列帧来实现签纸打开的效果,只需要最后一张图就可以了: [图片] 关于保存图片的功能 保存图片的功能是这次小程序中的一个大难点,这里非常感谢泽贤和小苏提供的建议。下面来介绍一下保存图片的功能到底是什么: [图片][图片] 通过点击分享按钮,可以生成一张和签纸类似的图片,用户可以长按保存这张图片到手机本地。从而达到转发分享的目的。 而这个保存,所需要的就是canvas。小程序API提供了canvas的接口: [图片] 通过这个接口,我们可以把当前画布指定区域的内容导出成指定大小的图片,然后再调用小程序的预览接口进行图片预览以及保存: [图片] 但是在这里,vine遇到了几个问题: 多次点击保存按钮触发canvas,导致手机滑动的时候非常卡顿 生成预览图片时间非常慢 三星note5保存图片尺寸的问题 下面来介绍一下这两个问题相应的解决措施: 多次点击保存按钮触发canvas,导致手机滑动的时候非常卡顿 canvas只在需要触发的时候渲染,在用户没有点击保存按钮的时候,默认不渲染canvas。代码如下:由于小程序和MVVM框架类似,无法直接操作dom,所以vine采取了在样式上面添加状态,通过修改showcanvas变量的值,来控制canvas的显示和隐藏。在执行画图操作的时候使变量值为false,显示canvas,在绘制完成的时候更改变量值,隐藏canvas [代码]<[代码][代码]canvas[代码] [代码]class[代码][代码]=[代码][代码]'{{showcanvas ? "hidcanvas" : "showcanvas"}}'[代码] [代码]style[代码][代码]=[代码][代码]"width: 750px; height: 1334px;position:fixed;left:100000rpx;top:-100000rpx;"[代码] [代码]canvas-id[代码][代码]=[代码][代码]"shareQRcode"[代码][代码]></[代码][代码]canvas[代码][代码]>[代码] 生成预览图片时间非常慢 生成预览图片时间非常慢,目前判断的原因是,直接预览canvas临时路径的图片耗时比较久。那么应该如何优化这个过程呢? 小程序的API提供了几个关于文件的接口: [图片] [图片] [图片] 我们可以通过这几个接口优化目前的预览方式。在这其中vine做了两种不同的尝试。 第一,在执行wx.previewImage的success的回调的时候,把previewImage生成的临时路径保存下来,下次再点击预览的时候,直接获取本地已保存的文件列表wx.getSavedFileList,取得最近保存下来的文件的本地路径,进行预览。这么做的想法主要是为了解决canvas渲染的问题,每天只渲染一次,后续都是通过预览本地图片链接达到生成图片的目的。梳理一下流程: 初次渲染:canvasToTempFilePath -> previewImage -> saveFile 二次渲染:getSavedFileList -> previewImage 可惜理想始终是丰满的,在实际操作的过程中vine发现,通过这样的方式预览图片的时候,一直处于loading的状态,无法生成初次渲染的图片。vine非常苦恼,至今没能找到原因。 于是vine想了另一种办法,调整了预览的顺序如下: 初次渲染:canvasToTempFilePath -> saveFile -> previewImage 二次渲染:getSavedFileList -> previewImage 这时候vine发现,先把canvas生成的临时路径保存到本地,再预览,这种方法是可行的!而且在二次渲染的过程中,由于只是读取小程序本地的图片路径,无需再次调用canvas绘图,二次预览的时间大大减少。 在这里我们需要注意官方文档中提到的一点:小程序本地文件存储的大小限制为10M。所以我们需要调用wx.removeSavedFiled的方法删除我们不需要的图片(当天之前存下来的图片都是我们无需保存到本地的图片,可以删除) 三星note5保存图片尺寸的问题 在数量庞大版本不一的安卓手机上,vine收到反馈,三星note5在保存图片的时候会遇到图片底部被裁减的情况。在通过多次修改以及对比类似的小程序以后发现,note5用canvas绘制的图片有一个最大的范围是750*1150,超出这个范围的图片,底部就会被裁减。为什么会有这个最大范围vine目前还没探索出结论,希望遇到过这个问题的大神可以和vine交流一下心得,手动比心!~ 最后梳理一下小程序项目的一些注意点: 小程序需要申请小程序公司主体的账号,拿到appid,给到后台同学配置接口 小程序的体验需要上传体验版本,获取体验者的微信号,到小程序管理后台开通体验者权限 小程序开发过程中需要的单位为rpx,兼容问题较少 在小程序的需求中,需要仔细查看文档中提到的版本以及兼容问题,做好降级处理:https://mp.weixin.qq.com/debug/wxadoc/dev/framework/compatibility.html
2018-01-29 - 基于加速度计判断横竖屏
也许有人会问,小程序中都是竖直app形态,要横竖屏判断有什么用?即使判断出了横屏状态,你能把小程序横过来?答案是不能的,但是判断当前设备处于横屏或者竖屏状态来实现一些友好的用户体验交互方式的需求确实存在。例如手机横屏,让视频播放自动全屏,手机竖屏,让视频切换回来小屏。 然而,截止至目前,小程序官方的API中并没有提供这样的横竖屏判断的方法。那么我们只能自己想办法实现这样的判断。小程序的设备API中提供了加速度计的监听方法,使用方法如下: [代码]wx.onAccelerometerChange([代码][代码]function[代码][代码](res) {[代码] [代码] [代码][代码]console.log(res.x)[代码] [代码] [代码][代码]console.log(res.y)[代码] [代码] [代码][代码]console.log(res.z)[代码] [代码]})[代码] 加速度计的三轴以下是一般移动设备的加速度计三轴坐标系示例图: [图片] 以手机竖直面向用户为例,加速计的三轴坐标系统的X、Y、Z轴定义如下: 沿着手机屏幕顶部向上是Y轴正方向,向下是Y轴负方向; 当手机顶部朝上时,沿着手机屏幕向右是X轴正方向,向左是X轴负方向; 正对手机时,垂直屏幕向外是Z轴正方向,垂直屏幕向里是Z轴负方向; 当手机处于静止状态时,手机此时只受一个重力加速度(1g=9.8m/s²)的作用,加速度计返回的res.x、res.y、res.z的值就是设备的三轴受到的加速度的值,取值范围从[-1g,1g]。设备以不同方式放置时,x/y/z的值如下: [图片] 计算姿态角在stackoverflow上找到了根据加速度计三轴的值计算姿态角公式(https://stackoverflow.com/questions/3755059/3d-accelerometer-calculate-the-orientation),经过结合设备的三轴坐标方向对公式进行调整,最终得出了公式: [代码]Pitch = atan2(Y, Z) * 180/M_PI;Roll = atan2(-X, sqrt(Y*Y + Z*Z)) * 180/M_PI;[代码] [代码][代码]Roll = atan2(-X, sqrt(Y*Y + Z*Z)) * 180/M_PI;[代码][代码] Roll(绕Y轴旋转的角度) 当设备绕着自身Y轴旋转时(表示手机左侧或右侧翘起的角度),该角度值将会发生变化,取值范围是-90到90度。 Pitch(绕X轴旋转的角度) 当手机绕着自身的Y轴旋转(表示手机顶部或尾部翘起的角度),该角度会发生变化,值的范围是-180到180度。 [图片] 接下来就是根据自己对横竖屏角度的观测,再结合微信小程序中,视频全屏只能以手机向左旋转方式全屏的特性,只对用户左侧横屏判断为横屏状态,实现代码片段如下: [代码] // 0为竖屏,1为横屏[代码][代码] [代码][代码]let lastState = 0;[代码][代码] [代码][代码]let lastTime = Date.now();[代码] [代码] [代码][代码]wx.startAccelerometer();[代码] [代码] [代码][代码]wx.onAccelerometerChange((res) => {[代码][代码] [代码][代码]const now = Date.now();[代码][代码] [代码] [代码] [代码][代码]// 500ms检测一次[代码][代码] [代码][代码]if[代码] [代码](now - lastTime < 500) {[代码][代码] [代码][代码]return[代码][代码];[代码][代码] [代码][代码]}[代码][代码] [代码][代码]lastTime = now;[代码] [代码] [代码][代码]let nowState;[代码] [代码] [代码][代码]// 57.3 = 180 / Math.PI[代码][代码] [代码][代码]const Roll = Math.atan2(-res.x, Math.sqrt(res.y * res.y + res.z * res.z)) * 57.3;[代码][代码] [代码][代码]const Pitch = Math.atan2(res.y, res.z) * 57.3;[代码] [代码] [代码][代码]// console.log('Roll: ' + Roll, 'Pitch: ' + Pitch)[代码] [代码] [代码][代码]// 横屏状态[代码][代码] [代码][代码]if[代码] [代码](Roll > 50) {[代码][代码] [代码][代码]if[代码] [代码]((Pitch > -180 && Pitch < -60) || (Pitch > 130)) {[代码][代码] [代码][代码]nowState = 1;[代码][代码] [代码][代码]} [代码][代码]else[代码] [代码]{[代码][代码] [代码][代码]nowState = lastState;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]} [代码][代码]else[代码] [代码]if[代码] [代码]((Roll > 0 && Roll < 30) || (Roll < 0 && Roll > -30)) {[代码][代码] [代码][代码]let absPitch = Math.abs(Pitch);[代码] [代码] [代码][代码]// 如果手机平躺,保持原状态不变,40容错率[代码][代码] [代码][代码]if[代码] [代码]((absPitch > 140 || absPitch < 40)) {[代码][代码] [代码][代码]nowState = lastState;[代码][代码] [代码][代码]} [代码][代码]else[代码] [代码]if[代码] [代码](Pitch < 0) { [代码][代码]/*收集竖向正立的情况*/[代码][代码] [代码][代码]nowState = 0;[代码][代码] [代码][代码]} [代码][代码]else[代码] [代码]{[代码][代码] [代码][代码]nowState = lastState;[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]else[代码] [代码]{[代码][代码] [代码][代码]nowState = lastState;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]// 状态变化时,触发[代码][代码] [代码][代码]if[代码] [代码](nowState !== lastState) {[代码][代码] [代码][代码]lastState = nowState;[代码][代码] [代码][代码]if[代码] [代码](nowState === 1) {[代码][代码] [代码][代码]console.log([代码][代码]'change:横屏'[代码][代码]);[代码][代码] [代码][代码]} [代码][代码]else[代码] [代码]{[代码][代码] [代码][代码]console.log([代码][代码]'change:竖屏'[代码][代码]);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]});[代码] 然后就可以在横竖屏切换的状态下,去切换视频的横竖屏了 [代码]if[代码] [代码](state === 1) {[代码][代码] [代码][代码]video.requestFullScreen();[代码][代码]} [代码][代码]else[代码] [代码]{[代码][代码] [代码][代码]video.exitFullScreen();[代码][代码]}[代码] 其他 另外,在这里发现小程序的一个小bug,就是当进入一个页面,马上就调用requestFullScreen()方法去拉起视频全屏时,会破坏整个页面的布局,并且再调用全屏方法时,视频就无法再全屏了,像这样: [图片] 所以为了防止用户直接以横屏的状态进入一个视频播放页,而我们的横屏判断检测生效立即触发全屏引发bug,我将监听横竖屏的事件通过setTimeout(listener, 3000)延迟3s监听,这样横屏才不会触发bug。 最后 文中的很多知识点很多都是从网络文章学来,可能存在错误的理解,如有错误,欢迎各位指正。 最后再打个广告,欢迎喜欢看游戏直播的小伙伴来试用我们的《TG电竞》直播小程序,这里聚合各大平台的知名主播,总有一款适合你哦。 [图片]
2018-01-25