解决苹果手机wx.canvasToTempFilePath:fail on image问题,canvas的那些坑,图片失真问题

用小程序做过画图工具的小伙伴,可能都遇到过这个问题,程序在安卓手机和电脑上面都没有问题,但是同样的代码在苹果手机下就出现了该问题:wx.canvasToTempFilePath:fail on image。

网上有很多解决方案,比如不能加隐藏none,hidden等。这些CSS设置的初衷会为了隐藏画布,与画图失败没有关系。如果想要隐藏画布也可以用另一种方案:让画布的位置固定在屏幕之外。

<canvas canvas-id="shareCanvas" class='shareCanvas' style='left:9000px;width:1200px;height:1500px;position:fixed;'>
</canvas>

出现fail no image真正的原因在于画布canvas的大小超过了苹果手机所能设置的大小界限。

canvas有自身的width,height属性来控制尺寸,而且不同的设备拥有不同的尺寸。

比如:

苹果手机不能超过4096*4096像素;

安卓不能超过8192*8192像素;

在PC,CHROME浏览器,360浏览器,不能超过16384*16384像素;

这些尺寸限制有待进一步确认,因为我在测试中发现,苹果手机设置成4096也存在失败的概率。

所以,在写程序的时候,可以把想要画的图片同比例缩小,画布尺寸也设置小,这样就不会出现问题。

另一个问题,画布没问题了,同比例缩小之后,画出来的图片不久失真了吗?不会,给你解决方案:画完之后同比例放大。

                ctx.draw(false, () => {
                    wx.canvasToTempFilePath({
                        x: 0,
                        y: 0,
                        width: that.data.xp,
                        height: that.data.defaultHeight,
                        // 同比例放大,决定了保存图片的清晰程度
                        destWidth: that.data.xp * 2.5,
                        destHeight: that.data.defaultHeight * 2.5,
                        canvasId: 'shareCanvas',
                        fileType: 'jpg',
                        // 图片质量
                        quality: 1,
                        success: function (res) {
                            // 获得图片临时路径
                            let mixImage = res.tempFilePath;
                            that.setData({
                                mixImage: mixImage,
                                imageWidth: that.data.xp
                            });
                            wx.hideLoading();
                        }
                    });
                });

看实例,微信搜索小程序:图作妖

// 超精细半色调生成方法 async generateUltraHalftoneImage(imagePath) { const printWidth = 430; const printHeight = 430; // 创建离屏Canvas const offscreenCanvas = wx.createOffscreenCanvas({ type: '2d', width: printWidth, height: printHeight }); const ctx = offscreenCanvas.getContext('2d'); // 填充白色背景 ctx.fillStyle = '#FFFFFF'; ctx.fillRect(0, 0, printWidth, printHeight); // 加载图片 const img = offscreenCanvas.createImage(); await new Promise((resolve, reject) => { img.onload = resolve; img.onerror = reject; img.src = imagePath; }); // 计算缩放比例 const scale = Math.min( printWidth / img.width, printHeight / img.height ); // 计算绘制位置 const drawWidth = img.width * scale; const drawHeight = img.height * scale; const x = (printWidth - drawWidth) / 2; const y = (printHeight - drawHeight) / 2; // 使用最高质量绘制 ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(img, x, y, drawWidth, drawHeight); // 获取图像数据 const imageData = ctx.getImageData(0, 0, printWidth, printHeight); const data = imageData.data; // 专业级灰度处理 this.professionalGrayscale(data); // 应用超精细半色调效果 this.applyUltraFineHalftone(data, printWidth, printHeight); // 将半色调数据绘制到Canvas ctx.putImageData(imageData, 0, 0); // 生成临时文件路径 return await new Promise((resolve, reject) => { wx.canvasToTempFilePath({ canvas: offscreenCanvas, fileType: 'png', quality: 1, success: (res) => resolve(res.tempFilePath), fail: reject }); }); }, // 专业级灰度处理 professionalGrayscale(data) { // 使用更精确的灰度公式 for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; // 使用CIE Lab转换公式的简化版本 const gray = 0.2126 * r + 0.7152 * g + 0.0722 * b; // 应用S曲线增强(保留更多中间调) const enhanced = this.sCurveEnhancement(gray); data[i] = data[i + 1] = data[i + 2] = enhanced; } }, // S曲线增强(优化中间调) sCurveEnhancement(value) { const normalized = value / 255; // 专业S曲线公式(更平缓的过渡) let curved; if (normalized < 0.25) { curved = 2.5 * Math.pow(normalized, 2); } else if (normalized < 0.75) { curved = 1.5 * normalized - 0.125; } else { curved = 1 - 2.5 * Math.pow(1 - normalized, 2); } return Math.max(0, Math.min(255, curved * 255)); }, // 超精细半色调算法 applyUltraFineHalftone(data, width, height) { // 创建临时数组存储灰度值 const grayData = new Float32Array(width * height); // 提取灰度数据 for (let i = 0, j = 0; i < data.length; i += 4, j++) { grayData[j] = data[i]; } // 使用专业级误差扩散算法 for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const idx = y * width + x; let oldVal = grayData[idx]; // 使用动态阈值(更精细的控制) const threshold = this.calculateUltraThreshold(grayData, x, y, width, height); // 应用抖动技术减少颗粒感 const jitter = this.calculateJitter(x, y, width, height); const effectiveThreshold = threshold * (1 + jitter); const newVal = oldVal < effectiveThreshold ? 0 : 255; // 设置最终像素值 const dataIdx = idx * 4; data[dataIdx] = data[dataIdx + 1] = data[dataIdx + 2] = newVal; // 计算误差 const error = oldVal - newVal; // 专业级误差扩散(Jarvis-Judice-Ninke算法) if (x + 1 < width) { grayData[idx + 1] += error * 7 / 48; } if (x + 2 < width) { grayData[idx + 2] += error * 5 / 48; } if (y + 1 < height) { if (x - 2 >= 0) { grayData[idx + width - 2] += error * 3 / 48; } if (x - 1 >= 0) { grayData[idx + width - 1] += error * 5 / 48; } grayData[idx + width] += error * 7 / 48; if (x + 1 < width) { grayData[idx + width + 1] += error * 5 / 48; } if (x + 2 < width) { grayData[idx + width + 2] += error * 3 / 48; } } if (y + 2 < height) { if (x - 2 >= 0) { grayData[idx + width * 2 - 2] += error * 1 / 48; } if (x - 1 >= 0) { grayData[idx + width * 2 - 1] += error * 3 / 48; } grayData[idx + width * 2] += error * 5 / 48; if (x + 1 < width) { grayData[idx + width * 2 + 1] += error * 3 / 48; } if (x + 2 < width) { grayData[idx + width * 2 + 2] += error * 1 / 48; } } } } }, // 计算超精细阈值 calculateUltraThreshold(grayData, x, y, width, height) { const baseThreshold = 128; const idx = y * width + x; const currentGray = grayData[idx]; // 计算7x7邻域平均值(更大范围更平滑) let sum = 0; let count = 0; for (let dy = -3; dy <= 3; dy++) { for (let dx = -3; dx <= 3; dx++) { const nx = x + dx; const ny = y + dy; if (nx >= 0 && nx < width && ny >= 0 && ny < height) { const nIdx = ny * width + nx; sum += grayData[nIdx]; count++; } } } const localAvg = sum / count; // 计算局部对比度 const contrastFactor = Math.abs(currentGray - localAvg) / 255; // 在低对比度区域使用更平滑的阈值 if (contrastFactor < 0.1) { return baseThreshold * 0.3 + localAvg * 0.7; } else if (contrastFactor < 0.3) { return baseThreshold * 0.5 + localAvg * 0.5; } else { return baseThreshold; } }, // 计算抖动值(减少规则图案) calculateJitter(x, y, width, height) { // 使用伪随机噪声减少规则图案 const seed = (x * 15485863 + y * 2038074743) % 1000000009; const noise = (seed % 100) / 100; // 应用正弦波创建更自然的抖动模式 const freq = 0.1; const xWave = Math.sin(x * freq); const yWave = Math.sin(y * freq); // 混合噪声和波形 const jitter = (noise + xWave + yWave) / 30; // 小幅度抖动 return jitter; }, 这个代码继续优化,提升图片质感,精细化图片,回复我的代码需要完整的js代码,不要出现什么填补代码,不用担心性能问题,我得机器性能无敌强,只需要图片效果最好就行,注意我的代码格式不要变,只需要修改算法,不要瞎改我的代码
最新发布
07-30
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

链诸葛

真爱了。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值