From f2b8c16c2c07cdbc9f2684627c3c887ae6e0b821 Mon Sep 17 00:00:00 2001 From: Niklas von Hertzen Date: Thu, 3 Aug 2017 20:28:39 +0800 Subject: [PATCH] Implement visibility css prop --- src/CanvasRenderer.js | 130 +++++++++++++++++++----------------- src/NodeContainer.js | 10 ++- src/parsing/visibility.js | 22 ++++++ tests/cases/visibility.html | 44 ++++++------ 4 files changed, 123 insertions(+), 83 deletions(-) create mode 100644 src/parsing/visibility.js diff --git a/src/CanvasRenderer.js b/src/CanvasRenderer.js index b77f8cf..3f61318 100644 --- a/src/CanvasRenderer.js +++ b/src/CanvasRenderer.js @@ -48,8 +48,10 @@ export default class CanvasRenderer { } renderNode(container: NodeContainer) { - this.renderNodeBackgroundAndBorders(container); - this.renderNodeContent(container); + if (container.isVisible()) { + this.renderNodeBackgroundAndBorders(container); + this.renderNodeContent(container); + } } renderNodeContent(container: NodeContainer) { @@ -242,69 +244,71 @@ export default class CanvasRenderer { } renderStack(stack: StackingContext) { - this.ctx.globalAlpha = stack.getOpacity(); - const transform = stack.container.style.transform; - if (transform !== null) { - this.ctx.save(); - this.ctx.translate( - stack.container.bounds.left + transform.transformOrigin[0].value, - stack.container.bounds.top + transform.transformOrigin[1].value - ); - this.ctx.transform( - transform.transform[0], - transform.transform[1], - transform.transform[2], - transform.transform[3], - transform.transform[4], - transform.transform[5] - ); - this.ctx.translate( - -(stack.container.bounds.left + transform.transformOrigin[0].value), - -(stack.container.bounds.top + transform.transformOrigin[1].value) - ); - } - const [ - negativeZIndex, - zeroOrAutoZIndexOrTransformedOrOpacity, - positiveZIndex, - nonPositionedFloats, - nonPositionedInlineLevel - ] = splitStackingContexts(stack); - const [inlineLevel, nonInlineLevel] = splitDescendants(stack); + if (stack.container.isVisible()) { + this.ctx.globalAlpha = stack.getOpacity(); + const transform = stack.container.style.transform; + if (transform !== null) { + this.ctx.save(); + this.ctx.translate( + stack.container.bounds.left + transform.transformOrigin[0].value, + stack.container.bounds.top + transform.transformOrigin[1].value + ); + this.ctx.transform( + transform.transform[0], + transform.transform[1], + transform.transform[2], + transform.transform[3], + transform.transform[4], + transform.transform[5] + ); + this.ctx.translate( + -(stack.container.bounds.left + transform.transformOrigin[0].value), + -(stack.container.bounds.top + transform.transformOrigin[1].value) + ); + } + const [ + negativeZIndex, + zeroOrAutoZIndexOrTransformedOrOpacity, + positiveZIndex, + nonPositionedFloats, + nonPositionedInlineLevel + ] = splitStackingContexts(stack); + const [inlineLevel, nonInlineLevel] = splitDescendants(stack); - // https://www.w3.org/TR/css-position-3/#painting-order - // 1. the background and borders of the element forming the stacking context. - this.renderNodeBackgroundAndBorders(stack.container); - // 2. the child stacking contexts with negative stack levels (most negative first). - negativeZIndex.sort(sortByZIndex).forEach(this.renderStack, this); - // 3. For all its in-flow, non-positioned, block-level descendants in tree order: - this.renderNodeContent(stack.container); - nonInlineLevel.forEach(this.renderNode, this); - // 4. All non-positioned floating descendants, in tree order. For each one of these, - // treat the element as if it created a new stacking context, but any positioned descendants and descendants - // which actually create a new stacking context should be considered part of the parent stacking context, - // not this new one. - nonPositionedFloats.forEach(this.renderStack, this); - // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks. - nonPositionedInlineLevel.forEach(this.renderStack, this); - inlineLevel.forEach(this.renderNode, this); - // 6. All positioned, opacity or transform descendants, in tree order that fall into the following categories: - // All positioned descendants with 'z-index: auto' or 'z-index: 0', in tree order. - // For those with 'z-index: auto', treat the element as if it created a new stacking context, - // but any positioned descendants and descendants which actually create a new stacking context should be - // considered part of the parent stacking context, not this new one. For those with 'z-index: 0', - // treat the stacking context generated atomically. - // - // All opacity descendants with opacity less than 1 - // - // All transform descendants with transform other than none - zeroOrAutoZIndexOrTransformedOrOpacity.forEach(this.renderStack, this); - // 7. Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index - // order (smallest first) then tree order. - positiveZIndex.sort(sortByZIndex).forEach(this.renderStack, this); + // https://www.w3.org/TR/css-position-3/#painting-order + // 1. the background and borders of the element forming the stacking context. + this.renderNodeBackgroundAndBorders(stack.container); + // 2. the child stacking contexts with negative stack levels (most negative first). + negativeZIndex.sort(sortByZIndex).forEach(this.renderStack, this); + // 3. For all its in-flow, non-positioned, block-level descendants in tree order: + this.renderNodeContent(stack.container); + nonInlineLevel.forEach(this.renderNode, this); + // 4. All non-positioned floating descendants, in tree order. For each one of these, + // treat the element as if it created a new stacking context, but any positioned descendants and descendants + // which actually create a new stacking context should be considered part of the parent stacking context, + // not this new one. + nonPositionedFloats.forEach(this.renderStack, this); + // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks. + nonPositionedInlineLevel.forEach(this.renderStack, this); + inlineLevel.forEach(this.renderNode, this); + // 6. All positioned, opacity or transform descendants, in tree order that fall into the following categories: + // All positioned descendants with 'z-index: auto' or 'z-index: 0', in tree order. + // For those with 'z-index: auto', treat the element as if it created a new stacking context, + // but any positioned descendants and descendants which actually create a new stacking context should be + // considered part of the parent stacking context, not this new one. For those with 'z-index: 0', + // treat the stacking context generated atomically. + // + // All opacity descendants with opacity less than 1 + // + // All transform descendants with transform other than none + zeroOrAutoZIndexOrTransformedOrOpacity.forEach(this.renderStack, this); + // 7. Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index + // order (smallest first) then tree order. + positiveZIndex.sort(sortByZIndex).forEach(this.renderStack, this); - if (transform !== null) { - this.ctx.restore(); + if (transform !== null) { + this.ctx.restore(); + } } } diff --git a/src/NodeContainer.js b/src/NodeContainer.js index d0ea94b..62984d6 100644 --- a/src/NodeContainer.js +++ b/src/NodeContainer.js @@ -13,6 +13,7 @@ import type {Position} from './parsing/position'; import type {TextTransform} from './parsing/textTransform'; import type {TextDecoration} from './parsing/textDecoration'; import type {Transform} from './parsing/transform'; +import type {Visibility} from './parsing/visibility'; import type {zIndex} from './parsing/zIndex'; import type {Bounds, BoundCurves, Path} from './Bounds'; @@ -36,6 +37,7 @@ import {parsePosition, POSITION} from './parsing/position'; import {parseTextDecoration} from './parsing/textDecoration'; import {parseTextTransform} from './parsing/textTransform'; import {parseTransform} from './parsing/transform'; +import {parseVisibility, VISIBILITY} from './parsing/visibility'; import {parseZIndex} from './parsing/zIndex'; import {parseBounds, parseBoundCurves, calculatePaddingBoxPath} from './Bounds'; @@ -56,6 +58,7 @@ type StyleDeclaration = { textDecoration: TextDecoration, textTransform: TextTransform, transform: Transform, + visibility: Visibility, zIndex: zIndex }; @@ -90,6 +93,7 @@ export default class NodeContainer { textDecoration: parseTextDecoration(style), textTransform: parseTextTransform(style.textTransform), transform: parseTransform(style), + visibility: parseVisibility(style.visibility), zIndex: parseZIndex(style.zIndex) }; @@ -127,7 +131,11 @@ export default class NodeContainer { return this.isRootElement() && !this.isFloating() && !this.isAbsolutelyPositioned(); } isVisible(): boolean { - return !contains(this.style.display, DISPLAY.NONE) && this.style.opacity > 0; + return ( + !contains(this.style.display, DISPLAY.NONE) && + this.style.opacity > 0 && + this.style.visibility === VISIBILITY.VISIBLE + ); } isAbsolutelyPositioned(): boolean { return this.style.position !== POSITION.STATIC && this.style.position !== POSITION.RELATIVE; diff --git a/src/parsing/visibility.js b/src/parsing/visibility.js new file mode 100644 index 0000000..2362d98 --- /dev/null +++ b/src/parsing/visibility.js @@ -0,0 +1,22 @@ +/* @flow */ +'use strict'; + +export const VISIBILITY = { + VISIBLE: 0, + HIDDEN: 1, + COLLAPSE: 2 +}; + +export type Visibility = $Values; + +export const parseVisibility = (visibility: string): Visibility => { + switch (visibility) { + case 'hidden': + return VISIBILITY.HIDDEN; + case 'collapse': + return VISIBILITY.COLLAPSE; + case 'visible': + default: + return VISIBILITY.VISIBLE; + } +}; diff --git a/tests/cases/visibility.html b/tests/cases/visibility.html index 9c09a63..77dcdb9 100644 --- a/tests/cases/visibility.html +++ b/tests/cases/visibility.html @@ -1,24 +1,30 @@ - - Visible elements tests - - - + + Visible elements tests + + + - - -

Display:none and visible:hidden tests

-
This should be visible
-
display:none, This should be hidden
-
visibility:hidden, This should be hidden
-
-
display:none, This should be hidden
-
visibility:hidden, This should be hidden
+ + +
+

Display:none and visible:hidden tests

+
This should be visible
+
display:none, This should be hidden
+
visibility:hidden, This should be hidden
+
+
display:none, This should be hidden
+
visibility:hidden, This should be hidden
+
- + +