小程序web-view嵌套H5对AR特效canvas用MediaRecorder录屏踩坑记录

Posted 肖无疾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小程序web-view嵌套H5对AR特效canvas用MediaRecorder录屏踩坑记录相关的知识,希望对你有一定的参考价值。

没有iPhone手机,都是找别人测和云真机,但云真机调ar也不方便,微信也难登,真的很难搞。

  • AR场景下,在播放视频或音频前,一般只有一次点击按钮的机会,然后就是一段用户拍摄目标的空窗期,因此必须在用户点击按钮的时候解锁声音bgm.muted = false,此时播放空白音频或视频成为基本操作,等到识别到目标物体再替换音视频路径即可(如用AudioContext也可通过代码产生音频解锁,如JSMpeg中的player.audioOut.unlock方法)。
  • 有苹果手机同时播放多个声音时出现各种怪异的声响...
  • 部分苹果手机在同时播放多个有声媒体时存在限制,在上例中如果同时解锁video和audio,会出现audio自动暂停播放的情况,AudioContext也受影响,可能莫名其妙播放几秒就自动停了,排查老半天,搜索总算找到一个类似介绍:Safari Play Multiple Video Restrictions at the Same Time | ZEGOCLOUD Documentation
  • ios下测试中出现一例navigator.mediaDevices获取不到undefined的情况,怀疑是摄像头麦克风被其它应用占用了。
  • IOS下使用getUserMedia进行录音时,如果同时在使用AudioContext播放音乐,会出现音乐前几秒走调变音的情况,怎么调整先后顺序加timeout都不好使,可能与autoGainControl参数有关,关了试试。但是用audio标签播放的不受影响,但部分机型好像也不行?
  • 使用getUserMedia进行录音时会影响其它声音的音量,实际上手机的音量从媒体音量变成了系统音量,同时音乐还会出现卡顿的情况。
  • 虽然微信小程序文档说web-view页面可以使用onPageStateChange监听前后台事件,但是在安卓上res.active返回的是字符串类型,苹果手机有的事件无效,有的是bool类型,一塌糊涂。还是用visibilitychange吧。
  • 部分苹果手机下拉屏幕会自动暂停音视频播放,上述事件无法被监听到。只好用 setInterval不停的监测了。
  • canvas.captureStream()在iphone 13 pro (IOS 15.3)上webkit出现了崩溃现象,canvas是webgl的,2d的测试正常。日志为com.apple.WebKit.WebContent[10688] exceeded mem limit: ActiveSoft 1536 MB (non-fatal) MobileSafari(WebKit)[10670] <Error>: 0x106ac3600 - [PID=10688] WebProcessProxy::didClose: (web process 0 crash)
  • MediaRecorder苹果支持video/mp4,兼容性挺好谁都能播。安卓微信里支持video/webm;codecs=h264,最终输出格式为video/x-matroska;codecs=avc1,opus,兼容性不佳,只能h5里安卓自己看。安卓指定录制格式为h264时性能不及默认格式VP8,实际录制时长会有丢失。低端安卓录制分辨率大于600宽时就会丢失时长。webm格式在播放器里不播完似乎是不知道最终时长的,可以通过durationchange事件获取,loadedmetadata事件则不行。
  • 阿里云文档里声称支持webm转mp4但实际测试是失败的,提交工单自己也说不支持...使用函数计算ffmpeg能转mkv,但VP8是失败的;腾讯云虽然界面里没说支持webm,但实际却是可以转的...虽然也有客户端用ffmpeg.js转mp4的方案,但文件太大,转换太慢,而且微信里也不支持。最快的转换方案还是用webrtc边直播边录制。阿里云直播文档里没有搜到,似乎是不支持canvas做为视频流;而腾讯云实测是支持的,然后使用实时音视频旁路直播录制即可。实时音视频 Web-高级功能-文档中心-腾讯云

canvas菜鸟基于小程序实现图案在线定制功能

前言

最近在捣腾一个 袜子图案在线定制 的小程序,核心的需求大概是选择一只纯色的袜子,然后客户可以在袜子上面添加图案,
最终生成一个设计图保存后服务器。定制的图案可以旋转,缩放和拖动,当然,还可以删除。

内容比较初级, 因为我也只是一个 canvas 新手,本文仅是一次实践的记录.

技术分享图片

实现-绘图

其实刚开始接手这个项目的时候,我是打算用小程序的 web-view 组件来嵌套 h5 的,
因为小程序的 canvas 的坑,或者说小程序的坑,我是深有体会的,不过回过头来想,
web-view 我还没再项目中用过, 还说不定会遇到什么更大的坑呢,所以最终还是选择小程序的 canvas

首先, 计算图片的大小和位置,不能让图片超过画布(我这里是不能超过画布的 80%),位置在画布的正中间.

然后, 需要将原点移动到画布的中心点 这点比较重要,等下下面的旋转如果没有这一步的话,会沿着图片的左上角旋转.

第三步,将目标图像画到画板上,找个是没啥难的,直接调用 ctx.drawImage 既可

if (!this.imgUrl) return
ctx.drawImage(this.imgUrl, x, y, this.tempImgWidth, this.tempImgHeight)

drawImage 之前需要 判断一下目标图像的路径是否可以拿到图片,如果图片路径有问题,会直接报错,并且影响接下来的代码执行,
相当于整个应用崩溃了.

我这里因为都是本地图片,所以只要判断图片存不存在即可,如果是网络图片,还要先保存成本地图片再 drawImage,
不然有可能会出现很多画不出来的可能性.

由于中心点已经偏移了,这个时候, drawImage 的 x 和 y 需要向左上角偏移回去(imgWidth / -2, imgHeight / -2),
保持图片的中心点跟canvas的原点重叠.

到此, 图片就画好了,如果有旋转,要在这个时候旋转.旋转完以后,再画外框和操作按钮,
这样可以保持外框和操作按钮不随着图片旋转(经过验证,这种方式比跟着旋转的好很多),
旋转也很简单,直接调用 ctx.rotate 即可, 旋转的角度计算方法百度上有.我是直接拷贝的

然后就是操作按钮和虚线框,参数跟 drawImage 一样,因为是画一个跟图片一样大小的框框嘛..

虚线框

ctx.setStrokeStyle(‘#fd749c‘)
ctx.setLineDash([5, 5], 10);
ctx.strokeRect(x, y, this.tempImgWidth, this.tempImgHeight)

操作按钮,这里的 r 是按钮的半径, d 是按钮的直径,无非就是在图片的四个角,画按钮.

// 画 删除 按钮
ctx.drawImage(‘/static/design/icons/delete.png‘, x - r, y - r, d, d)
// 画 旋转 按钮
ctx.drawImage(‘/static/design/icons/rotate.png‘, x + this.tempImgWidth - r, y - r, d, d)
// 画 缩放 按钮
ctx.drawImage(‘/static/design/icons/scale.png‘, x + this.tempImgWidth - r, y + this.tempImgHeight - r, d, d)

最后再调用一下 ctx.draw() 完成绘图. 所用到的 API 都很简单,不过过程要计算的东西还是很多

实现-操作

绘图已经完成了,那么如何做到点击或拖动操作按钮,做响应的操作呢?
这就需要监听 canvas 的 bindtouchstart, bindtouchmove, bindtouchend 三个事件,
然后 e.touches[0].x 和 e.touches[0].y 可以获取当前手指的位置,
在 bindtouchstart 的回调里面判断当前位置,如果跟某个操作按钮重叠,就说明是操做,如果都没有重叠,则是拖动,然后把操作记录下来,
在 bindtouchmove 的回调里面,获取手指移动的距离和角度,然后根据 bindtouchstart 里记录的操作,
调整对应的参数(拖动跟缩放,只要计算当前点的 x,y 跟 bindtouchstart 点的 x,y 的偏移量即可,旋转的上面有提过,百度有),
然后重新调用 ctx.draw() 重新绘图.

一个操作做完,可能需要复位一些东西,这个可以在 bindtouchend 的回调里面去做

tips: 这里的 e.touches[0].x 的 e 是回调函数带的参数

保存定制图

这些都画好以后,需要把定制图保存起来,指定图保存的不单单是原来那个 canvas里的图,还要连底图(也就是那张袜子图)一起保存起来,
需要一个袜子图那么大的 canvas(这个称为临时 canvas 吧),先把袜子画进去,然后再把图案画上去,画图案上去的时候,由于画布大小位置已经变了,
所以需要稍微计算一下,大概就是 x,y 要分别加上原来的 canvan 跟袜子图左上角的距离.然后还要把外框跟操作按钮去掉.
最后调用 ctx.draw 把图画出来..这里有点需要注意的是,draw 其实是个异步函数,如果你在ctx.draw() 下一行做保存操作,
那么你保存的会是一个空白的图片,draw 函数的第二个参数,是一个回调函数,想要获取画完图的 canvas,要在这个回调函数里面才能拿的到

ctx.draw(false, function () {
    // 要先保存到临时路径
    wx.canvasToTempFilePath({
      canvasId: ‘tempCanvas‘,
      success: function (res) {
        // 然后再保存到相册
        wx.saveImageToPhotosAlbum({
          filePath: res.tempFilePath,
          success () {
            wx.showToast({
              title: ‘保存成功!‘,
              icon: ‘none‘,
              duration: 2000
            })
          }
        })
      }
    })
  }
)

你感受到了微信小程序团队满满的恶意了吗? 这大概是我第一次写这么深的嵌套吧..传说中的回调地狱.虽然可以用 promise 封装解决,
不过为了几个几乎不会复用的 API 去封装似乎不太划算. 不过总算还是保存成功了.

tips: 为了保存的时候这个临时 canvas 不影响正常界面,这我的做法是对 临时 canvas 设置 position:fixed; left:100vw;这两个样式,把它隐藏起来

最后

本文用的代码都是基于 mpvue 框架的, 这个也是个比较基础的东西,就不整个项目开源浪费 github 的空间了,
我把跟 canvas 有关代码上传到这里,
仅供参考,单独运行是肯定运行不起来, 如果有需要源码,可以私下发邮件找我要.

本文的的地址 个人技术帖合集 欢迎随意 star 和 follow, 和不随意的 issue






















以上是关于小程序web-view嵌套H5对AR特效canvas用MediaRecorder录屏踩坑记录的主要内容,如果未能解决你的问题,请参考以下文章

微信小程序web-view组件嵌入h5页面导致双导航栏,如何只保留其中一个?

微信小程序web-view组件嵌入h5页面导致双导航栏,如何只保留其中一个?

小程序嵌套网页

canvas菜鸟基于小程序实现图案在线定制功能

微信小程序web-view环境下H5跳转小程序页面方法

微信小程序嵌套h5页面,h5页面返回小程序,小程序和h5的页面和交互方法,h5点击分享小程序页面