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

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

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

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

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

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

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

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

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

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

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


person aborted    schedule 08.03.2017    source источник
comment
Напишите функцию, которая вычитает прямоугольник из другого, создавая взамен список меньших прямоугольников. Теперь примените эту функцию к ограничивающей рамке и первому выделенному прямоугольнику. Затем примените эту функцию к каждому из прямоугольников, возвращенных из предыдущего вызова. Промыть и повторить. См. также en.wikipedia.org/wiki/Constructive_solid_geometry для более оптимальных алгоритмов.   -  person le_m    schedule 24.03.2017
comment
Привет! Я представил рабочее решение, вы можете взглянуть на него?   -  person user7401478    schedule 08.05.2017
comment
На самом деле вам нужен тип данных Region. Области выполняют объединение/пересечение/вычитание на прямоугольных участках двумерного пространства и спроектированы таким образом, что вы можете легко создавать их из прямоугольников и деконструировать из них прямоугольники. Я уже писал код на C для этого раньше, и в общем случае это сложно. Большинство графических интерфейсов изначально включают тип региона (он есть во всех Windows, X и Mac) и используют его для большей части рендеринга, но, похоже, нет реализации JavaScript, которую я могу найти: Удивительно, но пакет NPM не доступен. сделать это.   -  person Sean Werkema    schedule 08.05.2017


Ответы (3)


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

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

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

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

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

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

person user7401478    schedule 05.05.2017
comment
Это прекрасно работает! Просто чтобы быть таким парнем, в зависимости от того, какова ваша минимальная поддерживаемая цель, имейте в виду, что IE 10 и ниже не поддерживает свойство `pointer-events'. - person Jonathan Bowman; 09.05.2017
comment
@JonathanBowman Спасибо за отзыв! Я знаю об этом, и есть специальный взлом IE с участием \9 iirc. OP ничего не сказал о поддержке браузера, поэтому я предполагаю, что это не проблема. На самом деле, ОП ничего не сказал вообще, возможно, он не хочет отдавать награду: D - person user7401478; 09.05.2017
comment
Ха-ха, будем надеяться, что это не так :) Мне нужно проверить этот хак, потому что события-указатели действительно полезны, но я знаю некоторые компании (например, ту, в которой я работаю), все еще есть клиенты на IE 9. беспокоиться о. И да, вы абсолютно правы, он не упомянул о поддержке клиентов, я просто знаю, что иногда вас укусят такие вещи, поэтому я хотел вмешаться. Спасибо за совет по обходному пути, я поищу его! - person Jonathan Bowman; 09.05.2017
comment
Альтернативой может быть базовое наложение-›box-›box overlay. Все коробки находятся поверх оверлея и имеют собственный размер оверлея, точно покрывающий коробку. При выборе это наложение исчезает. Обходит события указателя, которые необходимо пройти - person Laplie Anderson; 11.05.2017
comment
@LaplieAnderson Что-то вроде небольшого наложения для каждой коробки? Также может выполнять свою работу, но не так эффективно, как решение с одним наложением. События указателя поддерживаются во всех основных браузерах, и если вам действительно нужна поддержка устаревших браузеров, для устаревших браузеров есть хак iirc. Однако, если ваши клиенты используют такие браузеры, вы можете подумать о переходе на CSS2. - person user7401478; 12.05.2017

Вы можете решить эту проблему проще, используя тень.

Затем вам нужно только сегментировать экран на 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>    

person vals    schedule 07.05.2017

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

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

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

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

Или их не было, пока я не написал для этого библиотеку.

Лежащая в основе логика регионов несколько сложна. Есть много крайних случаев, и обработка всех сценариев, которые могут возникнуть, может быть сложной задачей. В моей реализации Region2D используются непересекающиеся строки (полосы) непересекающихся прямоугольников (одномерные области), это то же самое, что и многие серверы 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>

person Sean Werkema    schedule 19.06.2017