前不久接到一个活动需求,用户拍照然后结合相应的素材生成另一张图片并分享传播。因为个人对 Canvas 很感兴趣就兴致勃勃的接下来了,后期遇到很多有意思的坑,分享一下。

// 绘制上下文获取
var doc = document
var can = doc.getElementById('canvas')
var ctx = can.getContext('2d')

首先是获取用户的图片文件,这里使用 input 标签获取

// Html:
<input type="file" class="js_photo_input" id="input" accept="image/*" multiple style="display: none;"/>
<div id="input"></div>// JS
// 因为 input 的样式是不能重写的,这里使用一个 div 绑定 click 事件去触发文件选择
doc.getElementById('input').click()
// 绑定事件,监听文件获取
$('.js_photo_input').on('change', fileChanged)

这里有一个需要注意的点,iPhone 下竖屏拍照,系统会自动将图片旋转,导致输出的图片与拍照时显示不一致。这里需要使用相关的接口处理一下。
详细介绍

// 获取用户选择的文件
function fileChanged(e) {var files = e.target.filesif(files[0]){// 获取文件后使用 getOrientation 函数处理getOrientation(files[0], function(orientation) {// orientation 是图片的旋转系数,参考下面的图// 一般情况下 iPhone 对图片的旋转为下图的情况 6// ctx.rotate(90 * Math.PI / 180) 旋转90度// 然后使用 canvas 处理,导出新的图片即可})return fileHandleShow(files[0])}
}function getOrientation(file, callback) {// iphone 下图片被系统旋转var reader = new FileReader()reader.onload = function(e) {var view = new DataView(e.target.result)if (view.getUint16(0, false) != 0xFFD8) return callback(-2)var length = view.byteLength, offset = 2while (offset < length) {var marker = view.getUint16(offset, false)offset += 2if (marker == 0xFFE1) {if (view.getUint32(offset += 2, false) != 0x45786966) return callback(-1)var little = view.getUint16(offset += 6, false) == 0x4949offset += view.getUint32(offset + 4, little)var tags = view.getUint16(offset, little)offset += 2for (var i = 0; i < tags; i++)if (view.getUint16(offset + (i * 12), little) == 0x0112)return callback(view.getUint16(offset + (i * 12) + 8, little))}else if ((marker & 0xFF00) != 0xFF00) breakelse offset += view.getUint16(offset, false)}return callback(-1)};reader.readAsArrayBuffer(file.slice(0, 64 * 1024))
}

然后到了绘制步骤,图片必须加载完成后才可以绘制,否则绘制不出图像,不多说。

var img = new Image()
img.src = 'url'// 图片的地址
img.onload = function() {var _this = thisctx.drawImage(_this, 0, 0, 100, 100)
}

在 iPhone 中,用户拍照的图片动辄上10M,再经过 canvas 处理为 base64 格式,体积非常大,所以需要使用 canvas 压缩处理。

var img = new Image()
img.src = 'url' // 图片的地址
img.onloda = function() {var quality = 0.5 // 压缩系数,默认是 0.92 范围 0 ~ 1//生成canvasvar canvas = document.createElement('canvas')var ctx = canvas.getContext('2d')var w = that.width, h = that.heightvar anw = document.createAttribute("width")anw.nodeValue = wvar anh = document.createAttribute("height")anh.nodeValue = hcanvas.setAttributeNode(anw)canvas.setAttributeNode(anh)ctx.drawImage(that, 0, 0, w, h)
}var base64 = canvas.toDataURL('image/jpeg', quality) // 这里得到压缩后的 base64 格式的图片

接下来使用 drawImage 绘制图片,使用 toDataUrl 导出。导出的时候要注意,如果有跨域图片资源,会报错。由于我们的项目资源都是放在 CDN 的,所以有跨域的问题,导致导出图片失败。

// 错误信息
Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported// 大致意思是,画布已经被污染,无法导出
// 这里是因为 canvas 在安全方面做了限制// 解决方案有两种
// 第一种就是把图片放在本域下,这样就没有跨域问题了// 第二种方案是,存放图片的服务器进行配置
// 或者参数只配置页面的域名
Access-Control-Allow-Origin="*"// 然后在导出的图片设置相关属性
var image = new Image()
image.setAttribute('crossOrigin','anonymous') // 设置属性 crossOrigin
image.src = can.toDataURL('image/jpeg', 0.6)
// 然后就可以导出了// 两种解决方法都有缺陷,视情况选择

然后就可以正确导出了,这里图片一般都是要经过服务器处理存储的,如果图片过大可能会导致图片上传损坏,所以对图片的压缩处理很重要。

最近找到了比较完美的解决方法,下面贴出来

// 在 URL 后面加一个时间戳就不会报错了,也不需要服务端设置
// 当然了,这样的话 cdn 也会失效,没有缓存作用var img = new Image();
var timestamp = new Date().getTime();
img.setAttribute('crossOrigin', 'anonymous');
img.src = url + '?' + timestamp;

详细链接

原文链接:https://blog.csdn.net/qq_25243451/article/details/79758132

Canvas 使用 toDataUrl 导出图片的各类错误相关推荐

  1. java todataurl_用canvas的toDataURL()将图片转为dataURL(base64)

    假设一个应用场景:由于某些特殊原因从服务端请求到图片路径(图片被存储在服务器上),要求通过该路径获取对应图片的 base64 dataURL.在这个场景中,我们首先推断该图片路径是可访问的,同时还需要 ...

  2. java todataurl_利用canvas中toDataURL()将图片转为dataURL(base64)的方法详解

    将图片转为base64的好处 将图片转换为Base64编码,可以让你很方便地在没有上传文件的条件下将图片插入其它的网页.编辑器中. 这对于一些小的图片是极为方便的,因为你不需要再去寻找一个保存图片的地 ...

  3. canvas节点无法导出图片_uniapp canvas绘制图片后无法canvasToTempFilePath导出

    后续补充:锅应该来自MIUI升级,因为我将之前打包的App安装后,依旧有这个问题(之前测试的时候是不存在的)... 但是但是,依旧请官方看看小米到底给你们挖了什么坑,解决此问题... 小米 webvi ...

  4. canvas节点无法导出图片_开源小程序,练手必备,仿“美图秀秀”处理图片。

    微信小程序:图片裁剪.缩放.涂鸦.添加文字.拼长图.拼相框.表情包制作.便捷的图片编辑工具. 图片编辑小程序--HiPhoto 全能.便捷的图片编辑工具.实现了图片裁剪.添加文字.涂鸦.拼长图.拼相框 ...

  5. canvas学习——toDataURL()方法

    toDataURL()方法 1.toDataURL()方法是什么? toDataURL()是canvas对象的一种方法,用于将canvas对象转换为base64位编码: 2.利用canvas的toDa ...

  6. 解决canvas导出图片模糊问题

    // 解决导出图片模糊的方法toBeCanvas() {var copyDom = $("#canvasQR");var width = copyDom.offsetWidth; ...

  7. vue上传图片加水印;js上传图片添加水印;vue给图片添加水印;canvas图片添加水印;canvas画布导出图片

    uni-app微信小程序图片加水印,点击看这篇 需求场景: 要求上传图片,并给图片添加水印.传给后端的也是有水印的图片. 逻辑步骤: 通过input上传图片,拿到图片的信息和base64,将图片绘制到 ...

  8. 前端 canvas toDataURL() 转图片生成空白图片问题

    这里写自定义目录标题 前端 canvas toDataUrl() 转图片生成空白图片问题 感谢大神 解决方法 问题发生 解决办法原理 vue项目中的使用 总结 2022-05-17追加修改 前端 ca ...

  9. 把view或者div绘制 canvas ,导出图片功能实现完整源码附效果图(兼容H5和小程序)

    先看下效果图:(上面灰色块内的用div和CSS写出来的,然后绘制到canvas) 实现此功能需要使用到一个微信小程序的插件,插件官方文档地址: wxml-to-canvas | 微信开放文档 本博客代 ...

最新文章

  1. 启明云端技术社区之星--张广星
  2. kafka删除队列_没想到 Kafka 还会这样问,学会这些带你轻松搞定大厂面试!
  3. SAP ABAP实用技巧介绍系列之 ABAP XSLT 使用attribute增加新的属性
  4. openWRT自学---针对backfire版本的主要目录和文件的作用的分析整理
  5. SSRF,以weblogic为案例
  6. ubuntu和linux服务器,Linux服务器系统CentOS和Ubuntu Server如何选择? | 偶乃秋辰
  7. node 多进程 vs java_多进程 VS 多线程 VS 线程池 VS EventLoop
  8. 中国互联网发展状况报告:境内约 2.6 万网站被植入后门
  9. update在python中是什么意思_update()与save()有什么区别
  10. 电商中的订单号如何实现
  11. 4.1 随机变量的数学期望
  12. CNFs/CNTs复合薄膜-供应高储能效率铁电聚合物基电介质/ 三硒化二铟In2Se3/ 硒化铟(InSe和In2Se3)纳米/Cu(In,Ga)Se_2和Cu_2ZnSnSe_4薄膜定制
  13. 好的提问和寻找答案的网站(会时常更新)
  14. Win7 添加grub引导Linux最简单方法
  15. 三菱FX5U系列PLC模拟量使用方法及相关参数设置
  16. 大众点评woff反爬
  17. PCIe基础知识及Xilinx相关IP核介绍
  18. Tomcat开机自启动(Win+lin系统)
  19. 40-岁的中年失业者怎么活下去?,移动端跨平台框架
  20. 左连接、右连接、内连接、全外连接的区别是什么?

热门文章

  1. linux测试包转发率,提高ROS小包转发速率 - RouterOS - 自由的生活_软路由论坛 - Powered by Discuz!...
  2. vue项目中使用echarts-地图
  3. 小白到架构师需要掌握的技能
  4. python登陆Tom邮箱的代码一例
  5. 如何查看计算机操作过程,如何查看电脑使用记录,小编教你查看电脑使用记录的方法...
  6. 文储研习社第06期 | 一张图,看懂区块“链”
  7. Halcon20--C#与Halcon联合编程时深度学习报错问题记录
  8. 模板管理 编辑文件功能
  9. python iot 开源_开源IOT 最小物联网系统
  10. 如何实现消息功能_微信服务号功能助手发送消息模板如何实现