preface
A while ago, I received a development task, is to copy the pictures on the web page, and paste into the chat tool. This is a very common operation, in daily work we often through QQ screenshot, Ctrl + C to copy the picture to the chat box.
Github has several high star JS copy libraries, such asclipboard.js,copy-to-clipboardAnd so on, its implementation principle is selected after the element, calldocument.exceCommand('copy')
This API implements replication, but usually only supports itText copiedAnd the API is obsolete. Therefore, I decided to package a small component to replicate images in React.
Receives clipboard content
Although we don’t need to implement the clipboard -> paste process, but to clarify the data needed to copy the clipboard, we can briefly understand how the input box receives data. What kind of data is it receiving?
- The basic principle is through listening
paste
Event, interceptedclipboardData
Data (notepaste
Events can only occur incontenteditable: true
Triggered on the element) - through
reader.readAsDataURL
Achieve the generation of preview pictures, or directly upload picture data to the back end
/ / code reference: https://cloud.tencent.com/document/product/269/37448
document
.getElementById("testPasteInput")
.addEventListener("paste".function (e) {
let clipboardData = e.clipboardData;
let file;
if (
clipboardData &&
clipboardData.files &&
clipboardData.files.length > 0
) {
file = clipboardData.files[0];
var reader = new FileReader();
reader.onload = function (e) {
var image = document.getElementById("result");
image.src = e.target.result;
image.style.display = "block";
};
reader.readAsDataURL(file);
}
if (typeof file === "undefined") {
console.warn("File is undefined, please check code or browser compatibility!");
return; }});Copy the code
Through the code, we learn that we receive File/Blob type content from the clipboard in the input box via listening events.
Now that we know how messages are received, we need to figure out how to add the target content to the clipboard.
There are two main apis currently available:
excecommand
Clipboard API
Copy content to clipboard using Excecommand
The selected elements
The key to using Excecommand (‘copy’) is that we want to implement the effect of the selected element. When we think of selection, we usually think of the input select() event, but not all elements have this interface. A more general API is the Range API. We can use this API to select elements in the page or dynamically generate an element and select.
function selectFakeInput(input: HTMLELEMENT) {
let selection = window.getSelection();
let range = document.createRange(); range.selectNode(input); selection! .removeAllRanges(); selection! .addRange(range);return () = >{ selection! .removeAllRanges(); }; }Copy the code
Write clipboard
let success = document.execCommand("copy");
if(! success) {throw Error("Replication failed!");
}
Copy the code
Practice has found that this API does a good job of copying text, while image copying often fails
Copy content to the Clipboard using the Clipboard API
Convert the target content to Blob type
//text
function textToBlob(target: string = "") {
return new Blob([target], { type: "text/plain" });
}
// image SRC (image source must be cross-domain)
function imageToBlob(target: string = "") {
const response = await fetch(target);
return await response.blob();
}
Copy the code
Ask your browser if it supports it
async function isSupportClipboardWrite() {
try {
const permission = awaitnavigator.permissions? .query? ({.//@ts-ignore
name: "clipboard-write".allowWithoutGesture: false});returnpermission? .state ==="granted";
} catch (error) {
return false; }}Copy the code
Write clipboard
const data = [new window.ClipboardItem({ [blob.type]: blob })];
await navigator.clipboard.write(data);
Copy the code
Encapsulate the React component
Here, we try to use hooks encapsulation.
export const useCopyImage = (props: ClipboardImageHooksProps) = > {
const [status, setStatus] = useState<ChangeStatus>(null);
const [err, setErr] = useState<Error> (null);
const copyImage = useCallback(async (target: ImageCopyTarget) => {
try {
setErr(null);
setStatus("loading");
const canWrite = await isSupportClipboardWrite();
if (
!canWrite ||
!window.ClipboardItem || ! navigator.clipboard? .write ) {throw Error("broswer not supported!");
}
const blob = await imageToBlob(target);
const data = [new window.ClipboardItem({ [blob.type]: blob })];
await navigator.clipboard.write(data);
setStatus("done");
} catch (err) {
setStatus("error"); setErr(err); }} []);return { status, error: err, copy:copyImage };
};
Copy the code
Can be used directly after packaging
import { useCopyText } from "rc-clipboard-copy";
const App = () = > {
const { copy, error, status } = useCopyText({});
return (
<div>
<button onClick={()= > copy("hello word 2")}>copy text</button>
</div>
);
};
Copy the code
conclusion
See the Github repository for details
The resources
- Direct clipboard paste upload picture front-end JS implementation
- Messaging (Web & Applets)
- Interact with the clipboard