- 目录 web-to-pixso/ → web-to-ppt/ - 所有代码引用:PIXSO → WEB_TO_PPT, webToPixso → webToPPT - 数据格式:pixso-design-capture → web-to-ppt-capture - CSS 选择器:__web_to_pixso_ → __web_to_ppt_ - README 重写,移除 Pixso 相关描述 - docs/web-to-pixso-mapping.md → docs/web-to-ppt-mapping.md
163 lines
4.7 KiB
JavaScript
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-ppt-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.__webToPPTRunCapture = async function runCapture(options = {}) {
|
|
if (!window.__webToPPTCapture) {
|
|
throw new Error("采集引擎未加载");
|
|
}
|
|
|
|
freezeAnimations();
|
|
stabilizeCarousels();
|
|
await waitForStableTopLayer();
|
|
stabilizeCarousels();
|
|
await scrollToLoadLazyContent();
|
|
stabilizeCarousels();
|
|
await waitForImages();
|
|
await waitForFonts();
|
|
stabilizeCarousels();
|
|
await delay(200);
|
|
|
|
return window.__webToPPTCapture({
|
|
useProxy: Boolean(options.useProxy),
|
|
concurrency: options.concurrency || "8",
|
|
captureMode: options.captureMode === "editable" ? "editable" : "mixed",
|
|
captureWidth: options.captureWidth,
|
|
selectionId: options.selectionId,
|
|
selectionWidth: options.selectionWidth
|
|
});
|
|
};
|
|
})();
|