Begin implementing overflow clipping

This commit is contained in:
MoyuScript
2017-08-02 21:35:54 +08:00
parent 8a19c91ceb
commit 32642682af
7 changed files with 158 additions and 27 deletions

View File

@ -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>,

View File

@ -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) {

View File

@ -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();
}

View File

@ -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
View 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;
}
};