前端使用html2canvas生成图片踩坑

news2024/11/18 7:34:26

前端使用html2canvas生成图片经验总结

前言

主要是总结一下html2canvas生成图片的基础用法,以及自己在使用html2canvas过程中踩过的坑和相应的解决思路

背景

近段时间接手一个项目,需要向设备下发某一个图片,为了该图片可以实时更改,而且为了节省资源,减少接口的请求,所以不适合通过后端接到参数再生成图片,需要前端直接生成图片传给后端,这样图片实时更改也完全可以在前端完成。

选择html2canvas的原因

在前端直接生成图片的话,首先必定是想到了Canvas画图,但是通过Canvas原生API来生成图片又比较繁琐,预研消耗的时间也比较长,所以就想到直接只用开源库,利用html2canvas来生成图片,这样节省了自己很多的开发成本,而且使用这种开源库,也不需要我去担心后期的维护或者当中可能出现的风险,因为html2canvas相对来说已经算是成熟稳定的开源库了。总的来说,我们对于生成图片的需求没那么复杂,同时为了节省开发的时间,这是我选择html2canvas的原因。

html2canvas的基本介绍

原理

在我看来,html2canvas的效果就好像对页面中的某一模块进行了截图处理,截取的图片就是前端生成的图片。可是在我看了html2canvas官方文档之后,其实html2canvas看似是截图,其实真正的工作原理是通过读取网页上的目标DOM节点的信息去绘制一个canvas,然后再通过调用的canvastoDataURL()方法生成<img>图片

基础用法

安装

npm i html2canvas

导入引用

import html2canvas from 'html2canvas'

使用

首先是HTML部分

在这里插入图片描述

因为html2canvas就是根据DOM节点来生成canvas的,所以这段html代码就是我们需要绘制的DOM节点,该html在页面中展示的效果就是即将生成的图片效果,所以看起来会像截图一样,因为使用了Vue,所以这里用ref来绑定该DOM

重点是JavaScript部分,这里我重新编写一遍代码

html2canvas(this.$refs.xxxx. {
	useCORS: true, // 使用跨域
	backgroundColor: null // 没有背景色
}).then((canvas) => {
	let url = canvas.toDataURL('image/png');
	console.log(url);
})

在这段代码中,首先向html2canvas传入我们需要生成图片的DOM节点,通过ref来绑定该DOM,等到html2canvas通过该DOM节点的信息生成canvas后,再调用canvas.toDataURL('image/png')方法,image/png是要生成的图片格式为png,然后该方法会返回一个图片的base64,下面是我生成的图片效果
在这里插入图片描述

踩过的坑

接下来我主要列举一下在使用html2canvas过程中踩过的坑,以及我的解决方案

问题一:图片渲染的不完整

因为html2canvas生成图片是需要获取选定的DOM的,但我当前的页面中,没有地方可去展示这块DOM,所以一开始我想要动态的去渲染该DOM,也就是等到我需要生成图片的时候,我才去生成DOM然后再传给html2canvas,但是这就遇到了一个比较严重的问题,就是图片渲染的不完整,从我上面的html代码中可以得知,我需要渲染的图片中,也有两张图片,因为DOM的渲染也需要时间,并且html2canvas生成图片是一个异步的过程,这就导致在生成图片时,其实获取的DOM信息并不完整,只能够获取到文字元素的信息,获取不到图片的信息。就大抵是下图的一个效果,只有文字。
在这里插入图片描述

解决方案

我这个方案其实有一些懒省事,我放弃了动态生成DOM的想法,转而一开始就将我需要的DOM给渲染在页面中,只不过因为页面中没有可以展示它的位置,所以我将该DOM元素用定位的方式隐藏在超出页面很多的角落,因为定位脱离的元素文档流,所以也不用担心会因此改变页面的结构,但是这种提前渲染我总觉得也是一种资源消耗,所以问题是解决了,但是如果大家有什么更好的解决方法,可以评论给我。

问题二:图片涉及跨域问题导致无法渲染

这个问题产生的原因依旧还是跟DOM中存在的img标签相关,从上面图片生成的最终效果,大家可以知道,我最好需要的图片,是有一个底图背景和其他图片的,而跨域的图片虽然可以被canvas读取,但是这也会导致canvas被污染,进而导致canvas无法导出img标签可用的图片数据。

解决方案一:加入allowTaint属性设置为true,加入useCORS属性设置为true(失败)

加入这两个属性之后,本来以为可以完成跨域生成图片,可是结果是html2canvas依然会报图片跨域的错误,因为这个错误,生成的图片就是一片空白,原因是因为html2canvas需要我们先提供一段DOM节点,然后它再读取并解析这一段DOM节点生成canvas对象。如果DOM节点中已经使用了标签的话,它也会解析这个标签的src属性,然后重新创建一个Image对象,给它添加crossOrigin="anonymous"属性后尝试以跨域的方式重新读取图片数据。需要注意的是,一般CDN上的图片都是带有缓存响应头并且会在浏览器端缓存的,而且缓存的不仅仅是图片数据,还有HTTP响应头。所以问题的根本原因我们就找到了,当html2canvas尝试以跨域的方式去读取图片数据时,它读取到的是浏览器的缓存数据,而且因为我们没有给DOM节点中的标签添加crossorigin="anonymous"属性,所以缓存数据是不带Access-Control-Allow-Origin响应头的,进而导致html2canvas库读取到的图片数据污染了生成的canvas对象,最终致使canvas导出数据报错。

解决方案二:给DOM中已存在img标签都加上crossorigin="anonymous"属性(失败)

从上面的原因我们可以得知,需要在DOM中已存在得每个img标签都加上crossorigin="anonymous"属性,果不其然,解决了html2canvas所报出得跨域错误,但由于当前页面的DOM信息中,两个图片是我之前上传到后端服务器保存的,然后后端传给我服务器自己的图片url后,因为设置了跨域,所以会导致读取服务器图片url失败,所以这个方法最终也是不可取。

解决方案三:将图片转为base64格式再去生成图片(成功)

这个方法比较简单粗暴,将图片直接转为base64,也不存在图片跨域的问题,可以直接通过html2canvas生成该图片
下面是转base64方式的方法
先根据服务器地址下载图片获取图片的二进制流,然后将二进制流转为base64
在这里插入图片描述

问题三:生成的图片模糊

其实这是最小的一个问题,但也是导致上面两个问题的导火索之一,在生成图片的时候,一开始并没有用img来作为底图,而是使用了background-image属性,这就导致了html2canvas生成的图片有点模糊,一开始我也尝试了放大图片之后再生成,然后用的时候在缩小,但是感觉依旧是模糊,有种画质不好的感觉,在看完文档之后也了解到它的图片生成和设备的分辨率也有关,这就让人比较无奈

解决方案

关于解决其实是无意中想要用img去替代background-image属性,但是替换之后,哦豁,发现效果出奇的好,大家也可以看到我最后的效果图,还是蛮清晰的。可能跟html2canvas的底层逻辑有关吧,关于这个问题我没有研究的很深,毕竟是解决了嘛

总结

以上就是我这次使用了html2canvas在前端直接生成图片的一些经验总结,遇见问题还是不要慌,除了官方文档和博客,也可以稍微进行一些天马行空的尝试,不消耗大量时间就也不亏嘛,在记录自己经验的同时,也希望能够对大家有所帮助,一起共同进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/409594.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue实现tagsview多页签导航功能

文章目录前言一、效果图二、实现思路1. 新建 tags-view.js2. 在Vuex里面引入 tags-view.js3. 新建 tabsView 组件4. 新建 ScrollPane 组件5. 引入 tabsView 组件6. 使用 keep-alive 组件&#xff0c;进行页签的缓存总结前言 基本上后台管理系统都需要有多页签的功能&#xff0…

vue 城市选择器(省市区)的使用 element-china-area-data

一、Element UI 中国省市区级联数据 本文参考&#xff1a;Element UI 中国省市区级联数据 本文参考&#xff1a;根据此文做的整理 1. 安装 npm install element-china-area-data -S2. 使用 import { regionData, CodeToText, TextToCode } from element-china-area-datareg…

vue项目打包优化及配置vue.config.js文件(实测有用)

首先我们需要在根目录里创建一个vue.config.js 首先在文件中先写入 //打包配置文件 module.exports {assetsDir: static, // outputDir的静态资源(js、css、img、fonts)目录publicPath: ./, // 静态资源路径&#xff08;默认/&#xff0c;如果不改打包后会白屏&#x…

HTML樱花飘落

樱花效果 FOR YOU GIRL 以梦为马&#xff0c;不负韶华 LOVE YOU FOREVER 实现代码 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html><head><meta http-equiv"…

【JavaScript-动画原理】如何使用js进行动画效果的实现

&#x1f482; 个人主页&#xff1a;Aic山鱼 个人社区&#xff1a;山鱼社区 &#x1f4ac; 如果文章对你有所帮助请点个&#x1f44d;吧!欢迎关注、点赞、收藏(一键三连)和订阅专哦目录 前言 1.动画原理 2.动画函数的封装 3.给不同元素添加定时器 4.缓动动画原理 5.给动…

原生JS实现FlappyBird游戏 超详细解析 快来做一个自己玩吧

目录​ 1.适配设备&#x1f43e; 2.背景滚动&#x1f490; 3.管道的创建与移动&#x1f338; 4.小鸟操作&#x1f337; 5.碰撞检测&#x1f340; 6.触屏事件&#x1f339; 7.制作开始与结束面板&#x1f33b; 8.得分统计&#x1f33a; 我们先来看看接下来我们要做的效果…

vue 路由报错

在进行如下路由跳转时 const edit (index: number) > { let row: any categoryData.value[index]; router.push({ name: “commodityedit”, query: { id: row.id, name: row.name } }); }; 遇到问题如下 TypeError: Failed to fetch dynamically imported module: http…

【uni-app】第三方ui组件推荐引入的方法

ui组件推荐 1. Muse-UI Muse UI 是一套 Material Design 风格开源组件库&#xff0c;旨在快速搭建页面。它基于 Vue 2.0 开发&#xff0c;并提供了自定义主题&#xff0c;充分满足可定制化的需求。material-design-icons 是谷歌定义的一套icontypeface 是谷歌定义的一套字体…

html+css+js制作LOL官网,web前端大作业(3个页面+模拟登录+链接)

文章目录一、效果图1.1首页1.2 模拟登录1.3 游戏资料页面1.4 商城页面二、代码2.1 首页2.2 资料页面2.3 商城页面三、链接一、效果图 1.1首页 1.2 模拟登录 1.3 游戏资料页面 1.4 商城页面 二、代码 2.1 首页 index.html <!doctype html> <html><head>&l…

JavaScript高级 |如何玩转箭头函数?

本文已收录于专栏⭐️ 《JavaScript》⭐️ 学习指南&#xff1a;箭头函数语法规则简写规则常见应用mapfilterreduce箭头函数中的this使用concatthis的查找规则完结散花参考文献箭头函数 在ES6中新增了函数的简写方式----箭头函数&#xff0c;箭头函数的出现不仅简化了大量代码…

Vue3自动引入组件,组件库

在做vue3项目中时&#xff0c;每次使用都需要先进行引入&#xff0c;用ts的还好&#xff0c;会有爆红提示&#xff0c;如果是使用js开发的很多时候都会等到编译的时候才发现哪里哪里又没有引入&#xff0c;就会很浪费时间&#xff0c;偶然发现一款好用的组件可以帮助我们很好的…

【你不知道的 CSS】你写的 CSS 太过冗余,以至于我对它下手了

:is() 你是否曾经写过下方这样冗余的CSS选择器: .active a, .active button, .active label {color: steelblue; }其实上面这段代码可以这样写&#xff1a; .active :is(a, button, label) {color: steelblue; }看~是不是简洁了很多&#xff01; 是的&#xff0c;你可以使用…

详细分析解决Uncaught SyntaxError: Cannot use import statement outside a module (at ...)的错误

文章目录1. 复现错误2. 分析错误3. 解决错误1. 复现错误 今天在学习es6时&#xff0c;启动页面后&#xff0c;却报出如下图错误&#xff1a; 即Uncaught SyntaxError: Cannot use import statement outside a module (at module.html?_ijtvfvtohb23jt1tj3r4ad3a0t82v:19:5)。 …

解决CitSpace分析新版本web of science文献报错“the timing slicing setting is outside the range of your data”

1.问题新版web of science于2021年7月7日上线&#xff0c;旧版 Web of Science 将同步运行到2021年底。现在旧版web of science入口早已关闭&#xff0c;新本web of science的残产品中也不在提供旧页面入口。近来在使用web of science文献制作CiteSpace图谱时发现&#xff0c;w…

【轻松解决el-dialog关闭后数据缓存 没清空】

问题描述 在使用el-dialog的时候&#xff0c;关闭弹窗之后&#xff0c;发现数据还是保存在上面&#xff0c;查资料试了那些方法&#xff0c;都不太行&#xff0c;最后发现以下方法&#xff0c;泪目。 解决方案&#xff1a; 方案一、使用this.resetForm("form")重置…

vue把el-dialog提取出来作为子组件,并实现父组件和子组件相互传值

最近做项目遇到了一个需求是&#xff0c;为了防止组件中代码冗长&#xff0c;需要将el-dialog对话弹出框单独封装成子组件&#xff0c;并将父组件中的id传递给子组件&#xff0c;子组件再根据id刷新父组件。具体项目代码太长&#xff0c;这里仅记录一下实现思路。 01 把el-dial…

vue 基于el-table实现多页多选、翻页回显过程

近半年时间在接触vue写pc页面&#xff0c;文中内容即在实际的开发过程中遇到的实际问题。 1、问题交代&#xff1a; 在pc版的列表页面中&#xff0c;假设当前在列表的第一页&#xff0c;想要在当前页面选择几行数据&#xff0c;跳转到其他页面选择几行数据&#xff0c;选择后…

【微信小程序】-- 全局数据共享 - MobX(四十三)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

利用CSS在图片中添加文字

前端项目中想要在图片中添加文字&#xff0c;方法有两种&#xff1a;1、js&#xff1b;2、css。第一种方法比较复杂&#xff0c;主要是写将图片与文字组合成新的图片的js代码&#xff0c;第二种方法简单粗暴&#xff0c;这里只讲第二种方法。 利用css在图片中添加文字&#xff…

关于npm i 的那点事

在我们开发中会经常用到npm i 这个命令&#xff0c;有npm i -S&#xff0c;npm i -g , npm i -D&#xff0c;npm install --save-dev, npm i -save&#xff0c;那么这几种命令到底有什么区别呢&#xff1f; 要先知道这几种命令的区别&#xff0c;我们首先要认识两个单词&#…