Javascript math "рисовать" прямоугольники вокруг нескольких других прямоугольников для достижения фонового эффекта

Примечание. Я буду использовать слово overlay взаимозаменяемо с фоном.

В настоящее время я работаю над режимом руководства и выделяю элементы, чтобы они выделялись из более темного полупрозрачного фона.

Итак, в основном у меня есть селектор для элемента, который я не хочу накладывать, а затем окружаю его полупрозрачными темными divs, имитируя наложение с исключенным элементом.

Мое решение отлично подходит для одного выделенного элемента, но когда это число становится 2+, становится сложно рассчитать, как разместить черные div вокруг этих элементов. И дело в следующем: потребности подсветки будут значительно различаться от страницы к странице - это не так, как эти несколько элементов будут статическими.

Итак, чтобы проиллюстрировать мою проблему:

введите описание изображения здесь

Здесь вы можете увидеть различные поля, которые нужно поместить на страницу, чтобы окружить Box2 и Box2 полупрозрачными темными div, в основном имитируя эффект наложения с выделенными элементами. Для одного случая, подобного этому, я могу жестко запрограммировать вычисления, и все будет хорошо, но что, если Box1 был выше, чем Box2? Тогда что, если они больше не пересекаются друг с другом по горизонтали? Что делать, если в Box3 требуется подсветка?

В любом случае я не знаю, сколько темных ящиков нужно разместить и где их разместить.

Есть ли какая-нибудь математическая формула, которая помогла бы мне в этом случае? Использование jQuery также возможно, поскольку оно включено в мой проект.

Как мне подойти к этой проблеме и сделать ее расширяемой (несколько ящиков, разные позиции)?

Ответ 1

Ты слишком задумываешься! То, что вы ищете, может быть достигнуто с помощью определенного HTML и CSS-макета.

Вы должны использовать макет, подобный этому:

.container
    .overlay
    .box
    .box
    .box
    .box

Фокус в том, чтобы использовать полноразмерную, полупрозрачную накладку и приносить элементы, которые необходимо выделить впереди. Очень важно назначить pointer-events: none наложение, чтобы вы могли щелкнуть его!

Сделайте .box значение z-index, скажем, 1 и .overlay, имеет значение z-index 100. Чтобы выделить определенный .box, установите его z-index на 101. Таким образом, он будет выделен.

Сейчас я на мобильный, но я поставил базовую концепцию концепции Codepen. Щелкните по ячейкам, чтобы выделить их, нажмите еще раз, чтобы отменить. Работает с несколькими ящиками!

Ответ 2

Вы можете легко решить эту проблему с помощью тени.

Затем вам нужно всего лишь сегментировать экран в 2 частях

body, html {
   height: 100%; 
 }

body {
    background-image: linear-gradient(45deg, yellow, tomato);
}
.mask {
    overflow: hidden;
    width: 50%; 
    height: 100%; 
    display: inline-block;
    position: relative;
}

#mask1 {
    left: 0px;
}

#mask2 {
    right: 0px;
}

.hole {
    width: 150px; 
    height: 90px;
    position: absolute;
    box-shadow: 0px 0px 0px 2000px rgba(0,0,0,0.5);
}

#mask1 .hole {
    left: 40px;
    top: 40px;
}

#mask2 .hole {
    left: 140px;
    top: 20px;
}
<div class="mask" id="mask1">
    <div class="hole"></div>
</div><div class="mask" id="mask2">
    <div class="hole"></div>
</div>    

Ответ 3

Рассмотрим использование регионов

В то время как здесь есть несколько хороших ответов, и вы можете подумать о перемещении элементов, таких как @WearyAdventurer, ни один из других ответов не отвечает на исходный вопрос:

Как создать элементы таким образом, чтобы они окружали другие элементы?

Ответ на этот вопрос лучше всего решать с использованием типа данных, называемого Region. Регионы позволяют легко выполнять операции набора с использованием фрагментов двумерного экрана: объедините эту область с этой областью, а затем вычтите другую область, а затем превратите результат в набор прямоугольников, которые я могу отобразить. Регионы существуют в большинстве оконных систем (MS Windows, X Windows, классический MacOS и другие) и используются в большинстве внутренних систем браузеров для рендеринга элементов, но они неожиданно отсутствуют в JavaScript.

Или они отсутствовали, пока я не написал библиотеку, чтобы сделать это.

Основная логика регионов несколько сложна. Есть много угловых случаев, и обработка всех сценариев, которые могут возникнуть, может быть сложной задачей. Моя реализация, Region2D, использует непересекающиеся строки (полосы) непересекающихся прямоугольников (1-мерные области), что аналогично тому, как многие X серверы Windows делают это. В других реализациях используются пространственные алгоритмы разбиения, а третьи (например, современные MacOS) используют методы переноса или полигональной рендеринга вместо областей вообще.


Основная идея

Независимо от того, какую реализацию вы используете, основная идея остается прежней. В вашем случае вы начинаете с большого прямоугольника, который покрывает экран, а затем просто вычитают два (или три, или четыре или любые) прямоугольники, которые вы хотите, а затем задаете область, прямоугольники которой являются результатом этих операций. В JavaScript, используя мою библиотеку Region2D, он выглядит следующим образом:

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();

Результирующий массив прямоугольников - это простые объекты, которые имеют координаты x/y/width/height/top/left/right/bottom, поэтому вы просто создаете <div> элементов из каждого, и все готово.


Рабочая реализация

Итак, вот рабочая реализация решения актуальной проблемы, представленной, используя мою библиотеку Region2D для выполнения тяжелого алгоритмического подъема:

// 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]);

// Subtract the targets from the container, and make an array of rectangles from it.
var coverRegion = containerRegion.subtract(target1Region).subtract(target2Region);
var coverRects = coverRegion.getRects();

// Create 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/[email protected]/plain/region2d.min.js"></script>

<div class="container">
    <div class="target1"></div>
    <div class="target2"></div>
</div>