Browse Source

feat: add full page option in take screenshot block

Ahmad Kholid 3 years ago
parent
commit
f7f50f1d6b

+ 8 - 0
src/background/index.js

@@ -165,9 +165,17 @@ message.on('open:dashboard', async (url) => {
     console.error(error);
   }
 });
+message.on('set:active-tab', (tabId) => {
+  return browser.tabs.update(tabId, { active: true });
+});
+
 message.on('get:sender', (_, sender) => {
   return sender;
 });
+message.on('get:tab-screenshot', (options) => {
+  console.log(browser.tabs.captureVisibleTab(options), 'aaa');
+  return browser.tabs.captureVisibleTab(options);
+});
 message.on('get:file', (path) => {
   return new Promise((resolve, reject) => {
     const isFile = /\.(.*)/.test(path);

+ 16 - 3
src/background/workflow-engine/blocks-handler/handler-take-screenshot.js

@@ -22,8 +22,15 @@ function saveImage({ fileName, uri, ext }) {
 
 async function takeScreenshot(block) {
   const nextBlockId = getBlockConnection(block);
-  const { ext, quality, captureActiveTab, fileName, saveToColumn, dataColumn } =
-    block.data;
+  const {
+    ext,
+    quality,
+    captureActiveTab,
+    fileName,
+    saveToColumn,
+    dataColumn,
+    fullPage,
+  } = block.data;
 
   const saveToComputer =
     typeof block.data.saveToComputer === 'undefined'
@@ -51,7 +58,13 @@ async function takeScreenshot(block) {
 
       await new Promise((resolve) => setTimeout(resolve, 500));
 
-      const uri = await browser.tabs.captureVisibleTab(options);
+      const uri = await (fullPage
+        ? this._sendMessageToTab({
+            tabId: this.activeTab.id,
+            options,
+            name: block.name,
+          })
+        : browser.tabs.captureVisibleTab(options));
 
       if (tab) {
         await browser.windows.update(tab.windowId, { focused: true });

+ 7 - 0
src/components/newtab/workflow/edit/EditTakeScreenshot.vue

@@ -1,5 +1,12 @@
 <template>
   <div class="take-screenshot">
+    <ui-checkbox
+      :model-value="data.fullPage"
+      class="mb-2"
+      @change="updateData({ fullPage: $event })"
+    >
+      {{ t('workflow.blocks.take-screenshot.fullPage') }}
+    </ui-checkbox>
     <ui-checkbox
       :model-value="data.saveToComputer"
       class="mb-2"

+ 139 - 0
src/content/blocks-handler/handler-take-screenshot.js

@@ -0,0 +1,139 @@
+/* eslint-disable no-await-in-loop */
+import { sendMessage } from '@/utils/message';
+
+function findScrollableElement(
+  element = document.documentElement,
+  maxDepth = 5
+) {
+  if (maxDepth === 0) return null;
+
+  const excludeTags = ['SCRIPT', 'STYLE', 'SVG', 'HEAD'];
+  const isScrollable = element.scrollHeight > window.innerHeight;
+
+  if (isScrollable) return element;
+
+  for (let index = 0; index < element.childElementCount; index += 1) {
+    const currentChild = element.children.item(index);
+    const isExcluded =
+      currentChild.tagName.includes('-') ||
+      excludeTags.includes(currentChild.tagName);
+
+    if (!isExcluded) {
+      const scrollableElement = findScrollableElement(
+        currentChild,
+        maxDepth - 1
+      );
+
+      if (scrollableElement) return scrollableElement;
+    }
+  }
+
+  return null;
+}
+function injectStyle() {
+  const style = document.createElement('style');
+  style.innerText =
+    'html::-webkit-scrollbar, body::-webkit-scrollbar, .automa-scrollable-el::-webkit-scrollbar{ width: 0 !important; height: 0 !important } body.is-screenshotting [is-sticky] { position: relative !important; } .hide-fixed [is-fixed] {visibility: hidden !important; opacity: 0 !important;}';
+  style.id = 'automa-css-scroll';
+  document.body.appendChild(style);
+
+  return style;
+}
+
+const loadAsyncImg = (src) =>
+  new Promise((resolve) => {
+    const image = new Image();
+    image.onload = () => {
+      resolve(image);
+    };
+    image.src = src;
+  });
+
+export default async function ({ tabId, options }) {
+  document.body.classList.add('is-screenshotting');
+
+  const canvas = document.createElement('canvas');
+  const context = canvas.getContext('2d');
+  const maxCanvasSize = 32767;
+
+  const scrollableElement = findScrollableElement();
+  const takeScreenshot = async () => {
+    await sendMessage('set:active-tab', tabId, 'background');
+    const imageUrl = await sendMessage(
+      'get:tab-screenshot',
+      options,
+      'background'
+    );
+
+    return imageUrl;
+  };
+
+  if (!scrollableElement) {
+    const imageUrl = await takeScreenshot();
+
+    return imageUrl;
+  }
+
+  scrollableElement.classList.add('automa-scrollable-el');
+
+  const style = injectStyle();
+  const originalYPosition = window.scrollY;
+  const originalScrollHeight = scrollableElement.scrollHeight;
+
+  canvas.height =
+    scrollableElement.scrollHeight > maxCanvasSize
+      ? maxCanvasSize
+      : scrollableElement.scrollHeight;
+  canvas.width = window.innerWidth;
+
+  document.body
+    .querySelectorAll('*:not([is-sticky], [is-fixed])')
+    .forEach((el) => {
+      const { position } = getComputedStyle(el);
+
+      if (position === 'sticky') el.setAttribute('is-sticky', '');
+      else if (position === 'fixed') el.setAttribute('is-fixed', '');
+    });
+
+  let scrollPosition = 0;
+
+  while (scrollPosition <= originalScrollHeight) {
+    const imageUrl = await takeScreenshot();
+
+    if (scrollPosition > 0 && !document.body.classList.contains('hide-fixed')) {
+      document.body.classList.add('hide-fixed');
+    }
+
+    const image = await loadAsyncImg(imageUrl);
+    const newScrollPos = scrollPosition + window.innerHeight;
+
+    if (newScrollPos - originalScrollHeight > 0) {
+      context.drawImage(
+        image,
+        0,
+        newScrollPos - originalScrollHeight,
+        image.width,
+        image.height,
+        0,
+        scrollPosition,
+        image.width,
+        image.height
+      );
+    } else {
+      context.drawImage(image, 0, scrollPosition);
+    }
+
+    scrollPosition = newScrollPos;
+    scrollableElement.scrollTo(0, newScrollPos);
+
+    await new Promise((resolve) => setTimeout(resolve, 1000));
+  }
+
+  style.remove();
+  document.body.classList.remove('hide-fixed');
+  document.body.classList.remove('is-screenshotting');
+
+  scrollableElement.scrollTo(0, originalYPosition);
+
+  return canvas.toDataURL(`image/${options.format}`, options.quality / 100);
+}

+ 0 - 30
src/content/take-screenshot.js

@@ -1,30 +0,0 @@
-function findScrollableElement(element = document.body, maxDepth = 3) {
-  if (maxDepth === 0) return null;
-
-  const excludeTags = ['SCRIPT', 'STYLE', 'SVG'];
-  const isScrollable = element.scrollHeight > window.innerHeight;
-
-  if (isScrollable) return element;
-
-  for (let index = 0; index < element.childElementCount; index += 1) {
-    const currentChild = element.children.item(index);
-    const isExcluded =
-      currentChild.tagName.includes('-') ||
-      excludeTags.includes(currentChild.tagName);
-
-    if (!isExcluded) {
-      const scrollableElement = findScrollableElement(
-        currentChild,
-        maxDepth - 1
-      );
-
-      if (scrollableElement) return scrollableElement;
-    }
-  }
-
-  return null;
-}
-
-export default async function () {
-  findScrollableElement();
-}

+ 1 - 0
src/locales/en/blocks.json

@@ -388,6 +388,7 @@
       },
       "take-screenshot": {
         "name": "Take screenshot",
+        "fullPage": "Take full page screenshot",
         "description": "Take a screenshot of current active tab",
         "imageQuality": "Image quality",
         "saveToColumn": "Save screenshot to column",