一键实现HTML转换成图片复制到剪切板上功能
背景:
项目截图可能由于用户电脑分辨率的问题无法截全图(排除用长截屏工具的情况下)
思路:
把html结构先用canvas转换成图片,再使用Clipboard API实现复制到剪切板的功能
在此过程中一共会有两种方法可以实现当前功能(Document.execCommand() 方法以及Clipboard API)
①因为
Document.execCommand()
方法虽然方便,但是有一些缺点。首先,它只能将选中的内容复制到剪贴板,无法向剪贴板任意写入内容。其次,它是同步操作,如果复制/粘贴大量数据,页面会出现卡顿。有些浏览器还会跳出提示框,要求用户许可,这时在用户做出选择前,页面会失去响应。
②相较于
Document.execCommand()
方法,它的所有操作都是异步的,调用是会返回 Promise 对象,不会造成页面卡顿。而且,它可以将任意内容(比如图片)放入剪贴板。navigator.clipboard
属性返回Clipboard
对象,所有操作都通过这个对象进行。但是
clipboard
允许脚本任意读取会产生安全风险,所以这个 API 的安全限制比较多。考虑到稳定性,所以我选择了用
clipboard
去实现这个功能
实现步骤:
1.首先将html
转换为图片需要html2canvas
插件帮忙
js
npm install html2canvas
或
yarn add html2canvas
安装成功后,在对应页面引入插件
js
import html2canvas from "html2canvas"
2.写页面代码
jsx
<Button onClick={() => saveCanvas()}>复制</Button>
jsx
<div style={{display:"flex",justifyContent:"center",alignItems:"center"}}>这是测试用例</div>
3.通过将html结构转换为canvas对象
jsx
const saveCanvas =()=>{
const scale = window.devicePixelRatio; // 当前显示设备的物理像素分辨率与CSS像素分辨率之比
html2canvas(document.querySelector("#qContent"), {
// dpi: 300, // 精度,处理模糊问题
useCORS: true, // 允许跨域
scale: scale, // 当前显示设备的物理像素分辨率与CSS像素分辨率之比
width: 1300, //图片宽度可自行设置
height: 600, //图片高度可自行设置
backgroundColor: "#fff", //图片背景色
allowTaint: true, //若是 useCORS:true,且同时设置allowTaint为true,仍然会认为画布已被污染而不可用。
removeContainer: true // 清除临时创建的克隆dom元素
// eslint-disable-next-line space-before-function-paren
}).then(async (canvas) => {
const base64Img = canvas.toDataURL("image/png"); // 通过toDataURL将此canvas对象转成base64编码
const file = base64toFile(base64Img, "图片"); // 转file
copyFile(file);
});
}
4.然后把它转换为base64文件的格式
jsx
const base64toFile = (dataBase64, filename = "file") => {
const arr = dataBase64.split(",");
const mime = arr[0].match(/:(.*?);/)[1]; // 获取file文件流的type名称
const suffix = mime.split("/")[1]; // 获取文件类型
const bstr = window.atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime
});
};
5.在上面已经获得了html结构转图片后的base64文件,将其转换成blob对象,再通过剪贴板的navigator.clipboard.write()方法写入
jsx
const copyFile = (file) => {
const reader = new FileReader();
reader.onload = (e) => {
const newFile = e.target.result.toString();
const img = new Image();
img.src = newFile;
img.onload = () => {
//blob对象在写入时可能会出现格式出题,所以先将图片解析成base64,在转换成blob对象写入,减少后面不必要的麻烦
let base64 = imageBase64(img);
let blob = base64ToBlob(base64.replace("data:image/png;base64,", ""), "image/png", 512);
navigator.clipboard.write([
new ClipboardItem({
"image/png": blob
})
]);
};
};
reader.readAsDataURL(file);
};
注意:首先,Chrome 浏览器规定,
clipboard API
只有 HTTPS 协议的页面才能使用这个 API。不过,开发环境(localhost
)允许使用非加密协议。其次,调用时需要明确获得用户的许可。权限的具体实现使用了 Permissions API,跟剪贴板相关的有两个权限:clipboard-write
(写权限)和clipboard-read
(读权限)。总而言之言而总之就是讲内容写到剪切板脚本可以完成,但是读取剪切板时就需要用户的同意才可以继续使用了。
jsx
//先将图片解析成base64
const imageBase64 = (img) => {
let canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
let dataURL = canvas.toDataURL("image/png");
return dataURL;
};
jsx
// 再转成blod // 注意base64里面的特殊符号 在atob的时候不能识别","
const base64ToBlob = (b64Data, contentType, sliceSize) => {
contentType = contentType || "";
sliceSize = sliceSize || 512;
let byteCharacters = window.atob(b64Data);
// var byteCharacters = b64Data;
// 该atob函数将base64编码的字符串解码为一个新字符串,其中包含二进制数据每个字节的字符。
let byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
let slice = byteCharacters.slice(offset, offset + sliceSize);
let byteNumbers = new Array(slice.length);
// 通过使用.charCodeAt字符串中每个字符的方法应用它来创建一个新的数组。
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
// 将这个数组转换为实际类型的数组,方法是将其传递给Uint8Array构造函数。
let byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
// 创建一个blob:包含这条数据的URL,返回去。
let blob = new Blob(byteArrays, { type: contentType });
return blob;
};
最后转来转去也终于是实现了功能,第一次写帖子,也欢迎大佬们指导和纠正。