Using Regions for Element Placement
Among the various suggestions provided, the concept of rearranging elements as proposed by @WearyAdventurer stands out. However, none of the responses directly address the core inquiry:
How can I design elements in a way that they enclose other elements?
The most effective solution to this query involves employing a data type known as a Region. Regions facilitate executing set operations on portions of 2-D screen space: merging one area with another and then excluding a different area, culminating in a collection of rectangles suitable for rendering. While prevalent in window systems like MS Windows, X Windows, and classic MacOS, regions are notably absent in JavaScript, until I developed a library to fill this void.
The implementation of regions entails intricate logic, navigating through numerous edge cases. My approach, termed Region2D, employs disjoint rows of distinct rectangles (1-dimensional regions), reminiscent of how many X Windows servers operate. Other methodologies incorporate spacial partitioning algorithms or alternate techniques such as pathing in modern MacOS.
Core Concept
Irrespective of the chosen implementation, the fundamental principle remains unchanged. In your scenario, initiate with a large rectangle covering the screen, systematically subtract the desired number of rectangles, and derive the resultant rectangle array from the operations. When utilizing my Region2D library in JavaScript, the process unfolds as follows:
var screenRegion = new Region([0, 0, viewportWidth, viewportHeight]);
var elemRegion1 = new Region(element1);
var elemRegion2 = new Region(element2);
var coverRegion = screenRegion.subtract(elemRegion1).subtract(elemRegion2);
var coverRectangles = coverRegion.getRects();
The resulting array comprises simple objects with coordinates like x
, y
, width
, height
, top
, left
, right
, and
bottom</code. You can easily create <code><div>
elements based on these values.
Implementing the Solution
Here's an operational implementation addressing the initial query, leveraging the Region2D library for detailed computation:
// Generate regions for each of the objects.
var containerRegion = new Region2D($(".container")[0]);
var target1Region = new Region2D($(".target1")[0]);
var target2Region = new Region2D($(".target2")[0]);
// Deduct targets from the container, generating an array of rectangles.
var coverRegion = containerRegion.subtract(target1Region).subtract(target2Region);
var coverRects = coverRegion.getRects();
// Craft gray <div> elements for each rectangle.
for (var i = 0, l = coverRects.length; i < l; i++) {
var coverRect = coverRects[i];
var coverElement = $("<div class='cover'>");
coverElement.css({
left: (coverRect.x - 1) + "px", top: (coverRect.y - 1) + "px",
width: coverRect.width + "px", height: coverRect.height + "px"
});
coverElement.appendTo($(".container"));
}
.container, .target1, .target2, .cover { position: absolute; top: 0; left: 0; box-sizing: border-box; }
.target1, .target2 { border: 1px solid red; }
.container { width: 330px; height: 230px; border: 1px solid blue; }
.target1 { top: 40px; left: 40px; width: 100px; height: 80px; }
.target2 { top: 100px; left: 180px; width: 100px; height: 80px; }
.cover { background: rgba(0, 0, 0, 0.5); border: 1px solid #000; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4b392e2c222425792f0b7a657b657b">[email protected]</a>/plain/region2d.min.js"></script>
<div class="container">
<div class="target1"></div>
<div class="target2"></div>
</div>