Files
html2pptx/web-to-pixso/runner.js
T
李进 330f4e96ce feat: 加入 web-to-pixso 插件源码
Chrome 扩展,用于从网页提取 DOM 树为 JSON 格式。
核心文件:capture.js(76KB,提取逻辑)
2026-07-27 12:22:27 +08:00

163 lines
4.7 KiB
JavaScript

(function () {
"use strict";
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
function freezeAnimations() {
const style = document.createElement("style");
style.id = "__web-to-pixso-freeze";
style.textContent = `
*, *::before, *::after {
animation-play-state: paused !important;
transition-duration: 0s !important;
scroll-behavior: auto !important;
}
.slick-track,
.swiper-wrapper {
transition-duration: 0s !important;
}
`;
document.documentElement.appendChild(style);
for (const media of document.querySelectorAll("video, audio")) {
try {
media.pause();
} catch {
// Ignore media elements that cannot be controlled by content scripts.
}
}
}
function stabilizeCarousels() {
try {
if (window.jQuery) {
window.jQuery(".slick-slider").each((_, element) => {
try {
window.jQuery(element).slick("slickPause");
window.jQuery(element).slick("slickGoTo", 0, true);
} catch {
// Some pages expose slick classes without the jQuery plugin instance.
}
});
}
} catch {
// Best effort only.
}
for (const element of document.querySelectorAll(".swiper, .swiper-container")) {
try {
if (element.swiper) {
element.swiper.autoplay?.stop?.();
element.swiper.slideToLoop?.(0, 0, false);
element.swiper.slideTo?.(0, 0, false);
element.swiper.update?.();
}
} catch {
// Keep DOM capture running even if a carousel API rejects.
}
}
for (const wrapper of document.querySelectorAll(".swiper-wrapper, .slick-track")) {
wrapper.style.transitionDuration = "0s";
wrapper.style.animationPlayState = "paused";
}
}
async function waitForStableTopLayer(timeout = 2500) {
const start = Date.now();
const selector = [
"header",
"nav",
"[role='navigation']",
"[class*='header' i]",
"[class*='nav' i]",
"[class*='top' i]"
].join(",");
while (Date.now() - start < timeout) {
const candidates = Array.from(document.querySelectorAll(selector));
const hasVisibleCandidate = candidates.some(element => {
const rect = element.getBoundingClientRect();
const style = window.getComputedStyle(element);
return rect.width > 20 &&
rect.height > 10 &&
rect.bottom >= 0 &&
rect.top < Math.max(160, window.innerHeight * 0.2) &&
style.display !== "none" &&
style.visibility !== "hidden" &&
Number(style.opacity || 1) > 0;
});
if (hasVisibleCandidate || document.readyState === "complete") {
await delay(300);
return;
}
await delay(120);
}
}
async function scrollToLoadLazyContent() {
const maxScroll = Math.max(
document.documentElement.scrollHeight,
document.body.scrollHeight
) - window.innerHeight;
if (maxScroll <= 0) return;
const step = Math.max(480, Math.floor(window.innerHeight * 0.8));
for (let y = 0; y <= maxScroll; y += step) {
window.scrollTo(0, Math.min(y, maxScroll));
await delay(160);
}
window.scrollTo(0, maxScroll);
await delay(220);
window.scrollTo(0, 0);
await delay(220);
}
async function waitForImages(timeout = 3500) {
const images = Array.from(document.images || []);
await Promise.race([
Promise.allSettled(images.map(image => {
if (image.complete) return Promise.resolve();
return new Promise(resolve => {
image.addEventListener("load", resolve, { once: true });
image.addEventListener("error", resolve, { once: true });
});
})),
delay(timeout)
]);
}
async function waitForFonts(timeout = 3000) {
if (!document.fonts?.ready) return;
await Promise.race([document.fonts.ready, delay(timeout)]);
}
window.__webToPixsoRunCapture = async function runCapture(options = {}) {
if (!window.__webToPixsoCapture) {
throw new Error("采集引擎未加载");
}
freezeAnimations();
stabilizeCarousels();
await waitForStableTopLayer();
stabilizeCarousels();
await scrollToLoadLazyContent();
stabilizeCarousels();
await waitForImages();
await waitForFonts();
stabilizeCarousels();
await delay(200);
return window.__webToPixsoCapture({
useProxy: Boolean(options.useProxy),
concurrency: options.concurrency || "8",
captureMode: options.captureMode === "editable" ? "editable" : "mixed",
captureWidth: options.captureWidth,
selectionId: options.selectionId,
selectionWidth: options.selectionWidth
});
};
})();