mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Implement input/textarea/select element rendering
This commit is contained in:
parent
adb1f50f00
commit
56b3b6df27
@ -4,11 +4,12 @@
|
|||||||
import type {Border, BorderSide} from './parsing/border';
|
import type {Border, BorderSide} from './parsing/border';
|
||||||
import type {BorderRadius} from './parsing/borderRadius';
|
import type {BorderRadius} from './parsing/borderRadius';
|
||||||
import type {Padding} from './parsing/padding';
|
import type {Padding} from './parsing/padding';
|
||||||
|
import type Circle from './drawing/Circle';
|
||||||
|
|
||||||
import Vector from './Vector';
|
import Vector from './drawing/Vector';
|
||||||
import BezierCurve from './BezierCurve';
|
import BezierCurve from './drawing/BezierCurve';
|
||||||
|
|
||||||
export type Path = Array<Vector | BezierCurve>;
|
export type Path = Array<Vector | BezierCurve> | Circle;
|
||||||
|
|
||||||
const TOP = 0;
|
const TOP = 0;
|
||||||
const RIGHT = 1;
|
const RIGHT = 1;
|
||||||
|
@ -1,19 +1,30 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import type Color from './Color';
|
import type Color from './Color';
|
||||||
import type Size from './Size';
|
import type Size from './drawing/Size';
|
||||||
import type {Path, BoundCurves} from './Bounds';
|
|
||||||
import type {TextBounds} from './TextBounds';
|
|
||||||
import type {BackgroundImage} from './parsing/background';
|
import type {BackgroundImage} from './parsing/background';
|
||||||
import type {Border, BorderSide} from './parsing/border';
|
import type {Border, BorderSide} from './parsing/border';
|
||||||
|
|
||||||
import Vector from './Vector';
|
import type {Path, BoundCurves} from './Bounds';
|
||||||
import BezierCurve from './BezierCurve';
|
|
||||||
|
|
||||||
import type NodeContainer from './NodeContainer';
|
|
||||||
import type TextContainer from './TextContainer';
|
|
||||||
import type {ImageStore, ImageElement} from './ImageLoader';
|
import type {ImageStore, ImageElement} from './ImageLoader';
|
||||||
|
import type NodeContainer from './NodeContainer';
|
||||||
import type StackingContext from './StackingContext';
|
import type StackingContext from './StackingContext';
|
||||||
|
import type {TextBounds} from './TextBounds';
|
||||||
|
|
||||||
|
import BezierCurve from './drawing/BezierCurve';
|
||||||
|
import Circle from './drawing/Circle';
|
||||||
|
import Vector from './drawing/Vector';
|
||||||
|
|
||||||
|
import {
|
||||||
|
parsePathForBorder,
|
||||||
|
calculateContentBox,
|
||||||
|
calculatePaddingBox,
|
||||||
|
calculatePaddingBoxPath
|
||||||
|
} from './Bounds';
|
||||||
|
import {FontMetrics} from './Font';
|
||||||
|
import TextContainer from './TextContainer';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BACKGROUND_ORIGIN,
|
BACKGROUND_ORIGIN,
|
||||||
@ -24,14 +35,6 @@ import {
|
|||||||
} from './parsing/background';
|
} from './parsing/background';
|
||||||
import {BORDER_STYLE} from './parsing/border';
|
import {BORDER_STYLE} from './parsing/border';
|
||||||
import {TEXT_DECORATION_LINE} from './parsing/textDecoration';
|
import {TEXT_DECORATION_LINE} from './parsing/textDecoration';
|
||||||
import {
|
|
||||||
parsePathForBorder,
|
|
||||||
calculateContentBox,
|
|
||||||
calculatePaddingBox,
|
|
||||||
calculatePaddingBoxPath
|
|
||||||
} from './Bounds';
|
|
||||||
|
|
||||||
import {FontMetrics} from './Font';
|
|
||||||
|
|
||||||
export type RenderOptions = {
|
export type RenderOptions = {
|
||||||
scale: number,
|
scale: number,
|
||||||
@ -68,7 +71,7 @@ export default class CanvasRenderer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container.textNodes.length) {
|
if (container.childNodes.length) {
|
||||||
this.ctx.fillStyle = container.style.color.toString();
|
this.ctx.fillStyle = container.style.color.toString();
|
||||||
this.ctx.font = [
|
this.ctx.font = [
|
||||||
container.style.font.fontStyle,
|
container.style.font.fontStyle,
|
||||||
@ -79,7 +82,14 @@ export default class CanvasRenderer {
|
|||||||
]
|
]
|
||||||
.join(' ')
|
.join(' ')
|
||||||
.split(',')[0];
|
.split(',')[0];
|
||||||
container.textNodes.forEach(this.renderTextNode, this);
|
container.childNodes.forEach(child => {
|
||||||
|
if (child instanceof TextContainer) {
|
||||||
|
this.renderTextNode(child);
|
||||||
|
} else {
|
||||||
|
this.path(child);
|
||||||
|
this.ctx.fill();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container.image) {
|
if (container.image) {
|
||||||
@ -267,6 +277,16 @@ export default class CanvasRenderer {
|
|||||||
|
|
||||||
path(path: Path) {
|
path(path: Path) {
|
||||||
this.ctx.beginPath();
|
this.ctx.beginPath();
|
||||||
|
if (path instanceof Circle) {
|
||||||
|
this.ctx.arc(
|
||||||
|
path.x + path.radius,
|
||||||
|
path.y + path.radius,
|
||||||
|
path.radius,
|
||||||
|
0,
|
||||||
|
Math.PI * 2,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
path.forEach((point, index) => {
|
path.forEach((point, index) => {
|
||||||
const start = point instanceof Vector ? point : point.start;
|
const start = point instanceof Vector ? point : point.start;
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
@ -286,6 +306,8 @@ export default class CanvasRenderer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.ctx.closePath();
|
this.ctx.closePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
128
src/Input.js
Normal file
128
src/Input.js
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/* @flow */
|
||||||
|
'use strict';
|
||||||
|
import type NodeContainer from './NodeContainer';
|
||||||
|
import TextContainer from './TextContainer';
|
||||||
|
|
||||||
|
import {BACKGROUND_CLIP, BACKGROUND_ORIGIN} from './parsing/background';
|
||||||
|
import {BORDER_STYLE} from './parsing/border';
|
||||||
|
|
||||||
|
import Circle from './drawing/Circle';
|
||||||
|
import Color from './Color';
|
||||||
|
import Length from './Length';
|
||||||
|
import {Bounds} from './Bounds';
|
||||||
|
import {TextBounds} from './TextBounds';
|
||||||
|
|
||||||
|
export const INPUT_COLOR = new Color([42, 42, 42]);
|
||||||
|
const INPUT_BORDER_COLOR = new Color([165, 165, 165]);
|
||||||
|
const INPUT_BACKGROUND_COLOR = new Color([222, 222, 222]);
|
||||||
|
const INPUT_BORDER = {
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: INPUT_BORDER_COLOR,
|
||||||
|
borderStyle: BORDER_STYLE.SOLID
|
||||||
|
};
|
||||||
|
export const INPUT_BORDERS = [INPUT_BORDER, INPUT_BORDER, INPUT_BORDER, INPUT_BORDER];
|
||||||
|
export const INPUT_BACKGROUND = {
|
||||||
|
backgroundColor: INPUT_BACKGROUND_COLOR,
|
||||||
|
backgroundImage: [],
|
||||||
|
backgroundClip: BACKGROUND_CLIP.PADDING_BOX,
|
||||||
|
backgroundOrigin: BACKGROUND_ORIGIN.PADDING_BOX
|
||||||
|
};
|
||||||
|
|
||||||
|
const RADIO_BORDER_RADIUS = new Length('50%');
|
||||||
|
const RADIO_BORDER_RADIUS_TUPLE = [RADIO_BORDER_RADIUS, RADIO_BORDER_RADIUS];
|
||||||
|
const INPUT_RADIO_BORDER_RADIUS = [
|
||||||
|
RADIO_BORDER_RADIUS_TUPLE,
|
||||||
|
RADIO_BORDER_RADIUS_TUPLE,
|
||||||
|
RADIO_BORDER_RADIUS_TUPLE,
|
||||||
|
RADIO_BORDER_RADIUS_TUPLE
|
||||||
|
];
|
||||||
|
|
||||||
|
const CHECKBOX_BORDER_RADIUS = new Length('3px');
|
||||||
|
const CHECKBOX_BORDER_RADIUS_TUPLE = [CHECKBOX_BORDER_RADIUS, CHECKBOX_BORDER_RADIUS];
|
||||||
|
const INPUT_CHECKBOX_BORDER_RADIUS = [
|
||||||
|
CHECKBOX_BORDER_RADIUS_TUPLE,
|
||||||
|
CHECKBOX_BORDER_RADIUS_TUPLE,
|
||||||
|
CHECKBOX_BORDER_RADIUS_TUPLE,
|
||||||
|
CHECKBOX_BORDER_RADIUS_TUPLE
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getInputBorderRadius = (node: HTMLInputElement) => {
|
||||||
|
return node.type === 'radio' ? INPUT_RADIO_BORDER_RADIUS : INPUT_CHECKBOX_BORDER_RADIUS;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const inlineInputElement = (node: HTMLInputElement, container: NodeContainer): void => {
|
||||||
|
if (node.type === 'radio' || node.type === 'checkbox') {
|
||||||
|
if (node.checked) {
|
||||||
|
const size = Math.min(container.bounds.width, container.bounds.height);
|
||||||
|
// TODO draw checkmark with Path Array<Vector>
|
||||||
|
container.style.font.fontSize = `${size - 3}px`;
|
||||||
|
container.childNodes.push(
|
||||||
|
node.type === 'checkbox'
|
||||||
|
? new TextContainer('\u2714', container, [
|
||||||
|
new TextBounds(
|
||||||
|
'\u2714',
|
||||||
|
new Bounds(
|
||||||
|
container.bounds.left + size / 6,
|
||||||
|
container.bounds.top + size - 1,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
])
|
||||||
|
: new Circle(
|
||||||
|
container.bounds.left + size / 4,
|
||||||
|
container.bounds.top + size / 4,
|
||||||
|
size / 4
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inlineFormElement(getInputValue(node), node, container);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const inlineTextAreaElement = (
|
||||||
|
node: HTMLTextAreaElement,
|
||||||
|
container: NodeContainer
|
||||||
|
): void => {
|
||||||
|
inlineFormElement(node.value, node, container);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const inlineSelectElement = (node: HTMLSelectElement, container: NodeContainer): void => {
|
||||||
|
const option = node.options[node.selectedIndex || 0];
|
||||||
|
inlineFormElement(option ? option.text || '' : '', node, container);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const reformatInputBounds = (bounds: Bounds): Bounds => {
|
||||||
|
if (bounds.width > bounds.height) {
|
||||||
|
bounds.left += (bounds.width - bounds.height) / 2;
|
||||||
|
bounds.width = bounds.height;
|
||||||
|
} else if (bounds.width < bounds.height) {
|
||||||
|
bounds.top += (bounds.height - bounds.width) / 2;
|
||||||
|
bounds.height = bounds.width;
|
||||||
|
}
|
||||||
|
return bounds;
|
||||||
|
};
|
||||||
|
|
||||||
|
const inlineFormElement = (value: string, node: HTMLElement, container: NodeContainer): void => {
|
||||||
|
const body = node.ownerDocument.body;
|
||||||
|
if (value.length > 0 && body) {
|
||||||
|
const wrapper = node.ownerDocument.createElement('html2canvaswrapper');
|
||||||
|
wrapper.style = node.ownerDocument.defaultView.getComputedStyle(node, null).cssText;
|
||||||
|
wrapper.style.position = 'fixed';
|
||||||
|
wrapper.style.left = `${container.bounds.left}px`;
|
||||||
|
wrapper.style.top = `${container.bounds.top}px`;
|
||||||
|
const text = node.ownerDocument.createTextNode(value);
|
||||||
|
wrapper.appendChild(text);
|
||||||
|
body.appendChild(wrapper);
|
||||||
|
container.childNodes.push(TextContainer.fromTextNode(text, container));
|
||||||
|
body.removeChild(wrapper);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getInputValue = (node: HTMLInputElement): string => {
|
||||||
|
const value =
|
||||||
|
node.type === 'password' ? new Array(node.value.length + 1).join('\u2022') : node.value;
|
||||||
|
|
||||||
|
return value.length === 0 ? node.placeholder || '' : value;
|
||||||
|
};
|
@ -41,6 +41,13 @@ import {parseVisibility, VISIBILITY} from './parsing/visibility';
|
|||||||
import {parseZIndex} from './parsing/zIndex';
|
import {parseZIndex} from './parsing/zIndex';
|
||||||
|
|
||||||
import {parseBounds, parseBoundCurves, calculatePaddingBoxPath} from './Bounds';
|
import {parseBounds, parseBoundCurves, calculatePaddingBoxPath} from './Bounds';
|
||||||
|
import {
|
||||||
|
INPUT_BACKGROUND,
|
||||||
|
INPUT_BORDERS,
|
||||||
|
INPUT_COLOR,
|
||||||
|
getInputBorderRadius,
|
||||||
|
reformatInputBounds
|
||||||
|
} from './Input';
|
||||||
|
|
||||||
type StyleDeclaration = {
|
type StyleDeclaration = {
|
||||||
background: Background,
|
background: Background,
|
||||||
@ -66,22 +73,30 @@ export default class NodeContainer {
|
|||||||
name: ?string;
|
name: ?string;
|
||||||
parent: ?NodeContainer;
|
parent: ?NodeContainer;
|
||||||
style: StyleDeclaration;
|
style: StyleDeclaration;
|
||||||
textNodes: Array<TextContainer>;
|
childNodes: Array<TextContainer | Path>;
|
||||||
bounds: Bounds;
|
bounds: Bounds;
|
||||||
curvedBounds: BoundCurves;
|
curvedBounds: BoundCurves;
|
||||||
image: ?string;
|
image: ?string;
|
||||||
|
|
||||||
constructor(node: HTMLElement, parent: ?NodeContainer, imageLoader: ImageLoader) {
|
constructor(node: HTMLElement, parent: ?NodeContainer, imageLoader: ImageLoader) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.textNodes = [];
|
this.childNodes = [];
|
||||||
const style = node.ownerDocument.defaultView.getComputedStyle(node, null);
|
const defaultView = node.ownerDocument.defaultView;
|
||||||
|
const style = defaultView.getComputedStyle(node, null);
|
||||||
const display = parseDisplay(style.display);
|
const display = parseDisplay(style.display);
|
||||||
|
|
||||||
|
const IS_INPUT = node.type === 'radio' || node.type === 'checkbox';
|
||||||
|
|
||||||
this.style = {
|
this.style = {
|
||||||
background: parseBackground(style, imageLoader),
|
background: IS_INPUT ? INPUT_BACKGROUND : parseBackground(style, imageLoader),
|
||||||
border: parseBorder(style),
|
border: IS_INPUT ? INPUT_BORDERS : parseBorder(style),
|
||||||
borderRadius: parseBorderRadius(style),
|
borderRadius:
|
||||||
color: new Color(style.color),
|
(node instanceof defaultView.HTMLInputElement ||
|
||||||
|
node instanceof HTMLInputElement) &&
|
||||||
|
IS_INPUT
|
||||||
|
? getInputBorderRadius(node)
|
||||||
|
: parseBorderRadius(style),
|
||||||
|
color: IS_INPUT ? INPUT_COLOR : new Color(style.color),
|
||||||
display: display,
|
display: display,
|
||||||
float: parseCSSFloat(style.float),
|
float: parseCSSFloat(style.float),
|
||||||
font: parseFont(style),
|
font: parseFont(style),
|
||||||
@ -103,7 +118,7 @@ export default class NodeContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.image = getImage(node, imageLoader);
|
this.image = getImage(node, imageLoader);
|
||||||
this.bounds = parseBounds(node);
|
this.bounds = IS_INPUT ? reformatInputBounds(parseBounds(node)) : parseBounds(node);
|
||||||
this.curvedBounds = parseBoundCurves(
|
this.curvedBounds = parseBoundCurves(
|
||||||
this.bounds,
|
this.bounds,
|
||||||
this.style.border,
|
this.style.border,
|
||||||
|
@ -5,6 +5,7 @@ import type Logger from './Logger';
|
|||||||
import StackingContext from './StackingContext';
|
import StackingContext from './StackingContext';
|
||||||
import NodeContainer from './NodeContainer';
|
import NodeContainer from './NodeContainer';
|
||||||
import TextContainer from './TextContainer';
|
import TextContainer from './TextContainer';
|
||||||
|
import {inlineInputElement, inlineTextAreaElement, inlineSelectElement} from './Input';
|
||||||
|
|
||||||
export const NodeParser = (
|
export const NodeParser = (
|
||||||
node: HTMLElement,
|
node: HTMLElement,
|
||||||
@ -45,17 +46,29 @@ const parseNodeTree = (
|
|||||||
for (let childNode = node.firstChild, nextNode; childNode; childNode = nextNode) {
|
for (let childNode = node.firstChild, nextNode; childNode; childNode = nextNode) {
|
||||||
nextNode = childNode.nextSibling;
|
nextNode = childNode.nextSibling;
|
||||||
const defaultView = childNode.ownerDocument.defaultView;
|
const defaultView = childNode.ownerDocument.defaultView;
|
||||||
if (childNode instanceof defaultView.Text) {
|
if (childNode instanceof defaultView.Text || childNode instanceof Text) {
|
||||||
if (childNode.data.trim().length > 0) {
|
if (childNode.data.trim().length > 0) {
|
||||||
parent.textNodes.push(new TextContainer(childNode, parent));
|
parent.childNodes.push(TextContainer.fromTextNode(childNode, parent));
|
||||||
}
|
}
|
||||||
} else if (childNode instanceof defaultView.HTMLElement) {
|
} else if (
|
||||||
|
childNode instanceof defaultView.HTMLElement ||
|
||||||
|
childNode instanceof HTMLElement
|
||||||
|
) {
|
||||||
if (IGNORED_NODE_NAMES.indexOf(childNode.nodeName) === -1) {
|
if (IGNORED_NODE_NAMES.indexOf(childNode.nodeName) === -1) {
|
||||||
inlinePseudoElement(childNode, PSEUDO_BEFORE);
|
inlinePseudoElement(childNode, PSEUDO_BEFORE);
|
||||||
inlinePseudoElement(childNode, PSEUDO_AFTER);
|
inlinePseudoElement(childNode, PSEUDO_AFTER);
|
||||||
const container = new NodeContainer(childNode, parent, imageLoader);
|
const container = new NodeContainer(childNode, parent, imageLoader);
|
||||||
|
|
||||||
if (container.isVisible()) {
|
if (container.isVisible()) {
|
||||||
|
if (childNode.tagName === 'INPUT') {
|
||||||
|
// $FlowFixMe
|
||||||
|
inlineInputElement(childNode, container);
|
||||||
|
} else if (childNode.tagName === 'TEXTAREA') {
|
||||||
|
// $FlowFixMe
|
||||||
|
inlineTextAreaElement(childNode, container);
|
||||||
|
} else if (childNode.tagName === 'SELECT') {
|
||||||
|
// $FlowFixMe
|
||||||
|
inlineSelectElement(childNode, container);
|
||||||
|
}
|
||||||
const treatAsRealStackingContext = createsRealStackingContext(
|
const treatAsRealStackingContext = createsRealStackingContext(
|
||||||
container,
|
container,
|
||||||
childNode
|
childNode
|
||||||
@ -105,7 +118,7 @@ const inlinePseudoElement = (node: HTMLElement, pseudoElt: ':before' | ':after')
|
|||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
anonymousReplacedElement.src = stripQuotes(image[1]);
|
anonymousReplacedElement.src = stripQuotes(image[1]);
|
||||||
} else {
|
} else {
|
||||||
anonymousReplacedElement.appendChild(node.ownerDocument.createTextNode(content));
|
anonymousReplacedElement.textContent = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
anonymousReplacedElement.style = style.cssText;
|
anonymousReplacedElement.style = style.cssText;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {ucs2} from 'punycode';
|
import {ucs2} from 'punycode';
|
||||||
import type TextContainer from './TextContainer';
|
import type NodeContainer from './NodeContainer';
|
||||||
import {Bounds, parseBounds} from './Bounds';
|
import {Bounds, parseBounds} from './Bounds';
|
||||||
import {TEXT_DECORATION} from './parsing/textDecoration';
|
import {TEXT_DECORATION} from './parsing/textDecoration';
|
||||||
|
|
||||||
@ -24,20 +24,20 @@ export class TextBounds {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parseTextBounds = (textContainer: TextContainer, node: Text): Array<TextBounds> => {
|
export const parseTextBounds = (
|
||||||
const codePoints = ucs2.decode(textContainer.text);
|
value: string,
|
||||||
const letterRendering =
|
parent: NodeContainer,
|
||||||
textContainer.parent.style.letterSpacing !== 0 || hasUnicodeCharacters(textContainer.text);
|
node: Text
|
||||||
|
): Array<TextBounds> => {
|
||||||
|
const codePoints = ucs2.decode(value);
|
||||||
|
const letterRendering = parent.style.letterSpacing !== 0 || hasUnicodeCharacters(value);
|
||||||
const textList = letterRendering ? codePoints.map(encodeCodePoint) : splitWords(codePoints);
|
const textList = letterRendering ? codePoints.map(encodeCodePoint) : splitWords(codePoints);
|
||||||
const length = textList.length;
|
const length = textList.length;
|
||||||
const textBounds = [];
|
const textBounds = [];
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
let text = textList[i];
|
let text = textList[i];
|
||||||
if (
|
if (parent.style.textDecoration !== TEXT_DECORATION.NONE || text.trim().length > 0) {
|
||||||
textContainer.parent.style.textDecoration !== TEXT_DECORATION.NONE ||
|
|
||||||
text.trim().length > 0
|
|
||||||
) {
|
|
||||||
if (FEATURES.SUPPORT_RANGE_BOUNDS) {
|
if (FEATURES.SUPPORT_RANGE_BOUNDS) {
|
||||||
textBounds.push(new TextBounds(text, getRangeBounds(node, offset, text.length)));
|
textBounds.push(new TextBounds(text, getRangeBounds(node, offset, text.length)));
|
||||||
} else {
|
} else {
|
||||||
|
@ -12,10 +12,15 @@ export default class TextContainer {
|
|||||||
parent: NodeContainer;
|
parent: NodeContainer;
|
||||||
bounds: Array<TextBounds>;
|
bounds: Array<TextBounds>;
|
||||||
|
|
||||||
constructor(node: Text, parent: NodeContainer) {
|
constructor(text: string, parent: NodeContainer, bounds: Array<TextBounds>) {
|
||||||
this.text = transform(node.data, parent.style.textTransform);
|
this.text = text;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.bounds = parseTextBounds(this, node);
|
this.bounds = bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromTextNode(node: Text, parent: NodeContainer) {
|
||||||
|
const text = transform(node.data, parent.style.textTransform);
|
||||||
|
return new TextContainer(text, parent, parseTextBounds(text, parent, node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
25
src/drawing/Circle.js
Normal file
25
src/drawing/Circle.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* @flow */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
export default class Circle {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
radius: number;
|
||||||
|
|
||||||
|
constructor(x: number, y: number, radius: number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.radius = radius;
|
||||||
|
if (__DEV__) {
|
||||||
|
if (isNaN(x)) {
|
||||||
|
console.error(`Invalid x value given for Circle`);
|
||||||
|
}
|
||||||
|
if (isNaN(y)) {
|
||||||
|
console.error(`Invalid y value given for Circle`);
|
||||||
|
}
|
||||||
|
if (isNaN(radius)) {
|
||||||
|
console.error(`Invalid radius value given for Circle`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,8 +6,8 @@ import type ImageLoader, {ImageElement} from '../ImageLoader';
|
|||||||
|
|
||||||
import Color from '../Color';
|
import Color from '../Color';
|
||||||
import Length from '../Length';
|
import Length from '../Length';
|
||||||
import Size from '../Size';
|
import Size from '../drawing/Size';
|
||||||
import Vector from '../Vector';
|
import Vector from '../drawing/Vector';
|
||||||
import {calculateBorderBoxPath, calculatePaddingBoxPath} from '../Bounds';
|
import {calculateBorderBoxPath, calculatePaddingBoxPath} from '../Bounds';
|
||||||
|
|
||||||
export type Background = {
|
export type Background = {
|
||||||
|
Loading…
Reference in New Issue
Block a user