html2canvas
非常适合截取屏幕截图,但目前无法在 textAreas 中捕获较长的文本或在多选中捕获多个变量。参见 https://github.com/niklasvh/html2canvas/issues/2008 进行讨论。
@chawes13 提供了很好的建议来解决这些导致以下功能的问题。我设置
height: document.body.scrollHeight
垂直裁剪我正在处理的应用程序的屏幕截图。但是,高度可以在onclone
过程中改变。我尝试了很多不同的选择,但似乎唯一可行的是(1)不使用任何垂直裁剪或(2)使用基于初始身高的裁剪,不幸的是,它切断了下半部分如果在onclone
期间进行了更改,则图像。该应用程序有很多选项卡,并且会随着加载不同的数据或选择变量而动态更改大小,因此无法设置为一个固定高度。
问题:是否可以确定克隆文档的(滚动)高度,并在需要时使用它来调整屏幕截图高度?
function generate_screenshot() {
html2canvas($("body")[0], {
y: 55,
height: document.body.scrollHeight,
onclone: (clonedDocument) => {
Array.from(clonedDocument.querySelectorAll("textarea")).forEach((textArea) => {
if (textArea && textArea.value.length > 30) {
const labelFor = textArea.getAttribute("id")
const label = clonedDocument.querySelector(`label[for="${labelFor}"]`)
const div = clonedDocument.createElement("div")
div.innerText = textArea.value
div.style.border = "1px solid #d3d3d3"
div.style.padding = "10px 10px 10px 10px"
div.style.width = "100%"
div.style.borderRadius = "5px"
div.style.boxSizing = "border-box";
div.style.margin = "0";
div.style.backgroundColor = "white"
textArea.style.display = "none"
textArea.parentElement.append(label, div);
}
})
Array.from(clonedDocument.querySelectorAll('select[multiple]')).forEach((msel) => {
const multiSelect = document.querySelector("#" + msel.getAttribute("id"));
if (multiSelect && multiSelect.selectedOptions.length > 1) {
const clonedMultiSelect = clonedDocument.querySelector("#" + msel.getAttribute("id"));
const list = clonedDocument.createElement('ul')
Array.from(multiSelect.selectedOptions).forEach((option) => {
const item = clonedDocument.createElement('li')
item.innerHTML = option.value
item.style = "list-style: none; padding-left: 0.5em"
item.style.width = "100%"
list.appendChild(item)
})
list.style.border = "1px solid #d3d3d3"
list.style.padding = "5px 5px 5px 5px"
list.style.width = "100%"
list.style.backgroundColor = "white"
list.style.borderRadius = "5px"
clonedMultiSelect.style.display = "none"
clonedMultiSelect.parentElement.appendChild(list)
}
});
},
ignoreElements: function(el) {
return el.classList.contains("navbar-inverse") || el.classList.contains("dropdown-menu");
}
}).then(canvas => {
var img = document.createElement("img");
img.src = canvas.toDataURL("png");
img.width = parseInt(canvas.style.width);
img.height = parseInt(canvas.style.height);
$("#screenshot_preview").empty();
$("#screenshot_preview").append(img);
});
}
好的,没有测试过,但你可以尝试这样的事情:
function generate_screenshot() {
html2canvas($("body")[0], {
y: 55,
onclone: (clonedDocument) => {
Array.from(clonedDocument.querySelectorAll("textarea")).forEach((textArea) => {
if (textArea && textArea.value.length > 30) {
const labelFor = textArea.getAttribute("id")
const label = clonedDocument.querySelector(`label[for="${labelFor}"]`)
const div = clonedDocument.createElement("div")
div.innerText = textArea.value
div.style.border = "1px solid #d3d3d3"
div.style.padding = "10px 10px 10px 10px"
div.style.width = "100%"
div.style.borderRadius = "5px"
div.style.boxSizing = "border-box";
div.style.margin = "0";
div.style.backgroundColor = "white"
textArea.style.display = "none"
textArea.parentElement.append(label, div);
}
})
Array.from(clonedDocument.querySelectorAll('select[multiple]')).forEach((msel) => {
const multiSelect = document.querySelector("#" + msel.getAttribute("id"));
if (multiSelect && multiSelect.selectedOptions.length > 1) {
const clonedMultiSelect = clonedDocument.querySelector("#" + msel.getAttribute("id"));
const list = clonedDocument.createElement('ul')
Array.from(multiSelect.selectedOptions).forEach((option) => {
const item = clonedDocument.createElement('li')
item.innerHTML = option.value
item.style = "list-style: none; padding-left: 0.5em"
item.style.width = "100%"
list.appendChild(item)
})
list.style.border = "1px solid #d3d3d3"
list.style.padding = "5px 5px 5px 5px"
list.style.width = "100%"
list.style.backgroundColor = "white"
list.style.borderRadius = "5px"
clonedMultiSelect.style.display = "none"
clonedMultiSelect.parentElement.appendChild(list)
}
});
// Dynamically adjust height based on cloned document's scroll height
var clonedBody = clonedDocument.querySelector("body");
var clonedHeight = clonedBody.scrollHeight;
clonedBody.style.height = clonedHeight + "px";
},
ignoreElements: function(el) {
return el.classList.contains("navbar-inverse") || el.classList.contains("dropdown-menu");
}
}).then(canvas => {
var img = document.createElement("img");
img.src = canvas.toDataURL("png");
img.width = parseInt(canvas.style.width);
img.height = parseInt(canvas.style.height);
// Dynamically adjust width and height based on cloned document's size
var clonedBody = canvas.parentNode.querySelector("body");
var clonedWidth = clonedBody.offsetWidth;
var clonedHeight = clonedBody.offsetHeight;
canvas.style.width = clonedWidth + "px";
canvas.style.height = clonedHeight + "px";
$("#screenshot_preview").empty();
$("#screenshot_preview").append(img);
});
}
基本上我将“onclone”移到了开头,在高度和 y 选项之前。在 onclone 函数中,我添加了代码以根据克隆文档的滚动高度动态调整高度。
我曾经在尝试从报告页面创建 PDF 时遇到过这个问题。这是我的代码。它不仅在拆分页面时打印所有内容。
splitImages(canvas: HTMLCanvasElement, maxHeight: number) {
let y = 0;
let images = [];
let acanvas = [];
let ctx = canvas.getContext('2d');
while (y < canvas.height) {
let height = y + maxHeight > canvas.height ? canvas.height - y : y + maxHeight;
let imgData = ctx.getImageData(0, y, canvas.width, y + height);
let newCanvas = document.createElement('canvas');
newCanvas.width = canvas.width;
newCanvas.height = maxHeight;
let ctxNewCanvas = newCanvas.getContext('2d');
ctxNewCanvas.putImageData(imgData, 0, 0);
if (y + maxHeight < canvas.height)
this.trimCanvas(newCanvas);
acanvas.push(newCanvas);
images.push(newCanvas.toDataURL());
y = y + newCanvas.height;
}
return { images, acanvas };
}
exportPDF(div_id: string, relName: string, mode: string = 'table', trimCanvas: boolean = true) {
let data: HTMLElement = document.getElementById(div_id);
html2canvas(data, {
windowHeight: data.scrollHeight,
scrollY: -window.scrollY,
onclone: doc => {
doc.querySelector('#' + div_id).removeChild(doc.querySelector("#btnExport"));
(<HTMLElement>doc.querySelector('#relLogo span')).style.display = 'flex';
if (mode == 'table') {
let tr = document.createElement('tr');
let numCols = doc.querySelector('tbody tr').children.length;
tr.innerHTML = "<td colspan='" + numCols + "'></td>";
doc.querySelector('tbody').lastChild.after(tr);
}
}
}).then(canvas => {
let pdf: jsPDF = new jsPDF({ orientation: 'landscape', unit: 'px', format: 'a4' });
if (trimCanvas)
this.trimCanvas(canvas);
const dataUrl: string = canvas.toDataURL()
const imgProps: ImageProperties = pdf.getImageProperties(dataUrl);
let pdfWidth: number = pdf.internal.pageSize.getWidth() * 0.95;
let pdfHeight: number = pdf.internal.pageSize.getHeight() * 0.925;
let borderTop: number = pdf.internal.pageSize.getWidth() * 0.025;
let borderLeft: number = pdf.internal.pageSize.getHeight() * 0.025;
let maxPxPP = (pdfHeight / pdfWidth) * imgProps.width;
let canvasSection = this.splitImages(canvas, maxPxPP);
let imagens: string[] = canvasSection.images;
pdf.addImage(imagens[0], 'PNG', borderLeft, borderTop, pdfWidth, pdfHeight);
if (imagens.length > 1) {
imagens.shift();
imagens.forEach(img => {
pdf.addPage('a4', 'l');
pdf.addImage(img, 'PNG', borderLeft, borderTop, pdfWidth, pdfHeight);
})
}
pdf.save(relName + '.pdf');
});
}