Демо: http://jlandowski.greenriverdev.com/virus-theory/vertices.html

Это будет простое руководство по созданию рендеринга графа с помощью Two.js, где вы щелкаете мышью, чтобы создать вершины, а ребра случайным образом соединяют их. Для начала вам нужно получить копию библиотеки Two.js. Вы можете найти его здесь: https://two.js.org/#download

Для начала создайте папку проекта и внутри файла HTML, затем папку javascript с вашим файлом javascript в ней или любым другим способом, которым вы хотите ее структурировать. Именно так я построил свой. Вот так будет выглядеть мой HTML-файл.

Затем откройте файл javascript и начните с этого фрагмента кода (обратите внимание, что я буду использовать ключевые слова let и const вместо var). .

Я начинаю с моего экземпляра библиотеки Two.js под названием two, который принимает объект параметров, здесь я говорю использовать холст для рендеринга графики и сделать его полноэкранным. Альтернативы: Two.Types.webgl и Two.Types.svg. Я просто придерживаюсь холста, но каждый из них должен работать нормально.

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

Далее назначаем обработчик щелчка для создания вершин на холсте:

Для начала я устанавливаю некоторые атрибуты размера вершины, а затем создаю vertexGroup, которая содержит все визуализированные формы. Это работает как слой в фотошопе, где группы накладываются друг на друга, и каждая форма в группе будет держаться вместе. Я использую его здесь в качестве структуры данных для хранения вершин, а затем для наложения группы вершин так, чтобы они рисовались поверх ребер.

Сама функция по щелчку, прикрепленная к элементу холста, принимает положение x и y щелчка мыши, затем создает объект Circle в этой позиции, устанавливает его цвет и контур, а затем добавляет его в vertexGroup . Наконец, вызов two.play () запускает цикл рендеринга, который будет постоянно перерисовывать холст со скоростью 60 кадров в секунду.

Щелчки теперь должны образовывать маленькие кружочки:

Следующими будут две группы, которые я использую для удержания ребер, размещенные под vertexGroup в строке 23:

edgeRenderingGroup будет использоваться для удержания ребер, анимация которых в данный момент выполняется, а edgeGroup будет удерживать ребра, анимация которых завершена. Затем добавьте этот фрагмент (выделен) к функции при нажатии (убедитесь, что он ПЕРЕД vertexGroup.add (vertex)):

Это вызовет функцию для создания случайного ребра, если существует 2 или более вершин, теперь для функции createEdge и ее вспомогательной функции rand ():

Сначала создайте эту вспомогательную функцию rand (), которая будет возвращать случайное целое число между заданными минимальным и максимальным значениями включительно. Пример: rand (1, 5) = {1, 2, 3, 4, 5} возможно. Функция createEdge принимает положение x, y, из которого создается ребро, и массив вершин, хранящийся в vertexGroup (объект Two.js Group имеет внутренний массив, в котором хранится формы в этой группе, здесь я назвал это вершинами).

Затем функция выбирает случайное количество ребер в зависимости от количества существующих вершин, затем мы создаем ребро столько раз. При создании ребра мы захватываем случайную существующую вершину и создаем объект Line с помощью two.makeLine (). Это берет начальную точку (x, y) и конечную точку (x, y). Затем мы стилизуем линию и назначаем нашему объекту линии два настраиваемых свойства, finalX и finalY, которые мы будем использовать, чтобы определить, где отрисовывать линию позже. Затем добавьте его в группу краев рендеринга.

В следующей части нам понадобится еще одна вспомогательная функция, чтобы абстрагироваться от некоторых странностей, присущих объекту Two.js Line:

Это потребует некоторого объяснения, если вы хотите пропустить это, неважно, если вы просто будете следовать коду.

При создании объекта формы в Two.js вы получаете объект, центральное положение которого представлено в его свойстве translation (например, shape.translation.x). Когда вы создаете объект линии, конструктор принимает (startX, startY, endX, endY), а затем выдает объект Line с центральным положением line.translation.x и line.translation .y. Это центральное положение автоматически рассчитывается как среднее из заданных вами начального и конечного положений. Наслаждайтесь моей потрясающей картинкой, чтобы продемонстрировать это.

Проблема в том, что для рендеринга моей линии я хочу перемещать конечную позицию линии на каждом этапе цикла рендеринга, чтобы создать иллюзию расширения для соединения с другой вершиной. Однако, чтобы найти исходное начальное и конечное положение, которое я указал для линии, мне нужно посмотреть на свойство vertices (line.vertices [0] = start pos, line.vertices [1] = конечная позиция). У каждой из этих двух вершин есть x и y, но они не являются исходными положениями, которые я ввел, вместо этого они относятся к этому новому центральному положению. Это фактические свойства Two.js, а не «вершины», которые я назвал, потому что я использовал терминологию графов, постарайтесь не путать их. 😄

Короче говоря, суть функции состоит в том, чтобы добавить центральную позицию к смещению начальной или конечной точки, чтобы получить РЕАЛЬНОЕ начальное или конечное положение линии, таким образом я могу увеличить конечную точку и до сих пор знаю где это.

Теперь последняя часть, собственно движок этого цикла обновления:

Это привязывает нашу пользовательскую функцию к событию Update, которое будет запускаться на каждом шаге цикла, примерно 60 раз в секунду. Здесь мы будем анимировать наши новые линии края. Сначала мы берем массив всех строк, которые в настоящее время находятся в группе рендеринга краев. Если есть какие-то ребра для анимации, примените к каждому из них функцию карты. Это метод для массивов Javascript, который будет запускать заданную функцию для каждого элемента массива. Поместите этот код над two.play ().

Вот оставшийся код, который входит в функцию Map и применяется к каждому краю. Здесь я использую эту функцию linePosition (), чтобы получить фактическую позицию конца строки (false для конечной позиции, true для начальной позиции), затем я беру конечные x и y, которые являются просто центр случайной вершины, к которой идет линия ребра (мы установили это в объекте ребра, когда мы впервые создали его).

Я вызываю эту настраиваемую функцию, которая проверяет, достиг ли конец линии круга вершины, к которой она растет, и если да, я удаляю край из группы рендеринга краев, чтобы он прекратил анимацию, и помещаю его в обычную группу краев, Я также очищаю те 2 свойства finalX, finalY, которые мне больше не нужны.

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

Вот та последняя вспомогательная функция, которая помогает абстрагироваться от уродливого кода, необходимого для проверки, не столкнулась ли линия с нашей вершиной:

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

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

Есть одна небольшая проблема: рёбра визуализируются поверх вершин, а нам нужно обратное, это связано с порядком, в котором мы создали группы Two.js. Поэтому вам просто нужно исправить их порядок в коде:

Теперь, когда vertexGroup создается ПОСЛЕ edgeGroup, а наши круги вершин помещаются внутри vertexGroup, наши вершины теперь будут отображаться поверх всего остального.

Готовый продукт: