I’m looking to get a DIV at the coordinates of an element (that’s simple enough), and this DIV should contain 4 other DIVs that precisely correspond to the 4 sides of the overlapping element. So, imagine I have an element with a blue border, and I want 4 separate DIVs to cover each of the 4 sides in red.
So, in the end, we’ll have an element with a red border.
The challenge here is that this element, as well as its parents, can undergo multiple transformations (like CSS transforms), and thus, the 4 sides must also be positioned according to these transformations, as well as ensuring the correct length (for example, a scale of 2 can modify the size).
So, I’ve tried A LOT of things, without much success. The angle seems right, but the length of the sides and their position are quite random.
I started by retrieving the matrices of the element and each parent.
I attempted several things to try to apply these matrices to each coordinate point (DOMpoint) in the hope of calculating the final length/width.
I’ve provided a fiddle of my best attempt so far (I’ve commented out some parts, or added “Test” to variables that I’ve tested, but without success).
https://jsfiddle.net/kenor33/kjm70qts/35/
Calcul matrix (element and parent) :
function calculateGlobalMatrix (element) {
let matrix = new DOMMatrix();
while (element && element instanceof HTMLElement) {
const computedStyle = window.getComputedStyle(element);
const transform = computedStyle.transform;
const currentMatrix = new DOMMatrix(transform);
matrix = matrix.multiply(currentMatrix);
element = element.parentNode;
}
// matrix = matrix.inverse();
return matrix;
}
Calcul dimensions with matrix
function calculateDimensions (element, matrix) {
const boundingRect = element.getBoundingClientRect();
const width = boundingRect.width;
const height = boundingRect.height;
const corners = [
new DOMPoint(boundingRect.left, boundingRect.top),
new DOMPoint(boundingRect.right, boundingRect.top),
new DOMPoint(boundingRect.right, boundingRect.bottom),
new DOMPoint(boundingRect.left, boundingRect.bottom)
];
const transformedCorners = corners.map(corner => {
const transformedCorner = corner.matrixTransform(matrix);
return {
x: transformedCorner.x,
y: transformedCorner.y
};
});
const minX = Math.min(...transformedCorners.map(c => c.x));
const maxX = Math.max(...transformedCorners.map(c => c.x));
const minY = Math.min(...transformedCorners.map(c => c.y));
const maxY = Math.max(...transformedCorners.map(c => c.y));
return {
width: Math.round(maxX - minX),
height: Math.round(maxY - minY),
left: minX,
top: minY
};
}
Styles for each side
function createFrameStyles(rect, matrix) {
const transformString = `matrix(${matrix.a}, ${matrix.b}, ${matrix.c}, ${matrix.d}, ${matrix.e}, ${matrix.f})`;
return [
{
// top bar
position: 'fixed',
width: `${rect.width}px`,
height: '2px',
backgroundColor: 'red',
transform: `${transformString} translate(0px, 0px)`
},
{
// bottom bar
position: 'fixed',
width: `${rect.width}px`,
height: '2px',
backgroundColor: 'red',
transform: `${transformString} translate(0px, ${rect.height}px)`
},
{
// left bar
position: 'fixed',
width: '2px',
height: `${rect.height}px`,
backgroundColor: 'red',
transform: `${transformString} translate(0px, 0px)`
},
{
// right bar
position: 'fixed',
width: '2px',
height: `${rect.height}px`,
backgroundColor: 'red',
transform: `${transformString} translate(${rect.width}px, 0px)`
}
];
}
And apply
// Matrix and dimensions
const globalMatrix = calculateGlobalMatrix(element);
const dimensions = await calculateDimensions(element, globalMatrix);
const frameStyles = createFrameStyles(dimensions, globalMatrix);
// Global rect (without transform)
const rect = element.getBoundingClientRect();
coord.style.width = `${rect.width}px`;
coord.style.height = `${rect.height}px`;
coord.style.left = `${rect.left}px`;
coord.style.top = `${rect.top}px`;
// container with 4 sides
const container = document.createElement('div');
frameStyles.forEach(style => {
const div = document.createElement('div');
Object.assign(div.style, style);
container.appendChild(div);
});
coord.appendChild(container);
See JSfiddle for example (i don’t find button to add code in stackoverflow)
Thank you so much!