Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ab8957a

Browse files
sainthkhflotwig
authored andcommitted
Fixed backface visibility. (cypress-io#5591)
* Fixed backface visibility. * Show reason. * Reorganized logic.
1 parent 6c39d2e commit ab8957a

File tree

2 files changed

+89
-2
lines changed

2 files changed

+89
-2
lines changed

packages/driver/src/dom/visibility.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ const isHidden = (el, name = 'isHidden()') => {
6161
return true // is hidden
6262
}
6363

64+
if (elIsBackface($el)) {
65+
return true
66+
}
67+
6468
// we do some calculations taking into account the parents
6569
// to see if its hidden by a parent
6670
if (elIsHiddenByAncestors($el)) {
@@ -112,6 +116,40 @@ const elHasVisibilityHidden = ($el) => {
112116
return $el.css('visibility') === 'hidden'
113117
}
114118

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+
115153
const elHasVisibilityCollapse = ($el) => {
116154
return $el.css('visibility') === 'collapse'
117155
}
@@ -272,6 +310,10 @@ const elIsHiddenByAncestors = function ($el, $origEl = $el) {
272310
return !elDescendentsHavePositionFixedOrAbsolute($parent, $origEl)
273311
}
274312

313+
if (elIsBackface($parent)) {
314+
return true
315+
}
316+
275317
// continue to recursively walk up the chain until we reach body or html
276318
return elIsHiddenByAncestors($parent, $origEl)
277319
}
@@ -388,6 +430,10 @@ const getReasonIsHidden = function ($el) {
388430
return `This element '${node}' is not visible because it has an effective width and height of: '${width} x ${height}' pixels.`
389431
}
390432

433+
if (elIsBackface($el)) {
434+
return `This element '${node}' is not visible because it is rotated and its backface is hidden.`
435+
}
436+
391437
if ($parent = parentHasNoOffsetWidthOrHeightAndOverflowHidden($el.parent())) {
392438
parentNode = $elements.stringify($parent, 'short')
393439
width = elOffsetWidth($parent)

packages/driver/test/cypress/integration/dom/visibility_spec.js

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -820,12 +820,53 @@ describe('src/cypress/dom/visibility', () => {
820820
})
821821

822822
describe('css backface-visibility', () => {
823+
describe('element visibility by backface-visibility and rotation', () => {
824+
const add = (el) => {
825+
return $(el).appendTo(cy.$$('body'))
826+
}
827+
828+
it('is visible when there is no transform', () => {
829+
const el = add('<div>No transform</div>')
830+
831+
expect(el).to.be.visible
832+
})
833+
834+
it('is visible when an element is rotated < 90 degrees', () => {
835+
const el = add('<div style="backface-visibility: hidden; transform: rotateX(45deg)">rotateX(45deg)</div>')
836+
837+
expect(el).to.be.visible
838+
839+
const el2 = add('<div style="backface-visibility: hidden; transform: rotateY(-45deg)">rotateY(-45deg)</div>')
840+
841+
expect(el2).to.be.visible
842+
})
843+
844+
it('is invisible when an element is rotated > 90 degrees', () => {
845+
const el = add('<div style="backface-visibility: hidden; transform: rotateX(135deg)">rotateX(135deg)</div>')
846+
847+
expect(el).to.be.hidden
848+
849+
const el2 = add('<div style="backface-visibility: hidden; transform: rotateY(-135deg)">rotateY(-135deg)</div>')
850+
851+
expect(el2).to.be.hidden
852+
})
853+
854+
it('is invisible when an element is rotated in 90 degrees', () => {
855+
const el = add('<div style="backface-visibility: hidden; transform: rotateX(90deg)">rotateX(90deg)</div>')
856+
857+
expect(el).to.be.hidden
858+
859+
const el2 = add('<div style="backface-visibility: hidden; transform: rotateY(-90deg)">rotateY(-90deg)</div>')
860+
861+
expect(el2).to.be.hidden
862+
})
863+
})
864+
823865
it('is visible when backface not visible', function () {
824866
expect(this.$parentsWithBackfaceVisibilityHidden.find('#front')).to.be.visible
825867
})
826868

827-
// TODO: why is this skipped?
828-
it.skip('is hidden when backface visible', function () {
869+
it('is hidden when backface visible', function () {
829870
expect(this.$parentsWithBackfaceVisibilityHidden.find('#back')).to.be.hidden
830871
})
831872
})

0 commit comments

Comments
 (0)