renderjs保存编辑后的图片
uni-app 里面做图片编辑,页面上能拖拽旋转只是第一步,真正麻烦的是怎么把编辑后的效果保存成图片
图片编辑组件里面,用户操作的是页面上的图片。
图片可以拖动、缩放、旋转、镜像。
这些操作本质上都是 DOM 和 CSS transform 的结果。
但是保存相册时,需要的是一张真实图片。
所以问题就变成了:如何把当前编辑区域重新生成成图片。
为什么需要renderjs
在普通 H5 项目里,如果想把 DOM 转成图片,可以直接在页面里用 html2canvas。
但是在 uni-app 里面,尤其是 App 端,逻辑层和视图层是分开的。
普通 script 里面不适合直接拿 DOM,也不适合直接操作浏览器环境里的对象。
所以这里用了 renderjs。
renderjs 可以运行在视图层,更接近真实 DOM。
这个场景刚好需要拿到页面里的编辑区域,然后用 html2canvas 截图。
触发方式
组件里面定义了一个状态:
1 | startStatus: 0 |
页面点击保存时,不是直接在普通 methods 里面截图,而是让这个值加一:
1 | saveImage() { |
模板里通过 change:prop 监听这个变化:
1 | <text :prop="startStatus" :change:prop="canvas.down"></text> |
当 startStatus 变化时,就会调用 renderjs 里面的 down 方法。
这个处理方式有点绕,但是它解决的是 uni-app 逻辑层和视图层通信的问题。
renderjs截图
renderjs 里面引入 html2canvas:
1 | <script module="canvas" lang="renderjs"> |
这里的 document.getElementById 就是在视图层执行的。
拿到 DOM 后,再交给 html2canvas:
1 | html2canvas(wrapper, { |
几个配置比较关键。
backgroundColor: null 是为了保留透明背景。
scrollY 和 scrollX 设置为 0,是为了避免页面滚动影响截图位置。
useCORS: true 是为了处理跨域图片。
scale: 2 是为了让生成图片清晰一点。
为什么要忽略旋转图标
组件里面有一个右下角旋转图标。
它只是交互工具,最终保存图片时不应该出现。
所以截图时过滤掉这个元素:
1 | ignoreElements: function(element) { |
这个细节挺重要的。
很多时候编辑界面里会有辅助线、按钮、遮罩、图标。
这些东西是给用户操作看的,不一定要进入最终图片。
生成base64
html2canvas 截图完成后,会得到一个 canvas。
然后转成 base64:
1 | let tempFilePath = canvas.toDataURL('image/png') |
这里拿到的还不是最终保存相册要用的路径。
它只是一个 base64 字符串。
所以还要通过组件普通 methods 继续处理。
renderjs 调用普通 script 里的方法:
1 | ownerInstance.callMethod('saveImg', tempFilePath) |
这一步相当于是把视图层生成的图片结果,传回逻辑层。
转成临时路径
普通 methods 里面的 saveImg 会继续处理:
1 | saveImg(url) { |
这里又做了一次转换。
因为最终外部页面保存到相册时,调用的是:
1 | uni.saveImageToPhotosAlbum({ |
这个 API 需要的是本地路径。
所以流程是:
1 | DOM编辑区域 |
为什么不直接返回base64
如果只是在 H5 页面展示,base64 其实已经够了。
但是在 App 或小程序里面,保存图片、上传文件、预览文件,很多 API 都更依赖本地临时路径。
base64 太长,也不适合到处传。
所以组件最后返回了两个值:
1 | { |
这样外部页面既可以预览 base64,也可以直接拿 filePath 保存。
总结
这次保存图片的重点不是 html2canvas 本身,而是为什么要这样绕一圈。
因为 uni-app 里面视图层和逻辑层是分开的。
DOM 截图这件事更适合放在 renderjs。
生成结果以后,再通过 ownerInstance.callMethod 传回普通 methods。
最后再把 base64 转成本地路径,交给保存相册 API。
以上就是我对 renderjs 保存编辑后图片的理解,如有错误,欢迎大佬指出。