mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
feat: add property parsing and ClipEffect for clip property
This fixes reftests/clip
This commit is contained in:
parent
f02d7bd737
commit
83eec2d24a
@ -30,6 +30,7 @@ import {
|
||||
borderRightWidth,
|
||||
borderTopWidth
|
||||
} from './property-descriptors/border-width';
|
||||
import {clip} from './property-descriptors/clip';
|
||||
import {color} from './property-descriptors/color';
|
||||
import {direction} from './property-descriptors/direction';
|
||||
import {display, DISPLAY} from './property-descriptors/display';
|
||||
@ -107,6 +108,7 @@ export class CSSParsedDeclaration {
|
||||
borderBottomWidth: ReturnType<typeof borderBottomWidth.parse>;
|
||||
borderLeftWidth: ReturnType<typeof borderLeftWidth.parse>;
|
||||
boxShadow: ReturnType<typeof boxShadow.parse>;
|
||||
clip: ReturnType<typeof clip.parse>;
|
||||
color: Color;
|
||||
direction: ReturnType<typeof direction.parse>;
|
||||
display: ReturnType<typeof display.parse>;
|
||||
@ -225,6 +227,12 @@ export class CSSParsedDeclaration {
|
||||
this.webkitTextStrokeWidth = parse(context, webkitTextStrokeWidth, declaration.webkitTextStrokeWidth);
|
||||
this.wordBreak = parse(context, wordBreak, declaration.wordBreak);
|
||||
this.zIndex = parse(context, zIndex, declaration.zIndex);
|
||||
|
||||
if (this.position == POSITION.ABSOLUTE || this.position == POSITION.FIXED) {
|
||||
this.clip = parse(context, clip, declaration.clip);
|
||||
} else {
|
||||
this.clip = null;
|
||||
}
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
|
38
src/css/property-descriptors/clip.ts
Normal file
38
src/css/property-descriptors/clip.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import {IPropertyValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
|
||||
import {CSSValue, isDimensionToken, isIdentWithValue, nonFunctionArgSeparator} from '../syntax/parser';
|
||||
import {Context} from '../../core/context';
|
||||
import {DimensionToken, TokenType} from '../syntax/tokenizer';
|
||||
|
||||
export interface RectClip {
|
||||
top: DimensionToken;
|
||||
right: DimensionToken;
|
||||
bottom: DimensionToken;
|
||||
left: DimensionToken;
|
||||
}
|
||||
|
||||
export const clip: IPropertyValueDescriptor<RectClip | null> = {
|
||||
name: 'clip',
|
||||
initialValue: 'auto',
|
||||
type: PropertyDescriptorParsingType.VALUE,
|
||||
prefix: false,
|
||||
parse: (_context: Context, token: CSSValue) => {
|
||||
if (isIdentWithValue(token, 'auto')) {
|
||||
return null;
|
||||
}
|
||||
if (token.type !== TokenType.FUNCTION || token.name !== 'rect') {
|
||||
throw new Error('Clip value must be auto or a rect function');
|
||||
}
|
||||
|
||||
const rectArgs = token.values.filter(nonFunctionArgSeparator).filter(isDimensionToken);
|
||||
if (rectArgs.length !== 4) {
|
||||
throw new Error('Rect clip must have 4 dimension elements');
|
||||
}
|
||||
|
||||
return {
|
||||
top: rectArgs[0],
|
||||
right: rectArgs[1],
|
||||
bottom: rectArgs[2],
|
||||
left: rectArgs[3]
|
||||
};
|
||||
}
|
||||
};
|
@ -40,15 +40,18 @@ export const getAbsoluteValue = (token: LengthPercentage, parent: number): numbe
|
||||
}
|
||||
|
||||
if (isDimensionToken(token)) {
|
||||
switch (token.unit) {
|
||||
case 'rem':
|
||||
case 'em':
|
||||
return 16 * token.number; // TODO use correct font-size
|
||||
case 'px':
|
||||
default:
|
||||
return token.number;
|
||||
}
|
||||
return getAbsoluteValueForDimension(token);
|
||||
}
|
||||
|
||||
return token.number;
|
||||
};
|
||||
export const getAbsoluteValueForDimension = (token: DimensionToken): number => {
|
||||
switch (token.unit) {
|
||||
case 'rem':
|
||||
case 'em':
|
||||
return 16 * token.number; // TODO use correct font-size
|
||||
case 'px':
|
||||
default:
|
||||
return token.number;
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {ElementContainer} from '../dom/element-container';
|
||||
import {getAbsoluteValue, getAbsoluteValueForTuple} from '../css/types/length-percentage';
|
||||
import {getAbsoluteValue, getAbsoluteValueForTuple, getAbsoluteValueForDimension} from '../css/types/length-percentage';
|
||||
import {Vector} from './vector';
|
||||
import {BezierCurve} from './bezier-curve';
|
||||
import {Path} from './path';
|
||||
@ -21,6 +21,10 @@ export class BoundCurves {
|
||||
readonly topRightBorderBox: Path;
|
||||
readonly bottomRightBorderBox: Path;
|
||||
readonly bottomLeftBorderBox: Path;
|
||||
readonly topLeftClipBox: Path;
|
||||
readonly topRightClipBox: Path;
|
||||
readonly bottomRightClipBox: Path;
|
||||
readonly bottomLeftClipBox: Path;
|
||||
readonly topLeftPaddingBox: Path;
|
||||
readonly topRightPaddingBox: Path;
|
||||
readonly bottomRightPaddingBox: Path;
|
||||
@ -72,6 +76,17 @@ export class BoundCurves {
|
||||
const paddingBottom = getAbsoluteValue(styles.paddingBottom, element.bounds.width);
|
||||
const paddingLeft = getAbsoluteValue(styles.paddingLeft, element.bounds.width);
|
||||
|
||||
let rectClipTop = 0;
|
||||
let rectClipRight = 0;
|
||||
let rectClipBottom = 0;
|
||||
let rectClipLeft = 0;
|
||||
if (styles.clip) {
|
||||
rectClipTop = getAbsoluteValueForDimension(styles.clip.top);
|
||||
rectClipRight = getAbsoluteValueForDimension(styles.clip.right);
|
||||
rectClipBottom = getAbsoluteValueForDimension(styles.clip.bottom);
|
||||
rectClipLeft = getAbsoluteValueForDimension(styles.clip.left);
|
||||
}
|
||||
|
||||
this.topLeftBorderDoubleOuterBox =
|
||||
tlh > 0 || tlv > 0
|
||||
? getCurvePoints(
|
||||
@ -223,6 +238,10 @@ export class BoundCurves {
|
||||
blh > 0 || blv > 0
|
||||
? getCurvePoints(bounds.left, bounds.top + leftHeight, blh, blv, CORNER.BOTTOM_LEFT)
|
||||
: new Vector(bounds.left, bounds.top + bounds.height);
|
||||
this.topLeftClipBox = new Vector(bounds.left + rectClipLeft, bounds.top + rectClipTop);
|
||||
this.topRightClipBox = new Vector(bounds.left + rectClipRight, bounds.top + rectClipTop);
|
||||
this.bottomRightClipBox = new Vector(bounds.left + rectClipRight, bounds.top + rectClipBottom);
|
||||
this.bottomLeftClipBox = new Vector(bounds.left + rectClipLeft, bounds.top + rectClipBottom);
|
||||
this.topLeftPaddingBox =
|
||||
tlh > 0 || tlv > 0
|
||||
? getCurvePoints(
|
||||
@ -369,6 +388,10 @@ export const calculateBorderBoxPath = (curves: BoundCurves): Path[] => {
|
||||
return [curves.topLeftBorderBox, curves.topRightBorderBox, curves.bottomRightBorderBox, curves.bottomLeftBorderBox];
|
||||
};
|
||||
|
||||
export const calculateRectClipPath = (curves: BoundCurves): Path[] => {
|
||||
return [curves.topLeftClipBox, curves.topRightClipBox, curves.bottomRightClipBox, curves.bottomLeftClipBox];
|
||||
};
|
||||
|
||||
export const calculateContentBoxPath = (curves: BoundCurves): Path[] => {
|
||||
return [
|
||||
curves.topLeftContentBox,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {ElementContainer, FLAGS} from '../dom/element-container';
|
||||
import {contains} from '../core/bitwise';
|
||||
import {BoundCurves, calculateBorderBoxPath, calculatePaddingBoxPath} from './bound-curves';
|
||||
import {BoundCurves, calculateBorderBoxPath, calculatePaddingBoxPath, calculateRectClipPath} from './bound-curves';
|
||||
import {ClipEffect, EffectTarget, IElementEffect, isClipEffect, OpacityEffect, TransformEffect} from './effects';
|
||||
import {OVERFLOW} from '../css/property-descriptors/overflow';
|
||||
import {equalPath} from './path';
|
||||
@ -61,6 +61,12 @@ export class ElementPaint {
|
||||
this.effects.push(new ClipEffect(paddingBox, EffectTarget.CONTENT));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.container.styles.clip) {
|
||||
const clipBox = calculateRectClipPath(this.curves);
|
||||
const clipEffect = new ClipEffect(clipBox, EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT);
|
||||
this.effects.push(clipEffect);
|
||||
}
|
||||
}
|
||||
|
||||
getEffects(target: EffectTarget): IElementEffect[] {
|
||||
@ -81,6 +87,11 @@ export class ElementPaint {
|
||||
);
|
||||
}
|
||||
}
|
||||
if (parent.container.styles.clip) {
|
||||
const clipBox = calculateRectClipPath(parent.curves);
|
||||
const clipEffect = new ClipEffect(clipBox, EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT);
|
||||
effects.unshift(clipEffect);
|
||||
}
|
||||
} else {
|
||||
effects.unshift(...croplessEffects);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Inline text in the top element</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
@ -16,7 +16,7 @@
|
||||
border: 5px solid blue;
|
||||
}
|
||||
body {
|
||||
font-family: Arial;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -37,10 +37,13 @@
|
||||
<div style="clip: rect(0px, 400px, 50px, 200px); position: absolute; top: 250px; left: 500px;">Some inline text <span> followed by text in span </span> followed by more inline text.
|
||||
<p>Then a block level element.</p>
|
||||
Then more inline text.</div>
|
||||
</body>
|
||||
|
||||
<div style="clip: rect(0px, 400px, 50px, 200px); position: absolute; top: 500px;">Some inline text <span> followed by text in span </span> followed by more inline text.
|
||||
<p>Then a block level element.</p>
|
||||
Then more inline text.</div>
|
||||
|
||||
<div style="clip: rect(0px, 400px, 50px, 200px); position: absolute; top: 500px; left: 500px; border-radius: 100%; overflow: hidden;">Some inline text <span> followed by text in span </span> followed by more inline text.
|
||||
<p>Then a block level element.</p>
|
||||
Then more inline text.</div>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user