iOS端Canvas渲染Base64图片旋转问题求助
这个问题我之前帮不少开发者排查过,本质是iOS和浏览器对图片方向信息的处理逻辑不一样导致的,我给你拆解清楚原因,再给你落地的解决方案:
为啥会出现旋转?
iOS相机(包括相册里的照片)会把拍摄时的方向信息存在图片的EXIF元数据里。浏览器的<img>标签会自动读取这个EXIF信息,悄悄帮你把图片调整到正确方向显示;但Canvas API本身完全不管这个元数据,你直接把图片画上去,它就会按照图片原始的像素方向渲染,自然就出现旋转错位了。Android设备的拍照逻辑在方向存储上和iOS不同,所以不会有这个问题。
具体解决步骤
核心思路就是:先读取图片的EXIF方向,再根据这个方向对Canvas做旋转、平移等变换,最后再绘制图片。
1. 读取图片的EXIF方向信息
要拿到这个方向值,最靠谱的方式是用轻量的EXIF处理库(比如exif-js,体积很小,不会给项目加负担)。如果你用的是Cordova/Angular项目,直接npm安装后引入就行。
这里给你写个封装好的异步函数,用来获取方向值:
// 假设你的Base64图片数据是dataUrl async function getImageOrientation(dataUrl) { return new Promise((resolve) => { const img = new Image(); img.src = dataUrl; img.onload = () => { // 用exif-js读取Orientation标签 EXIF.getData(img, function() { const orientation = EXIF.getTag(this, 'Orientation'); // 默认返回1(正常方向) resolve(orientation || 1); }); }; }); }
如果你不想引入第三方库,手动解析EXIF的话需要处理Base64的二进制数据,过程比较繁琐,不推荐,除非你对二进制操作很熟。
2. 根据方向调整Canvas绘制逻辑
拿到方向值后,我们需要对Canvas上下文做对应的变换,再绘制图片。下面是覆盖所有常见方向情况的完整代码:
async function drawImageToCanvasCorrectly(canvas, dataUrl) { const ctx = canvas.getContext('2d'); const img = new Image(); img.src = dataUrl; // 等图片加载完成再处理 await new Promise(resolve => img.onload = resolve); const orientation = await getImageOrientation(dataUrl); // 先清空Canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // 根据不同方向做变换 switch(orientation) { case 2: // 水平翻转 ctx.translate(img.width, 0); ctx.scale(-1, 1); ctx.drawImage(img, 0, 0, img.width, img.height); break; case 3: // 旋转180度 ctx.translate(img.width, img.height); ctx.rotate(Math.PI); ctx.drawImage(img, 0, 0, img.width, img.height); break; case 4: // 垂直翻转 ctx.translate(0, img.height); ctx.scale(1, -1); ctx.drawImage(img, 0, 0, img.width, img.height); break; case 5: // 水平翻转后旋转90度 ctx.rotate(0.5 * Math.PI); ctx.scale(1, -1); ctx.drawImage(img, 0, -img.width, img.width, img.height); break; case 6: // 旋转90度(iOS竖屏拍照最常见的情况) // 先调整Canvas尺寸,因为旋转后宽高互换了 canvas.width = img.height; canvas.height = img.width; ctx.rotate(0.5 * Math.PI); ctx.translate(0, -img.height); ctx.drawImage(img, 0, 0, img.width, img.height); break; case 7: // 水平翻转后旋转270度 ctx.rotate(0.5 * Math.PI); ctx.scale(-1, 1); ctx.drawImage(img, -img.height, 0, img.width, img.height); break; case 8: // 旋转270度 canvas.width = img.height; canvas.height = img.width; ctx.rotate(-0.5 * Math.PI); ctx.translate(-img.width, 0); ctx.drawImage(img, 0, 0, img.width, img.height); break; default: // 正常方向,直接绘制 canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0, img.width, img.height); } }
3. 配合你的拍照代码优化
看你的代码用的是Cordova Camera插件,其实可以尝试添加correctOrientation: true参数,让插件自动调整图片方向:
const options: CameraOptions = { quality: 50, destinationType: this.camera.DestinationType.DATA_URL, correctOrientation: true, // 尝试让插件自动修正方向 ... };
不过实测下来,有些iOS版本或者插件版本可能不生效,所以还是建议配合上面的Canvas处理逻辑,双重保障,不管是拍照还是选相册图片都能处理。
小提醒
- 一定要等图片加载完成后再读取EXIF和绘制,否则会获取不到正确的方向信息。
- 这个逻辑对相册选择的图片同样适用,因为相册里的iOS照片也带EXIF方向信息。
内容的提问来源于stack exchange,提问作者Vikas Jindal

