Compare commits

...

11 Commits

31 changed files with 232 additions and 107 deletions

View File

@ -101,22 +101,22 @@ jobs:
- os: macos-latest
name: OSX Safari Stable
targetBrowser: Safari_Stable
- os: macos-latest
- os: macos-10.15
name: iOS Simulator Safari 12
targetBrowser: Safari_IOS_12
xcode: /Applications/Xcode_10.3.app
- os: macos-latest
- os: macos-10.15
name: iOS Simulator Safari 13
targetBrowser: Safari_IOS_13
xcode: /Applications/Xcode_11.6_beta.app
- os: macos-latest
xcode: /Applications/Xcode_11.7.app
- os: macos-10.15
name: iOS Simulator Safari 14
targetBrowser: Safari_IOS_14
xcode: /Applications/Xcode_12_beta.app
xcode: /Applications/Xcode_12.4.app
- os: macos-11
name: iOS Simulator Safari 15
targetBrowser: Safari_IOS_15
xcode: /Applications/Xcode_13.0.app
xcode: /Applications/Xcode_13.2.app
- os: windows-latest
name: Windows Internet Explorer 9 (Emulated)
targetBrowser: IE_9

View File

@ -2,6 +2,39 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [1.3.3](https://github.com/niklasvh/html2canvas/compare/v1.3.2...v1.3.3) (2021-11-23)
### ci
* fix macos action runners (#2757) ([ed57781](https://github.com/niklasvh/html2canvas/commit/ed577815949b6a565df54f2101cf6f0fb731c290)), closes [#2757](https://github.com/niklasvh/html2canvas/issues/2757)
### fix
* "offsets" text when font is big ([fd22a01](https://github.com/niklasvh/html2canvas/commit/fd22a01a3c9e39293f47caaed0c0e13d2dc8170c))
* const enums (#2651) ([eeda86b](https://github.com/niklasvh/html2canvas/commit/eeda86bd5e81fb4e97675fe9bee3d4d15899997f)), closes [#2651](https://github.com/niklasvh/html2canvas/issues/2651)
## [1.3.2](https://github.com/niklasvh/html2canvas/compare/v1.3.1...v1.3.2) (2021-08-15)
### docs
* add warning for webgl cloning with preserveDrawingBuffer=false (#2661) ([01ed879](https://github.com/niklasvh/html2canvas/commit/01ed87907ad9c7688880e2c5cb8ebc22ef73a4d8)), closes [#2661](https://github.com/niklasvh/html2canvas/issues/2661)
* include src files on www (#2660) ([58ff000](https://github.com/niklasvh/html2canvas/commit/58ff0003f77d825ac027eeec95fa80c0123eaf8f)), closes [#2660](https://github.com/niklasvh/html2canvas/issues/2660)
### feat
* add support for data-html2canvas-debug property for debugging (#2658) ([cd0d725](https://github.com/niklasvh/html2canvas/commit/cd0d7258c3a93f2989d5d9ec0244ba2763ea2d23)), closes [#2658](https://github.com/niklasvh/html2canvas/issues/2658)
### fix
* disable transition properties (#2659) ([f143166](https://github.com/niklasvh/html2canvas/commit/f1431665513e0a4636fb167a241f4a0571ba728a)), closes [#2659](https://github.com/niklasvh/html2canvas/issues/2659)
* overflows with absolutely positioned content (#2663) ([38c6829](https://github.com/niklasvh/html2canvas/commit/38c682955a9299ca7785af71d8f251df799405b0)), closes [#2663](https://github.com/niklasvh/html2canvas/issues/2663)
## [1.3.1](https://github.com/niklasvh/html2canvas/compare/v1.3.0...v1.3.1) (2021-08-14)
@ -192,9 +225,14 @@ All notable changes to this project will be documented in this file. See [standa
* update www deps (#2525) ([2a013e2](https://github.com/niklasvh/html2canvas/commit/2a013e20c814b7dbaea98f54f0bde8f553eb79a2)), closes [#2525](https://github.com/niklasvh/html2canvas/issues/2525)
### feat
* add support for border-style dashed, dotted, double (#2531) ([72cd528](https://github.com/niklasvh/html2canvas/commit/72cd5284296e4cdb3fe88f2982ec7528604b6618))
### fix
* opacity with overflow hidden (#2450) ([82b7da5](https://github.com/niklasvh/html2canvas/commit/82b7da558c342e7f53d298bb1d843a5db86c3b21)), closes [#2450](https://github.com/niklasvh/html2canvas/issues/2450)
* top right border radius (#2522) ([ba17267](https://github.com/niklasvh/html2canvas/commit/ba172678f07f962e9f54b398df087e86217d7a13))
### test

View File

@ -34,19 +34,19 @@ module.exports = function(config) {
base: 'MobileSafari',
name: 'iPhone 8',
platform: 'iOS',
sdk: '13.6'
sdk: '13.7'
},
Safari_IOS_14: {
base: 'MobileSafari',
name: 'iPhone 8',
platform: 'iOS',
sdk: '14.0'
sdk: '14.4'
},
Safari_IOS_15: {
base: 'MobileSafari',
name: 'iPhone 8',
platform: 'iOS',
sdk: '15.0'
sdk: '15.2'
},
SauceLabs_IE9: {
base: 'SauceLabs',

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "html2canvas",
"version": "1.3.1",
"version": "1.3.3",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@ -6,7 +6,7 @@
"module": "dist/html2canvas.esm.js",
"typings": "dist/types/index.d.ts",
"browser": "dist/html2canvas.js",
"version": "1.3.1",
"version": "1.3.3",
"author": {
"name": "Niklas von Hertzen",
"email": "niklasvh@gmail.com",

29
src/core/debugger.ts Normal file
View File

@ -0,0 +1,29 @@
const elementDebuggerAttribute = 'data-html2canvas-debug';
export const enum DebuggerType {
NONE,
ALL,
CLONE,
PARSE,
RENDER
}
const getElementDebugType = (element: Element): DebuggerType => {
const attribute = element.getAttribute(elementDebuggerAttribute);
switch (attribute) {
case 'all':
return DebuggerType.ALL;
case 'clone':
return DebuggerType.CLONE;
case 'parse':
return DebuggerType.PARSE;
case 'render':
return DebuggerType.RENDER;
default:
return DebuggerType.NONE;
}
};
export const isDebugging = (element: Element, type: Omit<DebuggerType, DebuggerType.NONE>): boolean => {
const elementType = getElementDebugType(element);
return elementType === DebuggerType.ALL || type === elementType;
};

View File

@ -143,7 +143,6 @@ export class CSSParsedDeclaration {
textTransform: ReturnType<typeof textTransform.parse>;
transform: ReturnType<typeof transform.parse>;
transformOrigin: ReturnType<typeof transformOrigin.parse>;
transitionDuration: ReturnType<typeof duration.parse>;
visibility: ReturnType<typeof visibility.parse>;
webkitTextStrokeColor: Color;
webkitTextStrokeWidth: ReturnType<typeof webkitTextStrokeWidth.parse>;
@ -221,7 +220,6 @@ export class CSSParsedDeclaration {
this.textTransform = parse(context, textTransform, declaration.textTransform);
this.transform = parse(context, transform, declaration.transform);
this.transformOrigin = parse(context, transformOrigin, declaration.transformOrigin);
this.transitionDuration = parse(context, duration, declaration.transitionDuration);
this.visibility = parse(context, visibility, declaration.visibility);
this.webkitTextStrokeColor = parse(context, webkitTextStrokeColor, declaration.webkitTextStrokeColor);
this.webkitTextStrokeWidth = parse(context, webkitTextStrokeWidth, declaration.webkitTextStrokeWidth);

View File

@ -1,7 +1,7 @@
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {CSSValue, isIdentToken} from '../syntax/parser';
import {Context} from '../../core/context';
export enum BACKGROUND_CLIP {
export const enum BACKGROUND_CLIP {
BORDER_BOX = 0,
PADDING_BOX = 1,
CONTENT_BOX = 2

View File

@ -3,7 +3,7 @@ import {CSSValue, isIdentToken, parseFunctionArgs} from '../syntax/parser';
import {Context} from '../../core/context';
export type BackgroundRepeat = BACKGROUND_REPEAT[];
export enum BACKGROUND_REPEAT {
export const enum BACKGROUND_REPEAT {
REPEAT = 0,
NO_REPEAT = 1,
REPEAT_X = 2,

View File

@ -1,6 +1,6 @@
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {Context} from '../../core/context';
export enum BORDER_STYLE {
export const enum BORDER_STYLE {
NONE = 0,
SOLID = 1,
DASHED = 2,

View File

@ -1,6 +1,6 @@
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {Context} from '../../core/context';
export enum FLOAT {
export const enum FLOAT {
NONE = 0,
LEFT = 1,
RIGHT = 2,

View File

@ -1,6 +1,6 @@
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {Context} from '../../core/context';
export enum FONT_STYLE {
export const enum FONT_STYLE {
NORMAL = 'normal',
ITALIC = 'italic',
OBLIQUE = 'oblique'

View File

@ -1,6 +1,6 @@
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {Context} from '../../core/context';
export enum LIST_STYLE_POSITION {
export const enum LIST_STYLE_POSITION {
INSIDE = 0,
OUTSIDE = 1
}

View File

@ -1,6 +1,6 @@
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {Context} from '../../core/context';
export enum LIST_STYLE_TYPE {
export const enum LIST_STYLE_TYPE {
NONE = -1,
DISC = 0,
CIRCLE = 1,

View File

@ -1,11 +1,12 @@
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {CSSValue, isIdentToken} from '../syntax/parser';
import {Context} from '../../core/context';
export enum OVERFLOW {
export const enum OVERFLOW {
VISIBLE = 0,
HIDDEN = 1,
SCROLL = 2,
AUTO = 3
CLIP = 3,
AUTO = 4
}
export const overflow: IPropertyListDescriptor<OVERFLOW[]> = {
@ -20,6 +21,8 @@ export const overflow: IPropertyListDescriptor<OVERFLOW[]> = {
return OVERFLOW.HIDDEN;
case 'scroll':
return OVERFLOW.SCROLL;
case 'clip':
return OVERFLOW.CLIP;
case 'auto':
return OVERFLOW.AUTO;
case 'visible':

View File

@ -1,7 +1,7 @@
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {CSSValue, isIdentToken} from '../syntax/parser';
import {Context} from '../../core/context';
export enum PAINT_ORDER_LAYER {
export const enum PAINT_ORDER_LAYER {
FILL,
STROKE,
MARKERS

View File

@ -1,6 +1,6 @@
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {Context} from '../../core/context';
export enum POSITION {
export const enum POSITION {
STATIC = 0,
RELATIVE = 1,
ABSOLUTE = 2,

View File

@ -1,6 +1,6 @@
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {Context} from '../../core/context';
export enum TEXT_ALIGN {
export const enum TEXT_ALIGN {
LEFT = 0,
CENTER = 1,
RIGHT = 2

View File

@ -1,6 +1,6 @@
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {Context} from '../../core/context';
export enum TEXT_TRANSFORM {
export const enum TEXT_TRANSFORM {
NONE = 0,
LOWERCASE = 1,
UPPERCASE = 2,

View File

@ -1,6 +1,6 @@
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {Context} from '../../core/context';
export enum VISIBILITY {
export const enum VISIBILITY {
VISIBLE = 0,
HIDDEN = 1,
COLLAPSE = 2

View File

@ -10,7 +10,7 @@ import {radialGradient} from './functions/radial-gradient';
import {prefixRadialGradient} from './functions/-prefix-radial-gradient';
import {Context} from '../../core/context';
export enum CSSImageType {
export const enum CSSImageType {
URL,
LINEAR_GRADIENT,
RADIAL_GRADIENT
@ -56,12 +56,12 @@ export interface CSSLinearGradientImage extends ICSSGradientImage {
type: CSSImageType.LINEAR_GRADIENT;
}
export enum CSSRadialShape {
export const enum CSSRadialShape {
CIRCLE,
ELLIPSE
}
export enum CSSRadialExtent {
export const enum CSSRadialExtent {
CLOSEST_SIDE,
FARTHEST_SIDE,
CLOSEST_CORNER,

View File

@ -20,6 +20,7 @@ import {LIST_STYLE_TYPE, listStyleType} from '../css/property-descriptors/list-s
import {CSSParsedCounterDeclaration, CSSParsedPseudoDeclaration} from '../css/index';
import {getQuote} from '../css/property-descriptors/quotes';
import {Context} from '../core/context';
import {DebuggerType, isDebugging} from '../core/debugger';
export interface CloneOptions {
ignoreElements?: (element: Element) => boolean;
@ -136,6 +137,9 @@ export class DocumentCloner {
}
createElementClone<T extends HTMLElement | SVGElement>(node: T): HTMLElement | SVGElement {
if (isDebugging(node, DebuggerType.CLONE)) {
debugger;
}
if (isCanvasElement(node)) {
return this.createCanvasClone(node);
}
@ -190,7 +194,7 @@ export class DocumentCloner {
img.src = canvas.toDataURL();
return img;
} catch (e) {
this.context.logger.info(`Unable to clone canvas contents, canvas is tainted`);
this.context.logger.info(`Unable to inline canvas contents, canvas is tainted`, canvas);
}
}
@ -205,12 +209,23 @@ export class DocumentCloner {
if (!this.options.allowTaint && ctx) {
clonedCtx.putImageData(ctx.getImageData(0, 0, canvas.width, canvas.height), 0, 0);
} else {
const gl = canvas.getContext('webgl2') ?? canvas.getContext('webgl');
if (gl) {
const attribs = gl.getContextAttributes();
if (attribs?.preserveDrawingBuffer === false) {
this.context.logger.warn(
'Unable to clone WebGL context as it has preserveDrawingBuffer=false',
canvas
);
}
}
clonedCtx.drawImage(canvas, 0, 0);
}
}
return clonedCanvas;
} catch (e) {
this.context.logger.info(`Unable to clone canvas as it is tainted`);
this.context.logger.info(`Unable to clone canvas as it is tainted`, canvas);
}
return clonedCanvas;
@ -229,6 +244,7 @@ export class DocumentCloner {
if (window && isElementNode(node) && (isHTMLElementNode(node) || isSVGElementNode(node))) {
const clone = this.createElementClone(node);
clone.style.transitionProperty = 'none';
const style = window.getComputedStyle(node);
const styleBefore = window.getComputedStyle(node, ':before');

View File

@ -3,32 +3,33 @@ import {TextContainer} from './text-container';
import {Bounds, parseBounds} from '../css/layout/bounds';
import {isHTMLElementNode} from './node-parser';
import {Context} from '../core/context';
import {DebuggerType, isDebugging} from '../core/debugger';
export const enum FLAGS {
CREATES_STACKING_CONTEXT = 1 << 1,
CREATES_REAL_STACKING_CONTEXT = 1 << 2,
IS_LIST_OWNER = 1 << 3
IS_LIST_OWNER = 1 << 3,
DEBUG_RENDER = 1 << 4
}
export class ElementContainer {
readonly styles: CSSParsedDeclaration;
readonly textNodes: TextContainer[];
readonly elements: ElementContainer[];
readonly textNodes: TextContainer[] = [];
readonly elements: ElementContainer[] = [];
bounds: Bounds;
flags: number;
flags = 0;
constructor(protected readonly context: Context, element: Element) {
if (isDebugging(element, DebuggerType.PARSE)) {
debugger;
}
this.styles = new CSSParsedDeclaration(context, window.getComputedStyle(element, null));
this.textNodes = [];
this.elements = [];
if (isHTMLElementNode(element)) {
if (this.styles.animationDuration.some((duration) => duration > 0)) {
element.style.animationDuration = '0s';
}
if (this.styles.transitionDuration.some((duration) => duration > 0)) {
element.style.transitionDuration = '0s';
}
if (this.styles.transform !== null) {
// getBoundingClientRect takes transforms into account
@ -37,6 +38,9 @@ export class ElementContainer {
}
this.bounds = parseBounds(this.context, element);
this.flags = 0;
if (isDebugging(element, DebuggerType.RENDER)) {
this.flags |= FLAGS.DEBUG_RENDER;
}
}
}

View File

@ -1,6 +1,6 @@
import {ElementPaint, parseStackingContexts, StackingContext} from '../stacking-context';
import {asString, Color, isTransparent} from '../../css/types/color';
import {ElementContainer} from '../../dom/element-container';
import {ElementContainer, FLAGS} from '../../dom/element-container';
import {BORDER_STYLE} from '../../css/property-descriptors/border-style';
import {CSSParsedDeclaration} from '../../css/index';
import {TextContainer} from '../../dom/text-container';
@ -87,12 +87,12 @@ export class CanvasRenderer extends Renderer {
);
}
applyEffects(effects: IElementEffect[], target: EffectTarget): void {
applyEffects(effects: IElementEffect[]): void {
while (this._activeEffects.length) {
this.popEffect();
}
effects.filter((effect) => contains(effect.target, target)).forEach((effect) => this.applyEffect(effect));
effects.forEach((effect) => this.applyEffect(effect));
}
applyEffect(effect: IElementEffect): void {
@ -135,6 +135,10 @@ export class CanvasRenderer extends Renderer {
}
async renderNode(paint: ElementPaint): Promise<void> {
if (contains(paint.container.flags, FLAGS.DEBUG_RENDER)) {
debugger;
}
if (paint.container.styles.isVisible()) {
await this.renderNodeBackgroundAndBorders(paint);
await this.renderNodeContent(paint);
@ -289,7 +293,7 @@ export class CanvasRenderer extends Renderer {
}
async renderNodeContent(paint: ElementPaint): Promise<void> {
this.applyEffects(paint.effects, EffectTarget.CONTENT);
this.applyEffects(paint.getEffects(EffectTarget.CONTENT));
const container = paint.container;
const curves = paint.curves;
const styles = container.styles;
@ -468,6 +472,9 @@ export class CanvasRenderer extends Renderer {
}
async renderStackContent(stack: StackingContext): Promise<void> {
if (contains(stack.element.container.flags, FLAGS.DEBUG_RENDER)) {
debugger;
}
// https://www.w3.org/TR/css-position-3/#painting-order
// 1. the background and borders of the element forming the stacking context.
await this.renderNodeBackgroundAndBorders(stack.element);
@ -685,7 +692,7 @@ export class CanvasRenderer extends Renderer {
}
async renderNodeBackgroundAndBorders(paint: ElementPaint): Promise<void> {
this.applyEffects(paint.effects, EffectTarget.BACKGROUND_BORDERS);
this.applyEffects(paint.getEffects(EffectTarget.BACKGROUND_BORDERS));
const styles = paint.container.styles;
const hasBackground = !isTransparent(styles.backgroundColor) || styles.backgroundImage.length;
@ -899,7 +906,7 @@ export class CanvasRenderer extends Renderer {
const stack = parseStackingContexts(element);
await this.renderStack(stack);
this.applyEffects([], EffectTarget.BACKGROUND_BORDERS);
this.applyEffects([]);
return this.canvas;
}
}

View File

@ -20,36 +20,21 @@ export interface IElementEffect {
export class TransformEffect implements IElementEffect {
readonly type: EffectType = EffectType.TRANSFORM;
readonly target: number = EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT;
readonly offsetX: number;
readonly offsetY: number;
readonly matrix: Matrix;
constructor(offsetX: number, offsetY: number, matrix: Matrix) {
this.offsetX = offsetX;
this.offsetY = offsetY;
this.matrix = matrix;
}
constructor(readonly offsetX: number, readonly offsetY: number, readonly matrix: Matrix) {}
}
export class ClipEffect implements IElementEffect {
readonly type: EffectType = EffectType.CLIP;
readonly target: number;
readonly path: Path[];
constructor(path: Path[], target: EffectTarget) {
this.target = target;
this.path = path;
}
constructor(readonly path: Path[], readonly target: EffectTarget) {}
}
export class OpacityEffect implements IElementEffect {
readonly type: EffectType = EffectType.OPACITY;
readonly target: number = EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT;
readonly opacity: number;
constructor(opacity: number) {
this.opacity = opacity;
}
constructor(readonly opacity: number) {}
}
export const isTransformEffect = (effect: IElementEffect): effect is TransformEffect =>

View File

@ -27,6 +27,7 @@ export class FontMetrics {
container.style.fontSize = fontSize;
container.style.margin = '0';
container.style.padding = '0';
container.style.whiteSpace = 'nowrap';
body.appendChild(container);

View File

@ -1,6 +1,6 @@
import {BezierCurve} from './bezier-curve';
import {Vector} from './vector';
export enum PathType {
export const enum PathType {
VECTOR = 0,
BEZIER_CURVE = 1
}

View File

@ -1,13 +1,14 @@
import {ElementContainer, FLAGS} from '../dom/element-container';
import {contains} from '../core/bitwise';
import {BoundCurves, calculateBorderBoxPath, calculatePaddingBoxPath} from './bound-curves';
import {ClipEffect, EffectTarget, IElementEffect, OpacityEffect, TransformEffect} from './effects';
import {ClipEffect, EffectTarget, IElementEffect, isClipEffect, OpacityEffect, TransformEffect} from './effects';
import {OVERFLOW} from '../css/property-descriptors/overflow';
import {equalPath} from './path';
import {DISPLAY} from '../css/property-descriptors/display';
import {OLElementContainer} from '../dom/elements/ol-element-container';
import {LIElementContainer} from '../dom/elements/li-element-container';
import {createCounterText} from '../css/types/functions/counter';
import {POSITION} from '../css/property-descriptors/position';
export class StackingContext {
element: ElementPaint;
@ -32,27 +33,24 @@ export class StackingContext {
}
export class ElementPaint {
container: ElementContainer;
effects: IElementEffect[];
curves: BoundCurves;
readonly effects: IElementEffect[] = [];
readonly curves: BoundCurves;
listValue?: string;
constructor(element: ElementContainer, parentStack: IElementEffect[]) {
this.container = element;
this.effects = parentStack.slice(0);
this.curves = new BoundCurves(element);
if (element.styles.opacity < 1) {
this.effects.push(new OpacityEffect(element.styles.opacity));
constructor(readonly container: ElementContainer, readonly parent: ElementPaint | null) {
this.curves = new BoundCurves(this.container);
if (this.container.styles.opacity < 1) {
this.effects.push(new OpacityEffect(this.container.styles.opacity));
}
if (element.styles.transform !== null) {
const offsetX = element.bounds.left + element.styles.transformOrigin[0].number;
const offsetY = element.bounds.top + element.styles.transformOrigin[1].number;
const matrix = element.styles.transform;
if (this.container.styles.transform !== null) {
const offsetX = this.container.bounds.left + this.container.styles.transformOrigin[0].number;
const offsetY = this.container.bounds.top + this.container.styles.transformOrigin[1].number;
const matrix = this.container.styles.transform;
this.effects.push(new TransformEffect(offsetX, offsetY, matrix));
}
if (element.styles.overflowX !== OVERFLOW.VISIBLE) {
if (this.container.styles.overflowX !== OVERFLOW.VISIBLE) {
const borderBox = calculateBorderBoxPath(this.curves);
const paddingBox = calculatePaddingBoxPath(this.curves);
@ -65,16 +63,32 @@ export class ElementPaint {
}
}
getParentEffects(): IElementEffect[] {
getEffects(target: EffectTarget): IElementEffect[] {
let inFlow = [POSITION.ABSOLUTE, POSITION.FIXED].indexOf(this.container.styles.position) === -1;
let parent = this.parent;
const effects = this.effects.slice(0);
if (this.container.styles.overflowX !== OVERFLOW.VISIBLE) {
const borderBox = calculateBorderBoxPath(this.curves);
const paddingBox = calculatePaddingBoxPath(this.curves);
while (parent) {
const croplessEffects = parent.effects.filter((effect) => !isClipEffect(effect));
if (inFlow || parent.container.styles.position !== POSITION.STATIC || !parent.parent) {
effects.unshift(...croplessEffects);
inFlow = [POSITION.ABSOLUTE, POSITION.FIXED].indexOf(parent.container.styles.position) === -1;
if (parent.container.styles.overflowX !== OVERFLOW.VISIBLE) {
const borderBox = calculateBorderBoxPath(parent.curves);
const paddingBox = calculatePaddingBoxPath(parent.curves);
if (!equalPath(borderBox, paddingBox)) {
effects.push(new ClipEffect(paddingBox, EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT));
effects.unshift(
new ClipEffect(paddingBox, EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT)
);
}
}
return effects;
} else {
effects.unshift(...croplessEffects);
}
parent = parent.parent;
}
return effects.filter((effect) => contains(effect.target, target));
}
}
@ -87,7 +101,7 @@ const parseStackTree = (
parent.container.elements.forEach((child) => {
const treatAsRealStackingContext = contains(child.flags, FLAGS.CREATES_REAL_STACKING_CONTEXT);
const createsStackingContext = contains(child.flags, FLAGS.CREATES_STACKING_CONTEXT);
const paintContainer = new ElementPaint(child, parent.getParentEffects());
const paintContainer = new ElementPaint(child, parent);
if (contains(child.styles.display, DISPLAY.LIST_ITEM)) {
listItems.push(paintContainer);
}
@ -182,7 +196,7 @@ const processListItems = (owner: ElementContainer, elements: ElementPaint[]) =>
};
export const parseStackingContexts = (container: ElementContainer): StackingContext => {
const paintContainer = new ElementPaint(container, []);
const paintContainer = new ElementPaint(container, null);
const root = new StackingContext(paintContainer);
const listItems: ElementPaint[] = [];
parseStackTree(paintContainer, root, root, listItems);

View File

@ -55,6 +55,11 @@
transform: rotate(45deg)
}
.transition-delay {
transition: 1ms;
transition-delay: 50ms;
transform: rotate(45deg)
}
div {
float: left;
@ -84,5 +89,9 @@
<div class="transitioned broken">
<p>Hello</p>
</div>
<div class="transition-delay">
<p>Hello</p>
</div>
</body>
</html>

View File

@ -51,6 +51,9 @@
.scroll {
overflow: scroll;
}
.clip {
overflow: clip;
}
.auto {
overflow: auto;
}
@ -70,6 +73,10 @@
scroll
<p class="scroll">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec luctus pretium facilisis. Praesent rutrum eget nisl in tristique. Sed tincidunt nisl et tellus vulputate, nec rhoncus orci pretium.</p>
</div>
<div class="cell">
clip
<p class="clip">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec luctus pretium facilisis. Praesent rutrum eget nisl in tristique. Sed tincidunt nisl et tellus vulputate, nec rhoncus orci pretium.</p>
</div>
<div class="cell">
auto
<p class="auto">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec luctus pretium facilisis. Praesent rutrum eget nisl in tristique. Sed tincidunt nisl et tellus vulputate, nec rhoncus orci pretium.</p>
@ -129,5 +136,17 @@
</script>
</div>
<div class="hidden">Hidden<div style="opacity: 0.5">With opacity</div></div>
<div class="hidden" style="height: 0px; width: 50px;">
<div style="position: absolute; width: 50px; height: 50px; left:400px;">absolute on static parent</div>
</div>
<div class="hidden" style="height: 0px; width: 50px;">
<div style="position: relative; width: 10px; height: 10px; left:400px;">relative on static parent</div>
</div>
<div class="hidden" style="height: 0px; width: 50px;">
<div style="position: fixed; width: 50px; height: 50px; left:400px; top:0;">fixed on static parent</div>
</div>
<div class="hidden" style="height: 0px; width: 50px;position: relative;">
<div style="position: absolute; width: 10px; height: 10px; left:400px;">absolute on relative parent</div>
</div>
</body>
</html>

View File

@ -32,8 +32,10 @@
"license": "MIT",
"main": "n/a",
"scripts": {
"copybuild": "mkdirp public/dist && cpy ../dist/*.js public/dist",
"build": "npm run copybuild && gatsby build",
"copy:build": "mkdirp public/dist && cpy ../dist/*.js public/dist",
"copy:src": "mkdirp public/src && cpy ../src/**/*.ts public/src --parents",
"copy": "npm run copy:build && npm run copy:src",
"build": "npm run copy && gatsby build",
"start": "gatsby develop",
"test": "echo \"Error: no test specified\" && exit 1"
}