使用canvas2d的方法,安卓手机vivo z5x 红米note8 pro等用canvasToTempFilePath生成图片提示canvasToTempFilePath:fail:convert native buffer parameter fail. native buffer exceed size limit,初步怀疑是宽度设置太大原因,设置了180vw。
<canvas id="canvas" canvas-id="signCanvas" type="2d" binderror="drawError" bindtouchstart="drawStart"
bindtouchmove="drawMove" bindtouchend="drawEnd"
style="width:{{width}}vw;height:100%;border:1px solid #ddd;margin:auto;margin-left:{{marginLeft}}vw;">
</canvas>
data: {
marginLeft: 0,
width: 180,
demoSrc: '',
title: '',
isShowCanvas: true,
// 默认一开始只有一个页面
isOnlyOnePage: true
},
init(res) {
console.log(res)
const width = res[0].width
const height = res[0].height
const canvas = res[0].node
console.log('初始化宽高度', width, height)
canvasWidth = width;
canvasHeight = height
signCanvas = canvas.getContext('2d')
canvas.width = width * dpr
canvas.height = height * dpr
canvasObj = canvas
signCanvas.scale(dpr, dpr)
signCanvas.lineWidth = 8;
signCanvas.lineCap = "round";
signCanvas.strokeStyle = "#222222";
},
// 保存确定
saveSign() {
let that = this;
let canHeight = 90;
let canWidth = this.data.width;
if (handData.length) {
console.log('canvas宽高', canvasWidth, canvasHeight)
console.log('dest图片宽度高度', canWidth, canHeight)
wx.canvasToTempFilePath({
x: 0,
y: 0,
destHeight: canHeight,
destWidth: canWidth,
canvas: canvasObj,
success: function (res) {
console.log('res', res)
that.setData({
demoSrc: res.tempFilePath
})
wx.downloadFile({
url: res.tempFilePath,
success: (res) => {
console.log(res)
},
fail: (err) => {
console.log(err)
}
})
},
fail: function (err) {
console.log('生成图片失败', err)
if (err && err.errMsg.indexOf('native buffer exceed size limit') >= 0) {
util.showModel('生成图片失败', '生成图片过大,请重新进入该页面,减少签名文字大小,降低右移次数')
} else if (err && err.errMsg) {
util.showModel('生成图片失败', err.errMsg)
} else {
util.showModel('生成图片失败', '请重新打开页面签名')
}
}
})
}
}
点赞,我说怎么忽然就内存不足,还是新机型!
我的解决方案是:
以下是我实现这个需求的笔记,由于微信开发工具展示太丑,建议复制到 `markdown` 查看:
- 实现思路与步骤 一般的海报都带有小程序码,而小程序码是需要服务端生成之后返回一个图片地址给前端,之后再绘制出来 由于生成海报是动态的,如文字数量不是固定的,或是生成多种海报类型,海报的高度可能不一样,而初始化组件时还需要传入 `width` 和 `height`,并且渲染元素也需要 `width` 和 `height` 属性 1. 显示生成海报中的样式 2. 写好一个和海报样式一模一样的容器,并使用 `visibility: hidden` 进行隐藏 3. 获取到该容器的宽度和高度之后再初始化 `wxml-to-canvans` 组件且把带有 `wxml-to-canvans` 组件的容器使用 `opacity: 0` 隐藏起来 4. 获取到容器宽高之后获取小程序码 5. 获取小程序码之后获取到 `wxml-to-canvans` 实例并调用 `renderToCanvas` 方法,这个过程需要传入 `wxml` 和 `style` 对象 6. 渲染成功之后调用 `wxml-to-canvans` 实例的 `canvasToTempFilePath` 方法将 `canvas` 转为图片,返回图片的临时地址 7. 拿到临时地址之后把海报的容器的 `visibility` 改为 `visible` 8. 点击保存按钮之后需要调用 `wx.saveImageToPhotosAlbum` 的 `api`,把生成的临时地址传入即可。该 `api` 需要进行相册授权,且该授权拒绝过一次之后不再弹出授权框,需要做拒绝授权处理,如在 `fail` 函数中诱导用户打开 `wx.openSetting` 设置相册授权 9. 保存成功之后进行成功操作即可 - 注意事项 - 小程序对原生组件使用 `visibility: hidden` 不能达到效果,在 `ios` 中 `canvas` 依然有问题,所以使用 `opacity: 0` 进行替代(开发工具没效果) - `canvas` 在容器里面滚动时会跟着屏幕一起滚动(微信开发工具),在手机里面不会 - `canvas` 的层级比较高,但是新版本可以设置 `canvas` 的层级 - `wxml-to-canvas` 组件初始化需要一段时间,才能获取其组件并调用里面的方法 - `wxml-to-canvans` 放在带有加载渲染逻辑的 `sort` 里面不会渲染出来,即在 `sort` 使用 `if` 操作,然后 `if` 的条件最后使用 `this.setData` 然后进行显示,这个在微信开发工具不会渲染,在手机设备没有测试过 - `wxml-to-canvas` 第一次初始化了宽高之后,如果再对该 `width` 和 `height` 属性使用 `this.setData` 改变,这个组件的宽高有效果,但是这个组件里面的 `canvas` 不会改变,可以使用 `if` 等获取到宽高之后再渲染出来 - `wxml-to-canvas` 调用 `renderToCanvas` 方法时,如果绘制的内容带有图片且图片是在线链接,则需要在微信公众平台设置小程序的域名配置 - `canvas` 的宽高不能设置太大,安卓手机会出现闪退问题,`ios` 则会出现绘制白图问题 - `canvas` 宽度或高度过高时,需要按比例缩放,否则安卓机型生成不了图片 - `canvasToTempFilePath` 的报错记录 - `write file fail` `wxml-to-canvas` 在手机上调用 `canvasToTempFilePath` 方法时报 `write file fail`,原因是因为 `wxml-to-canvas` 的宽度或高度大于该组件的容器的宽度或高度,把 `wxml-to-canvas` 的 `width` 或 `height` 减去 `1` 或 `2` 就能解决问题 - `convert native buffer parameter fail.native buffer exceed size limit` 调用 `canvasToTempFilePath` 方法时报这个错误是因为 `canvas` 的宽度或高度太大,安卓机型的内存占用过高导致失败,需要把 `canvas` 绘制按比例缩放 - 重要实现过程 - 获取海报样式容器的高度 获取到容器的高度之后再初始化 `wxml-to-canvas` 组件 ```html <wxml-to-canvas wx:if="{{isPosterShow}}" class="widget" width="{{canvasWidth}}" height="{{canvasHeight > screenHeight ? screenHeight : canvasHeight}}" ></wxml-to-canvas> ``` ```js /** * 获取容器高度 * @method getContainerHeight */ getContainerHeight() { return new Promise((resolve, reject) => { const query = wx.createSelectorQuery().in(this); query.select('.poster-target').boundingClientRect(); query.exec((res) => { this.setData({ isPosterShow: true, canvasHeight: res[0].height }) resolve(); }) }) } ``` - 自适应设备以及缩放 不能超过屏幕高度,如果超过则需要设置为最大高度 ```html <wxml-to-canvas wx:if="{{isPosterShow}}" class="widget" width="{{canvasWidth}}" height="{{canvasHeight > screenHeight ? screenHeight : canvasHeight}}" ></wxml-to-canvas> ``` ```js class DprClass { constructor(curHeight, maxHeight) { this.curHeight = curHeight; // 当前容器高度 this.maxHeight = maxHeight; // 最大高度 this.sysInfo = wx.getSystemInfoSync(); } /** * 处理像素比例 * @method handlePxToRpx * @params {Number} num 当前设置的像素 * @params {Boolean} isNotToRpx 不需要转换成 rpx * @return {Number} num 当前设置的像素 */ handlePxToRpx(num, isNotToRpx = false) { const w = this.sysInfo.windowWidth; const mw = 750; // 设计稿宽度 // 如果是通过DOM获取的值不需要做适配 let diffNum = isNotToRpx ? num : (w / 750 * num); // 如果当前高度大于最大高度,则做缩放处理 if (this.curHeight > this.maxHeight) { const ratio = this.maxHeight / this.curHeight; diffNum = Number((diffNum * ratio).toFixed(3)); } return diffNum; } } function renderCanvas() { /** * 必须等 wxml-to-canvas 初始化一段时间才能获取成功 */ this.widget = this.selectComponent('.widget'); const dprObj = new DprClass(containerHeight, windowHeight); const wxml = `<text class="user-name">帅</text>` const style = { userName: { width: dprObj.handlePxToRpx(20), height: dprObj.handlePxToRpx(20), lineHeight: dprObj.handlePxToRpx(20), fontSize: dprObj.handlePxToRpx(18), color: '#000000' } } const p1 = this.widget.renderToCanvas({ wxml: poster.wxml, style: poster.style }); p1.then((res) => { this.extraImage() }).catch((err) => { console.log(err) this._handiePosterErr(); }) } ```
这个后面定义了x和y,从有滑动的点开始,重新设置width和height,尽量减少width和height,减少右移次数,完成需求的。
这个问题解决了吗?
请问,解决了吗?
试下设置小点正常吗?
能不能出个解决方案?
我在用canvas画长图,IOS设备,高度超过4096也报这个错。
安卓小米10,高度1795.5时canvas就不正常了,不渲染,但是不报错
我在用canvas画长图,IOS设备,高度超过4096也报这个错。
安卓小米10,高度1795.5时canvas就不正常了,不渲染,但是不报错
IOS的分辨率要比安卓差很多,看图