英文:
snipping tool in Chrome Extension not working properly
问题
我有一个Chrome扩展程序,其中有一个我一直在开发的截图工具。它几乎按照我希望的方式工作,问题在于似乎一旦加载网页,它就执行整个过程,而我本打算让用户单击按钮然后启动该过程。
所以,我基于CaptureVisibleTab API进行了一些脚本之间的通信。
首先是我的manifest.json(相关信息):
"manifest_version": 3,
"permissions": [
"activeTab",
"tabs",
"contextMenus",
"scripting",
"storage",
"identity"
],
"background": {
"service_worker": "scripts/background.js"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["scripts/content.js"],
"all_frames": false,
"run_at": "document_end"
在popup.HTML上有一个带有id为"snip"的按钮,相当容易理解。
然后在popup.js中,有一个用于snip的变量和一个事件监听器来排队截图:
var snip = document.getElementById("snip");
snip.addEventListener("click", function() {
chrome.runtime.sendMessage({ action: "captureVisibleTab" }, function(response) {
console.log(response.imageUrl);
});
});
现在在background.js中有一个用于该操作的监听器,并且仅返回图像结果以供裁剪,然后发送到内容脚本:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.action === "captureVisibleTab") {
chrome.tabs.captureVisibleTab(null, { format: "png" }, function(imageUrl) {
sendResponse({ imageUrl: imageUrl });
});
return true; // 必须指示响应将异步发送
}
});
最后,在content.js中,我只有一个用于captureVisibleTab的函数和消息。当它收到消息时,它会将网页的图像作为参数传递:
function cropScreenshot(imageUrl) {
// ... 你的裁剪代码
}
chrome.runtime.sendMessage({ action: "captureVisibleTab" }, function(response) {
if (response && response.imageUrl) {
// 调用cropScreenshot函数并将imageUrl作为参数传递
var imageUrl = response.imageUrl
cropScreenshot(imageUrl);
} else {
console.log("捕获可见选项卡时出错");
}
});
这就是大致的情况。我尝试在popup和background之间添加一个名为startSnip的操作,但这似乎不起作用。如今,这是我接近正确的最近一次尝试。所以我确定现在有明显的问题,只是我看不到。非常感谢任何帮助。
英文:
I have a Chrome extension, that has a snip tool I've been working on. Its almost working as I'd like, the issue is it seems that as soon as I load a webpage it executes the whole process, where I'm intending to have the user click a button and then it starts the process.
So, I've based on this on the CaptureVisibleTab API and I have to communicate between some scripts to do this.
To start my manifest.json (relevant info)
"manifest_version": 3,
permissions": [
"activeTab",
"tabs",
"contextMenus",
"scripting",
"storage",
"identity"
],
"background": {
"service_worker": "scripts/background.js"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["scripts/content.js"],
"all_frames": false,
"run_at": "document_end"
I have a button on the popup.HTML that has a id called "snip", pretty self explanitory
then in the popup.js there's a variable for the snip and a event listener to then que the screenshot
var snip = document.getElementById("snip");
snip.addEventListener("click", function() {
chrome.runtime.sendMessage({ action: "captureVisibleTab" }, function(response) {
console.log(response.imageUrl);
});
});
Now in background.js it has a listener for the action
and just returns the image result to then be cropped, and sent to the content script
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.action === "captureVisibleTab") {
chrome.tabs.captureVisibleTab(null, { format: "png" }, function(imageUrl) {
sendResponse({ imageUrl: imageUrl });
});
return true; // Required to indicate that the response will be sent asynchronously
}
});
finally in the content.js, I just have the function and the message for the captureVisibleTab
where when it gets it, it takes the image of the webapge and passes it as a argument in the function
function cropScreenshot(imageUrl) {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var isMouseDown = false;
var startX, startY, endX, endY;
var img = new Image();
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
canvas.style.display = "block";
var overlay = document.createElement("div");
overlay.style.position = "absolute";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = canvas.width + "px";
overlay.style.height = canvas.height + "px";
overlay.style.background = "rgba(0,0,0,0.4)";
document.body.appendChild(overlay);
overlay.addEventListener("mousedown", function(e) {
isMouseDown = true;
startX = e.offsetX;
startY = e.offsetY;
});
overlay.addEventListener("mouseup", function(e) {
isMouseDown = false;
endX = e.offsetX;
endY = e.offsetY;
var width = endX - startX;
var height = endY - startY;
if (width > 0 && height > 0) {
var croppedCanvas = document.createElement("canvas");
var croppedCtx = croppedCanvas.getContext("2d");
croppedCanvas.width = width;
croppedCanvas.height = height;
croppedCtx.drawImage(canvas, startX, startY, width, height, 0, 0, width, height);
var downloadLink = document.createElement("a");
downloadLink.href = croppedCanvas.toDataURL("image/png");
downloadLink.download = "snipped-img.png";
// Attach the anchor to the document and click it
document.body.appendChild(downloadLink);
downloadLink.click();
// Remove the anchor from the document
document.body.removeChild(downloadLink);
}
canvas.style.display = "none";
overlay.remove();
});
overlay.addEventListener("mousemove", function(e) {
if (isMouseDown) {
var width = e.offsetX - startX;
var height = e.offsetY - startY;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
ctx.fillStyle = "rgba(255,255,255,0.5)";
ctx.fillRect(startX, startY, width, height);
}
});
};
img.src = imageUrl;
}
chrome.runtime.sendMessage({ action: "captureVisibleTab" }, function(response) {
if (response && response.imageUrl) {
// Call the cropScreenshot function and pass the imageUrl as a parameter
var imageUrl = response.imageUrl
cropScreenshot(imageUrl);
} else {
console.log("Error capturing visible tab");
}
});
that is the gist of it, I have tried putting a action between popup and background, like startSnip, but that doesn't work. This as it sits is currently the closest I've come to getting it right. So I'm sure its something obvious now, and I just can't see it. any help is greatly appreciated
答案1
得分: 2
从你发送的代码中,截图功能在页面加载时就会执行,而不需要用户操作(比如点击按钮)。这可能是由于你的代码结构引起的。
以下是代码中监听 onMessage
并在操作是 captureVisibleTab
时执行的部分:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.action === "captureVisibleTab") {
chrome.tabs.captureVisibleTab(null, { format: "png" }, function(imageUrl) {
sendResponse({ imageUrl: imageUrl });
});
return true; // 需要指示响应将以异步方式发送
}
});
这部分代码在按钮点击时触发 sendMessage
,是正确的:
snip.addEventListener("click", function() {
chrome.runtime.sendMessage({ action: "captureVisibleTab" }, function(response) {
console.log(response.imageUrl);
});
});
然而,在你的 content.js
文件末尾,有以下代码:
chrome.runtime.sendMessage({ action: "captureVisibleTab" }, function(response){
if (response && response.imageUrl) {
// 调用 cropScreenshot 函数并将 imageUrl 作为参数传递
var imageUrl = response.imageUrl
cropScreenshot(imageUrl);
} else {
console.log("Error capturing visible tab");
}
});
这部分代码没有被封装在任何函数中。这部分代码会在 content.js
加载时执行,意味着每当页面加载时,这部分代码都会运行。
从你的 manifest.json
文件来看,你的 content_scripts
在 document_end
时运行 content.js
。这就是为什么你的扩展在页面加载时会进行截图的原因。
要解决这个问题,你可以将最后的代码部分封装在一个函数中,或者对代码结构进行一些修改。
英文:
From the codes you send the screenshot is functioning whenever you open a page without listening on user action (button click) might caused by your code structure.
This is the part where it is listening onMessage
and proceed if action is captureVisibleTab
.
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.action === "captureVisibleTab") {
chrome.tabs.captureVisibleTab(null, { format: "png" }, function(imageUrl) {
sendResponse({ imageUrl: imageUrl });
});
return true; // Required to indicate that the response will be sent asynchronously
}
});
This part of the code is fine where it only fires sendMessage
when button is clicked.
snip.addEventListener("click", function() {
chrome.runtime.sendMessage({ action: "captureVisibleTab" }, function(response) {
console.log(response.imageUrl);
});
});
However, in your content.js
you have this code at the end of the script.
chrome.runtime.sendMessage({ action: "captureVisibleTab" }, function(response){
if (response && response.imageUrl) {
// Call the cropScreenshot function and pass the imageUrl as a parameter
var imageUrl = response.imageUrl
cropScreenshot(imageUrl);
} else {
console.log("Error capturing visible tab");
}
});
This part of code is not enclosed within any function. This part of code will sendMessage
with captureVisibleTab
action. Which means, this part of code will run whenever content.js
is loaded.
From your manifest.json
, your content_scripts will runs content.js
at document_end
. Which means the last part of the code of content.js is firing whenever the page is loaded.
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["scripts/content.js"],
"all_frames": false,
"run_at": "document_end"
This might be the reason why your extension is taking screenshot whenever page is loaded. To fix it, you may enclose the last part of code in a function or make some changes to the code structures.
答案2
得分: 1
简化消息传递
正如其他人已经指出的,主要问题在于您的内容脚本。captureVisibleTab方法会在页面加载时立即调用,而不是在单击按钮时调用。您可以修复这个问题,并按照下面的方式简化您的代码。
下面的代码已经经过测试并可以工作。
background.js
您可以完全删除弹出窗口,而是使用onClicked事件。这样用户只需要单击扩展程序图标即可捕获当前标签页。可以通过清除默认弹出窗口处理程序并添加自己的处理程序来实现,如下所示:
// 允许在清单中设置了弹出窗口时触发onClicked
chrome.action.setPopup({ popup: "" });
chrome.action.onClicked.addListener((tab) => {
chrome.tabs
.captureVisibleTab(tab.windowId, { format: "png" })
.then((dataUrl) => {
chrome.tabs.sendMessage(tab.id, {
action: "captureVisibleTab",
tabId: tab.id,
length: dataUrl.length,
dataUrl: dataUrl,
});
});
});
content.js
然后在内容脚本中添加一个监听器来处理捕获的图像:
chrome.runtime.onMessage.addListener((message) => {
if (message.action === "captureVisibleTab") {
// 进行一些操作...
// cropScreenshot(message.dataUrl);
}
});
manifest.json
如果背景脚本在内容脚本加载之前尝试发送消息,它将失败,因为没有处理程序来接收消息。为了避免这个问题,您可以修改manifest中的run_at,提前加载内容脚本。这样,即使页面仍在加载广告等内容,您的捕获按钮仍将正常工作。
"content_scripts": [
{
"run_at": "document_start",
"js": ["scripts/content.js"],
... 等等
图像裁剪
有许多优秀的图像裁剪库可供使用,比如ImageCropper、Croppr和Cropper。每个库都具有不同的特点和复杂性。您可以考虑使用其中一个来替代您的自定义代码。
英文:
Simplified Messaging
As others have already pointed out, the main problem is with your content script. The captureVisibleTab method is called immediately on page load and not when a button is clicked. You can fix this and also simplify your code as shown.
The code below has been tested and works
background.js
You can remove the popup completely and instead use the onClicked event. This way users only need to click on the extension icon to capture the current tab. This can be done by clearing the default popup handler and adding your own handler as shown:
// Allows onClicked to fire when popup was set in manifest
chrome.action.setPopup({ popup: "" });
chrome.action.onClicked.addListener((tab) => {
chrome.tabs
.captureVisibleTab(tab.windowId, { format: "png" })
.then((dataUrl) => {
chrome.tabs.sendMessage(tab.id, {
action: "captureVisibleTab",
tabId: tab.id,
length: dataUrl.length,
dataUrl: dataUrl,
});
});
});
content.js
And to the content script add a listener to process the captured image:
chrome.runtime.onMessage.addListener((message) => {
if (message.action === "captureVisibleTab") {
// do something...
// cropScreenshot(message.dataUrl);
}
});
manifest.json
If the background script tries to send a message before the content script has loaded it will fail. There is no handler to receive the message. To avoid this problem you can modify run_at in the manifest to load the content script early. This way your capture button will still work even while the page is still loading ads, etc.
"content_scripts": [
{
"run_at": "document_start",
"js": ["scripts/content.js"],
... etc
Image Cropping
There are a number of good image cropping libraries available, like ImageCropper, Croppr, and Cropper. Each has different features and complexity. You may want to consider one of these in place of your custom code.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论