@@ -61,6 +61,10 @@ const isHidden = (el, name = 'isHidden()') => {
61
61
return true // is hidden
62
62
}
63
63
64
+ if ( elIsBackface ( $el ) ) {
65
+ return true
66
+ }
67
+
64
68
// we do some calculations taking into account the parents
65
69
// to see if its hidden by a parent
66
70
if ( elIsHiddenByAncestors ( $el ) ) {
@@ -112,6 +116,40 @@ const elHasVisibilityHidden = ($el) => {
112
116
return $el . css ( 'visibility' ) === 'hidden'
113
117
}
114
118
119
+ const numberRegex = / - ? [ 0 - 9 ] + (?: \. [ 0 - 9 ] + ) ? / g
120
+ // This is a simplified version of backface culling.
121
+ // https://en.wikipedia.org/wiki/Back-face_culling
122
+ //
123
+ // We defined view normal vector, (0, 0, -1), - eye to screen.
124
+ // and default normal vector, (0, 0, 1)
125
+ // When dot product of them are >= 0, item is visible.
126
+ const elIsBackface = ( $el ) => {
127
+ const el = $el [ 0 ]
128
+ const style = getComputedStyle ( el )
129
+ const backface = style . getPropertyValue ( 'backface-visibility' )
130
+ const backfaceInvisible = backface === 'hidden'
131
+ const transform = style . getPropertyValue ( 'transform' )
132
+
133
+ if ( ! backfaceInvisible || ! transform . startsWith ( 'matrix3d' ) ) {
134
+ return false
135
+ }
136
+
137
+ const m3d = transform . substring ( 8 ) . match ( numberRegex )
138
+ const defaultNormal = [ 0 , 0 , - 1 ]
139
+ const elNormal = findNormal ( m3d )
140
+ // Simplified dot product.
141
+ // [0] and [1] are always 0
142
+ const dot = defaultNormal [ 2 ] * elNormal [ 2 ]
143
+
144
+ return dot >= 0
145
+ }
146
+
147
+ const findNormal = ( m ) => {
148
+ const length = Math . sqrt ( + m [ 8 ] * + m [ 8 ] + + m [ 9 ] * + m [ 9 ] + + m [ 10 ] * + m [ 10 ] )
149
+
150
+ return [ + m [ 8 ] / length , + m [ 9 ] / length , + m [ 10 ] / length ]
151
+ }
152
+
115
153
const elHasVisibilityCollapse = ( $el ) => {
116
154
return $el . css ( 'visibility' ) === 'collapse'
117
155
}
@@ -272,6 +310,10 @@ const elIsHiddenByAncestors = function ($el, $origEl = $el) {
272
310
return ! elDescendentsHavePositionFixedOrAbsolute ( $parent , $origEl )
273
311
}
274
312
313
+ if ( elIsBackface ( $parent ) ) {
314
+ return true
315
+ }
316
+
275
317
// continue to recursively walk up the chain until we reach body or html
276
318
return elIsHiddenByAncestors ( $parent , $origEl )
277
319
}
@@ -388,6 +430,10 @@ const getReasonIsHidden = function ($el) {
388
430
return `This element '${ node } ' is not visible because it has an effective width and height of: '${ width } x ${ height } ' pixels.`
389
431
}
390
432
433
+ if ( elIsBackface ( $el ) ) {
434
+ return `This element '${ node } ' is not visible because it is rotated and its backface is hidden.`
435
+ }
436
+
391
437
if ( $parent = parentHasNoOffsetWidthOrHeightAndOverflowHidden ( $el . parent ( ) ) ) {
392
438
parentNode = $elements . stringify ( $parent , 'short' )
393
439
width = elOffsetWidth ( $parent )
0 commit comments