mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
fix: using existing canvas option (#2017)
* refactor: document cleanup to DocumentCloner * fix: using existing canvas option * fix: lint errors * fix: preview transform origin
This commit is contained in:
parent
34b06d6365
commit
076492042a
@ -42,7 +42,7 @@
|
|||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
document.querySelector("button").addEventListener("click", function() {
|
document.querySelector("button").addEventListener("click", function() {
|
||||||
html2canvas(document.querySelector("#content"), {canvas: canvas}).then(function(canvas) {
|
html2canvas(document.querySelector("#content"), {canvas: canvas, scale: 1}).then(function(canvas) {
|
||||||
console.log('Drew on the existing canvas');
|
console.log('Drew on the existing canvas');
|
||||||
});
|
});
|
||||||
}, false);
|
}, false);
|
||||||
|
91
src/__tests__/index.ts
Normal file
91
src/__tests__/index.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import html2canvas from '../index';
|
||||||
|
|
||||||
|
import {CanvasRenderer} from '../render/canvas/canvas-renderer';
|
||||||
|
import {DocumentCloner} from '../dom/document-cloner';
|
||||||
|
import {COLORS} from '../css/types/color';
|
||||||
|
|
||||||
|
jest.mock('../core/logger');
|
||||||
|
jest.mock('../css/layout/bounds');
|
||||||
|
jest.mock('../dom/document-cloner');
|
||||||
|
jest.mock('../dom/node-parser', () => {
|
||||||
|
return {
|
||||||
|
isBodyElement: () => false,
|
||||||
|
isHTMLElement: () => false,
|
||||||
|
parseTree: jest.fn().mockImplementation(() => {
|
||||||
|
return {styles: {}};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('../render/stacking-context');
|
||||||
|
jest.mock('../render/canvas/canvas-renderer');
|
||||||
|
|
||||||
|
describe('html2canvas', () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion
|
||||||
|
const element = {
|
||||||
|
ownerDocument: {
|
||||||
|
defaultView: {
|
||||||
|
pageXOffset: 12,
|
||||||
|
pageYOffset: 34
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as HTMLElement;
|
||||||
|
|
||||||
|
it('should render with an element', async () => {
|
||||||
|
DocumentCloner.destroy = jest.fn().mockReturnValue(true);
|
||||||
|
await html2canvas(element);
|
||||||
|
expect(CanvasRenderer).toHaveBeenLastCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
backgroundColor: 0xffffffff,
|
||||||
|
scale: 1,
|
||||||
|
height: 50,
|
||||||
|
width: 200,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
scrollX: 12,
|
||||||
|
scrollY: 34,
|
||||||
|
canvas: undefined
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(DocumentCloner.destroy).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have transparent background with backgroundColor: null', async () => {
|
||||||
|
await html2canvas(element, {backgroundColor: null});
|
||||||
|
expect(CanvasRenderer).toHaveBeenLastCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
backgroundColor: COLORS.TRANSPARENT
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use existing canvas when given as option', async () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion
|
||||||
|
const canvas = {} as HTMLCanvasElement;
|
||||||
|
await html2canvas(element, {canvas});
|
||||||
|
expect(CanvasRenderer).toHaveBeenLastCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
canvas
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not remove cloned window when removeContainer: false', async () => {
|
||||||
|
DocumentCloner.destroy = jest.fn();
|
||||||
|
await html2canvas(element, {removeContainer: false});
|
||||||
|
expect(CanvasRenderer).toHaveBeenLastCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
backgroundColor: 0xffffffff,
|
||||||
|
scale: 1,
|
||||||
|
height: 50,
|
||||||
|
width: 200,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
scrollX: 12,
|
||||||
|
scrollY: 34,
|
||||||
|
canvas: undefined
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(DocumentCloner.destroy).not.toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
17
src/core/__mocks__/logger.ts
Normal file
17
src/core/__mocks__/logger.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export class Logger {
|
||||||
|
debug() {}
|
||||||
|
|
||||||
|
static create() {}
|
||||||
|
|
||||||
|
static destroy() {}
|
||||||
|
|
||||||
|
static getInstance(): Logger {
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
info() {}
|
||||||
|
|
||||||
|
error() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const logger = new Logger();
|
4
src/css/layout/__mocks__/bounds.ts
Normal file
4
src/css/layout/__mocks__/bounds.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export const {Bounds} = jest.requireActual('../bounds');
|
||||||
|
export const parseBounds = () => {
|
||||||
|
return new Bounds(0, 0, 200, 50);
|
||||||
|
};
|
16
src/dom/__mocks__/document-cloner.ts
Normal file
16
src/dom/__mocks__/document-cloner.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export class DocumentCloner {
|
||||||
|
clonedReferenceElement?: HTMLElement;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion
|
||||||
|
this.clonedReferenceElement = {} as HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
toIFrame() {
|
||||||
|
return Promise.resolve({});
|
||||||
|
}
|
||||||
|
|
||||||
|
static destroy() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -410,6 +410,14 @@ export class DocumentCloner {
|
|||||||
: ` ${PSEUDO_HIDE_ELEMENT_CLASS_AFTER}`;
|
: ` ${PSEUDO_HIDE_ELEMENT_CLASS_AFTER}`;
|
||||||
return anonymousReplacedElement;
|
return anonymousReplacedElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static destroy(container: HTMLIFrameElement): boolean {
|
||||||
|
if (container.parentNode) {
|
||||||
|
container.parentNode.removeChild(container);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PseudoElementType {
|
enum PseudoElementType {
|
||||||
|
11
src/index.ts
11
src/index.ts
@ -116,6 +116,7 @@ const renderElement = async (element: HTMLElement, opts: Partial<Options>): Prom
|
|||||||
const renderOptions = {
|
const renderOptions = {
|
||||||
id: instanceName,
|
id: instanceName,
|
||||||
cache: options.cache,
|
cache: options.cache,
|
||||||
|
canvas: options.canvas,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
scale: options.scale,
|
scale: options.scale,
|
||||||
x: options.x,
|
x: options.x,
|
||||||
@ -153,7 +154,7 @@ const renderElement = async (element: HTMLElement, opts: Partial<Options>): Prom
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.removeContainer === true) {
|
if (options.removeContainer === true) {
|
||||||
if (!cleanContainer(container)) {
|
if (!DocumentCloner.destroy(container)) {
|
||||||
Logger.getInstance(instanceName).error(`Cannot detach cloned iframe as it is not in the DOM anymore`);
|
Logger.getInstance(instanceName).error(`Cannot detach cloned iframe as it is not in the DOM anymore`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,11 +164,3 @@ const renderElement = async (element: HTMLElement, opts: Partial<Options>): Prom
|
|||||||
CacheStorage.destroy(instanceName);
|
CacheStorage.destroy(instanceName);
|
||||||
return canvas;
|
return canvas;
|
||||||
};
|
};
|
||||||
|
|
||||||
const cleanContainer = (container: HTMLIFrameElement): boolean => {
|
|
||||||
if (container.parentNode) {
|
|
||||||
container.parentNode.removeChild(container);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
@ -71,10 +71,12 @@ export class CanvasRenderer {
|
|||||||
this.canvas = options.canvas ? options.canvas : document.createElement('canvas');
|
this.canvas = options.canvas ? options.canvas : document.createElement('canvas');
|
||||||
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
|
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
if (!options.canvas) {
|
||||||
this.canvas.width = Math.floor(options.width * options.scale);
|
this.canvas.width = Math.floor(options.width * options.scale);
|
||||||
this.canvas.height = Math.floor(options.height * options.scale);
|
this.canvas.height = Math.floor(options.height * options.scale);
|
||||||
this.canvas.style.width = `${options.width}px`;
|
this.canvas.style.width = `${options.width}px`;
|
||||||
this.canvas.style.height = `${options.height}px`;
|
this.canvas.style.height = `${options.height}px`;
|
||||||
|
}
|
||||||
this.fontMetrics = new FontMetrics(document);
|
this.fontMetrics = new FontMetrics(document);
|
||||||
this.ctx.scale(this.options.scale, this.options.scale);
|
this.ctx.scale(this.options.scale, this.options.scale);
|
||||||
this.ctx.translate(-options.x + options.scrollX, -options.y + options.scrollY);
|
this.ctx.translate(-options.x + options.scrollX, -options.y + options.scrollY);
|
||||||
|
@ -42,8 +42,10 @@ function onBrowserChange(browserTest: Test) {
|
|||||||
previewImage.src = `/results/${browserTest.screenshot}.png`;
|
previewImage.src = `/results/${browserTest.screenshot}.png`;
|
||||||
if (browserTest.devicePixelRatio > 1) {
|
if (browserTest.devicePixelRatio > 1) {
|
||||||
previewImage.style.transform = `scale(${1 / browserTest.devicePixelRatio})`;
|
previewImage.style.transform = `scale(${1 / browserTest.devicePixelRatio})`;
|
||||||
|
previewImage.style.transformOrigin = 'top left';
|
||||||
} else {
|
} else {
|
||||||
previewImage.style.transform = '';
|
previewImage.style.transform = '';
|
||||||
|
previewImage.style.transformOrigin = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user