mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Begin implementing overflow clipping
This commit is contained in:
parent
52a815a13f
commit
f278ba4f22
@ -171,6 +171,24 @@ const createPathFromCurves = (
|
||||
return path;
|
||||
};
|
||||
|
||||
export const calculateBorderBoxPath = (curves: BoundCurves): Path => {
|
||||
return [
|
||||
curves.topLeftOuter,
|
||||
curves.topRightOuter,
|
||||
curves.bottomRightOuter,
|
||||
curves.bottomLeftOuter
|
||||
];
|
||||
};
|
||||
|
||||
export const calculatePaddingBoxPath = (curves: BoundCurves): Path => {
|
||||
return [
|
||||
curves.topLeftInner,
|
||||
curves.topRightInner,
|
||||
curves.bottomRightInner,
|
||||
curves.bottomLeftInner
|
||||
];
|
||||
};
|
||||
|
||||
export const parseBoundCurves = (
|
||||
bounds: Bounds,
|
||||
borders: Array<Border>,
|
||||
|
@ -16,7 +16,6 @@ import type {ImageStore} from './ImageLoader';
|
||||
import type StackingContext from './StackingContext';
|
||||
|
||||
import {
|
||||
BACKGROUND_CLIP,
|
||||
BACKGROUND_ORIGIN,
|
||||
calculateBackgroungPaintingArea,
|
||||
calculateBackgroundPosition,
|
||||
@ -25,10 +24,10 @@ import {
|
||||
} from './parsing/background';
|
||||
import {BORDER_STYLE} from './parsing/border';
|
||||
import {
|
||||
parseBoundCurves,
|
||||
parsePathForBorder,
|
||||
calculateContentBox,
|
||||
calculatePaddingBox
|
||||
calculatePaddingBox,
|
||||
calculatePaddingBoxPath
|
||||
} from './Bounds';
|
||||
|
||||
export type RenderOptions = {
|
||||
@ -54,6 +53,15 @@ export default class CanvasRenderer {
|
||||
}
|
||||
|
||||
renderNodeContent(container: NodeContainer) {
|
||||
this.ctx.save();
|
||||
const clipPaths = container.getClipPaths();
|
||||
if (clipPaths.length) {
|
||||
clipPaths.forEach(path => {
|
||||
this.path(path);
|
||||
this.ctx.clip();
|
||||
});
|
||||
}
|
||||
|
||||
if (container.textNodes.length) {
|
||||
this.ctx.fillStyle = container.style.color.toString();
|
||||
this.ctx.font = [
|
||||
@ -92,17 +100,24 @@ export default class CanvasRenderer {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.ctx.restore();
|
||||
}
|
||||
|
||||
renderNodeBackgroundAndBorders(container: NodeContainer) {
|
||||
const curvePoints = parseBoundCurves(
|
||||
container.bounds,
|
||||
container.style.border,
|
||||
container.style.borderRadius
|
||||
);
|
||||
this.ctx.save();
|
||||
if (container.parent) {
|
||||
const clipPaths = container.parent.getClipPaths();
|
||||
if (clipPaths.length) {
|
||||
clipPaths.forEach(path => {
|
||||
this.path(path);
|
||||
this.ctx.clip();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const backgroungPaintingArea = calculateBackgroungPaintingArea(
|
||||
curvePoints,
|
||||
container.curvedBounds,
|
||||
container.style.background.backgroundClip
|
||||
);
|
||||
this.path(backgroungPaintingArea);
|
||||
@ -116,8 +131,9 @@ export default class CanvasRenderer {
|
||||
this.renderBackgroundImage(container);
|
||||
this.ctx.restore();
|
||||
container.style.border.forEach((border, side) => {
|
||||
this.renderBorder(border, side, curvePoints);
|
||||
this.renderBorder(border, side, container.curvedBounds);
|
||||
});
|
||||
this.ctx.restore();
|
||||
}
|
||||
|
||||
renderTextNode(textContainer: TextContainer) {
|
||||
|
@ -7,6 +7,7 @@ import type {BorderRadius} from './parsing/borderRadius';
|
||||
import type {DisplayBit} from './parsing/display';
|
||||
import type {Float} from './parsing/float';
|
||||
import type {Font} from './parsing/font';
|
||||
import type {Overflow} from './parsing/overflow';
|
||||
import type {Padding} from './parsing/padding';
|
||||
import type {Position} from './parsing/position';
|
||||
import type {TextTransform} from './parsing/textTransform';
|
||||
@ -14,7 +15,7 @@ import type {TextDecoration} from './parsing/textDecoration';
|
||||
import type {Transform} from './parsing/transform';
|
||||
import type {zIndex} from './parsing/zIndex';
|
||||
|
||||
import type {Bounds} from './Bounds';
|
||||
import type {Bounds, BoundCurves, Path} from './Bounds';
|
||||
import type ImageLoader from './ImageLoader';
|
||||
|
||||
import type TextContainer from './TextContainer';
|
||||
@ -29,6 +30,7 @@ import {parseDisplay, DISPLAY} from './parsing/display';
|
||||
import {parseCSSFloat, FLOAT} from './parsing/float';
|
||||
import {parseFont} from './parsing/font';
|
||||
import {parseLetterSpacing} from './parsing/letterSpacing';
|
||||
import {parseOverflow, OVERFLOW} from './parsing/overflow';
|
||||
import {parsePadding} from './parsing/padding';
|
||||
import {parsePosition, POSITION} from './parsing/position';
|
||||
import {parseTextDecoration} from './parsing/textDecoration';
|
||||
@ -36,7 +38,7 @@ import {parseTextTransform} from './parsing/textTransform';
|
||||
import {parseTransform} from './parsing/transform';
|
||||
import {parseZIndex} from './parsing/zIndex';
|
||||
|
||||
import {parseBounds} from './Bounds';
|
||||
import {parseBounds, parseBoundCurves, calculatePaddingBoxPath} from './Bounds';
|
||||
|
||||
type StyleDeclaration = {
|
||||
background: Background,
|
||||
@ -48,6 +50,7 @@ type StyleDeclaration = {
|
||||
font: Font,
|
||||
letterSpacing: number,
|
||||
opacity: number,
|
||||
overflow: Overflow,
|
||||
padding: Padding,
|
||||
position: Position,
|
||||
textDecoration: TextDecoration,
|
||||
@ -62,6 +65,7 @@ export default class NodeContainer {
|
||||
style: StyleDeclaration;
|
||||
textNodes: Array<TextContainer>;
|
||||
bounds: Bounds;
|
||||
curvedBounds: BoundCurves;
|
||||
image: ?string;
|
||||
|
||||
constructor(node: HTMLElement, parent: ?NodeContainer, imageLoader: ImageLoader) {
|
||||
@ -80,6 +84,7 @@ export default class NodeContainer {
|
||||
font: parseFont(style),
|
||||
letterSpacing: parseLetterSpacing(style.letterSpacing),
|
||||
opacity: parseFloat(style.opacity),
|
||||
overflow: parseOverflow(style.overflow),
|
||||
padding: parsePadding(style),
|
||||
position: parsePosition(style.position),
|
||||
textDecoration: parseTextDecoration(style),
|
||||
@ -97,12 +102,27 @@ export default class NodeContainer {
|
||||
// $FlowFixMe
|
||||
node.tagName === 'IMG' ? imageLoader.loadImage(node.currentSrc || node.src) : null;
|
||||
this.bounds = parseBounds(node);
|
||||
this.curvedBounds = parseBoundCurves(
|
||||
this.bounds,
|
||||
this.style.border,
|
||||
this.style.borderRadius
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
this.name = `${node.tagName.toLowerCase()}${node.id
|
||||
? `#${node.id}`
|
||||
: ''}${node.className.split(' ').map(s => (s.length ? `.${s}` : '')).join('')}`;
|
||||
}
|
||||
}
|
||||
getClipPaths(): Array<Path> {
|
||||
const parentClips = this.parent ? this.parent.getClipPaths() : [];
|
||||
const isClipped =
|
||||
this.style.overflow === OVERFLOW.HIDDEN || this.style.overflow === OVERFLOW.SCROLL;
|
||||
|
||||
return isClipped
|
||||
? parentClips.concat([calculatePaddingBoxPath(this.curvedBounds)])
|
||||
: parentClips;
|
||||
}
|
||||
isInFlow(): boolean {
|
||||
return this.isRootElement() && !this.isFloating() && !this.isAbsolutelyPositioned();
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import Color from '../Color';
|
||||
import Length from '../Length';
|
||||
import Size from '../Size';
|
||||
import Vector from '../Vector';
|
||||
import {calculateBorderBoxPath, calculatePaddingBoxPath} from '../Bounds';
|
||||
|
||||
export type Background = {
|
||||
backgroundImage: Array<BackgroundImage>,
|
||||
@ -123,20 +124,10 @@ export const calculateBackgroungPaintingArea = (
|
||||
// TODO support CONTENT_BOX
|
||||
switch (clip) {
|
||||
case BACKGROUND_CLIP.BORDER_BOX:
|
||||
return [
|
||||
curves.topLeftOuter,
|
||||
curves.topRightOuter,
|
||||
curves.bottomRightOuter,
|
||||
curves.bottomLeftOuter
|
||||
];
|
||||
return calculateBorderBoxPath(curves);
|
||||
case BACKGROUND_CLIP.PADDING_BOX:
|
||||
default:
|
||||
return [
|
||||
curves.topLeftInner,
|
||||
curves.topRightInner,
|
||||
curves.bottomRightInner,
|
||||
curves.bottomLeftInner
|
||||
];
|
||||
return calculatePaddingBoxPath(curves);
|
||||
}
|
||||
};
|
||||
|
||||
|
25
src/parsing/overflow.js
Normal file
25
src/parsing/overflow.js
Normal file
@ -0,0 +1,25 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
export const OVERFLOW = {
|
||||
VISIBLE: 0,
|
||||
HIDDEN: 1,
|
||||
SCROLL: 2,
|
||||
AUTO: 3
|
||||
};
|
||||
|
||||
export type Overflow = $Values<typeof OVERFLOW>;
|
||||
|
||||
export const parseOverflow = (overflow: string): Overflow => {
|
||||
switch (overflow) {
|
||||
case 'hidden':
|
||||
return OVERFLOW.HIDDEN;
|
||||
case 'scroll':
|
||||
return OVERFLOW.SCROLL;
|
||||
case 'auto':
|
||||
return OVERFLOW.AUTO;
|
||||
case 'visible':
|
||||
default:
|
||||
return OVERFLOW.VISIBLE;
|
||||
}
|
||||
};
|
49
tests/cases/overflow/overflow-transform.html
Normal file
49
tests/cases/overflow/overflow-transform.html
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Overflow tests</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<script type="text/javascript" src="../../test.js"></script>
|
||||
<style>
|
||||
#body-bg{
|
||||
width: 300px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border:2px solid #000;
|
||||
}
|
||||
.img-1{
|
||||
display: inline-block;
|
||||
height: 617px;
|
||||
left: -56.8px;
|
||||
position: absolute;
|
||||
resize: none;
|
||||
top: -412.317px;
|
||||
transform: rotate(3.5455rad);
|
||||
width: 629px;
|
||||
z-index: 2;
|
||||
background: #f0f;
|
||||
}
|
||||
.img-2{
|
||||
display: inline-block;
|
||||
height: 620px;
|
||||
left: -150px;
|
||||
position: absolute;
|
||||
resize: none;
|
||||
top: 205px;
|
||||
transform: rotate(3.5455rad);
|
||||
width: 461px;background: #ff0000;
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="body-bg">
|
||||
<p>Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard de l'imprimerie depuis les années 1500, quand un peintre anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte. Il n'a pas fait que survivre cinq siècles, mais s'est aussi adapté à la bureautique informatique, sans que son contenu n'en soit modifié. Il a été popularisé dans les années 1960 grâce à la vente de feuilles Letraset contenant des passages du Lorem Ipsum, et, plus récemment, par son inclusion dans des applications de mise en page de texte, comme Aldus PageMaker. </p>
|
||||
<div class="img-1"></div>
|
||||
<div class="img-2"></div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<title>Overflow tests</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<script type="text/javascript" src="../test.js"></script>
|
||||
<script type="text/javascript" src="../../test.js"></script>
|
||||
<style>
|
||||
.small{
|
||||
font-size:14px;
|
||||
@ -29,15 +29,28 @@
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Overflow: visible</h1>
|
||||
<div> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
</div>
|
||||
|
||||
<h1>Overflow: hidden</h1>
|
||||
<div style="overflow:hidden;float: right; height: 100px; border-radius: 100%; border-color: transparent;">
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s
|
||||
|
||||
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="../../assets/image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
|
||||
|
||||
<div style="overflow:visible;position:relative;"><u>position:relative within a overflow:hidden element</u><br /> <br />
|
||||
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow:hidden;">
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s
|
||||
|
||||
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="../assets/image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
with the release of <div style="border-width:10px 0 5px 0;background:green;">a</div>Letraset sheets containing Lorem Ipsum passages, <img src="../../assets/image.jpg" /> and more recently with desktop publishing software like <b>Aldus PageMaker</b> including versions of Lorem Ipsum.
|
||||
|
||||
|
||||
<div style="overflow:visible;position:relative;"><u>position:relative within a overflow:hidden element</u><br /> <br />
|
||||
@ -47,6 +60,5 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user