Твиттер замечательный и богатый информацией зверь. Я хотел объединить некоторые возможности визуализации Three.js и его возможности виртуальной реальности с Socket.IO и Node, чтобы создать симпатичный мир частиц, генерируемых потоком Twitter.
Ранее я уже обсуждал все основы разработки веб-приложений для виртуальной реальности в статье SitePoint. Вывод виртуальной реальности в Интернет с помощью Google Cardboard и Three.js , поэтому, если вы новичок в этой идее, сначала прочтите ее и вернитесь. Это демо использует те же основы.
Демо, которое мы будем создавать, будет смотреть прямую трансляцию в Твиттере по ключевому слову Когда кто-то пишет в Твиттере, наблюдая за потоком, он поднимает «башню» из сияющих частиц, которые представляют, как долго был твит. Это демо, в частности, будет искать упоминания слова «пицца». Почему пиццу спрашиваешь? Я искал термин, который упоминался не так часто, как «бибер», но чаще, чем «гоночные гиены». Короче говоря, лучшие термины — это те, которые достаточно часто встречаются, чтобы они появлялись во время просмотра, но не так часто, чтобы они появлялись со многими сотнями в секунду. Пицца является одним из них.
Демонстрационный код
Если вы заинтересованы в том, чтобы прямо в код и попробовать его, вы можете найти его здесь, на GitHub .
Хотите попробовать это в действии? У меня здесь запущенная версия: VR Twitter World .
Наш серверный код
Начнем с рассмотрения кода нашего Node-сервера. Он будет отображать наш плоский HTML, а также работать как сервер Socket.IO, который будет получать поток данных из Twitter.
Полный сервер относительно короткий и выглядит так:
var express = require('express'), app = express(), server = require('http').createServer(app), port = process.env.PORT || 80, io = require('socket.io')(server), config = require('./config.json'), Twitter = require('node-tweet-stream'), t = new Twitter(config); app.get('/', function(request, response) { response.sendFile(__dirname + '/public/index.html'); }); app.get(/^(.+)$/, function(req, res) { res.sendFile(__dirname + '/public/' + req.params[0]); }); app.use(function(err, req, res, next) { console.error(err.stack); res.status(500).send('Something broke!'); }); server.listen(port, function() { console.log('Listening on ' + port); }); t.track('pizza'); t.on('tweet', function(tweet){ console.log('Roger that. Tweets incoming!'); console.log(tweet); io.emit('tweet', tweet); }); t.on('error', function (err) { console.log('Brace yourself! We are goin doooowwwwwwnnnnnnnn! ', err); });
Наши первые строки настраивали сервер с использованием инфраструктуры Node Express. Это довольно простая настройка, которая включает в себя все наши зависимости и подготавливает переменную app
для нас, чтобы получить доступ к функциональности нашего сервера. port
определяет, на каком порту мы хотим, чтобы наш сервер работал ( process.env.PORT
— это переменная сервера, которую будут определять некоторые настройки хостинга, такие как Heroku).
var express = require('express'), app = express(), server = require('http').createServer(app), port = process.env.PORT || 80,
Затем мы устанавливаем переменную io
, одновременно запуская функциональность нашего сервера Socket.IO, присоединяя ее к серверу Express, который мы установили выше:
io = require('socket.io')(server),
Настройка доступа к Twitter
Переменная config
— это хороший способ сохранить ключи аутентификации Twitter приложения и токены доступа в своем собственном файле. Для просмотра потока Twitter в режиме реального времени мы будем использовать модуль npm, называемый node-tweet-stream, который предоставляет все функции, которые нам понадобятся. Мы присваиваем объект для нашего доступа к Твиттеру и всем связанным функциям переменной t
, передавая нашу конфигурацию JSON, чтобы доказать, что нам разрешен доступ к нему.
config = require('./config.json'), Twitter = require('node-tweet-stream'), t = new Twitter(config),
Если у вас нет ключей Twitter для доступа к API Twitter, не бойтесь! Вам просто нужно зарегистрировать приложение в Twitter. Перейдите на страницу управления приложениями Twitter , войдите в систему с учетными данными Twitter и нажмите «Создать новое приложение» .
Получив приложение, вы можете получить ключи и токены доступа, нажав ссылку «Ключи и токены доступа», которая появится на странице управления вашего приложения. Если вы не можете найти его, он будет по адресу: https://apps.twitter.com/app/0000000/keys
(замените 0000000
на идентификатор вашего приложения).
Затем создайте файл на том же уровне, что и index.html
именем config.json
. В него добавьте следующее со значениями вашего собственного приложения:
{ "consumer_key": "YOURKEY", "consumer_secret": "YOURKEYSECRET", "token": "YOURTOKEN", "token_secret": "YOURTOKENSECRET" }
Другие основы сервера
Далее в нашем файле index.js
мы настроили вызовы к корню нашего сервера для загрузки /public/index.html
:
app.get('/', function(request, response) { response.sendFile(__dirname + '/public/index.html'); });
У нас также есть все остальные статические файлы в public
каталоге на нашем сервере:
app.get(/^(.+)$/, function(req, res) { res.sendFile(__dirname + '/public/' + req.params[0]); });
Если у нас есть ошибка, мы регистрируем эту ошибку в консоли нашего сервера и возвращаем ошибку 500:
app.use(function(err, req, res, next) { console.error(err.stack); res.status(500).send('Something broke!'); });
Следующие строки запускают наш сервер со всеми вышеуказанными настройками.
server.listen(port, function() { console.log('Listening on ' + port); });
Получение нашего живого потока Twitter
Наконец, мы настроили наш сервер на Twitter. Мы используем функцию track()
чтобы указать, какое ключевое слово мы хотели бы отслеживать в постоянно расширяющемся потоке контента Twitter.
t.track('pizza');
Затем мы настраиваем функцию обратного вызова для запуска в любое время, когда модуль node-tweet-stream
обнаруживает твит с этим ключевым словом. Если он его видит, мы регистрируем его в журнале консоли нашего сервера (это необязательно, вы можете удалить его, если хотите), а затем отправлять этот твит как событие Socket.IO всем подключенным клиентам.
t.on('tweet', function(tweet){ console.log('Roger that. Tweets incoming!'); console.log(tweet); io.emit('tweet', tweet); });
Если по какой-либо причине у нас будет ошибка с нашим Twitter API, она будет записана в журналы нашего сервера:
t.on('error', function (err) { console.log('Brace yourself! We are goin doooowwwwwwnnnnnnnn! ', err); });
Все наши серверные зависимости и детали хранятся в package.json
как и во всех приложениях Node. Если вы новичок в Node.js, вы можете прочитать немного о том, что все означает: package.json .
Наш код переднего плана
Наш интерфейсный код начинается с той же настройки, что и в статье « Вывод виртуальной реальности в Интернет» с помощью Google Cardboard и статьи Three.js, — сцена Three.js, которую мы отображаем с помощью стереоскопического эффекта, переводя нашу сцену в вид виртуальной реальности. Чтобы быть кратким и приятным, я не буду рассказывать о битах, которые совпадают с предыдущей демонстрацией из этой статьи. Если вы не уверены в том, что я здесь не объясняю, проверьте информацию в предыдущей статье.
Настройка Socket.IO
Единственный новый JS-файл, который мы добавим по сравнению с нашим предыдущим фундаментом, — это JavaScript-файл Socket.IO. Это простой вкладыш:
<script src="/socket.io/socket.io.js"></script>
Чтобы получить доступ к функциональности из Socket.IO, все, что нам нужно, это назначить эту функциональность переменной io
, как вы увидите чуть ниже в нашем файле index.html
:
socket = io(),
Готовим наши башни
Затем мы устанавливаем переменные для наших «башен» (в основном это наши вертикальные наборы частиц, которые представляют твит). Все наши башни хранятся в объекте tweetTowers
называется tweetTowers
. Это контейнерный объект, который позволяет нам отслеживать все наши башни:
// Towers tweetTowers = new THREE.Object3D(),
particleTexture
и particleMaterial
— наши переменные, которые будут представлять, как будут выглядеть наши частицы:
particleTexture, particleMaterial,
maxTowerCount
— это максимальное количество башен, которые мы хотим видеть на нашей сцене — если оно установлено слишком высоко, мы можем получить запоздалый опыт. Я установил его на 6000, так как максимальное количество частиц составляет около миллиона. Разумное число на мой взгляд!
maxTowerCount = 6000,
range
— насколько велика область вокруг зрителя, где мы хотим разместить эти башни. Башни будут размещены в случайных местах сцены, так что это ограничивает расстояние между ними. Я обнаружил, что это приятнее с ними ближе к пользователю. Если они находятся дальше от пользователя, похоже, их не так много (несмотря на то, что существуют тысячи и тысячи частиц!). Я установил его на 100:
range = 100;
Наша функция инициации
В нашей функции init()
не так уж много нового. Он в основном настраивает нашу VR-камеру и элементы управления, как описано в предыдущей статье. Новые биты в конце.
Мы определили наше изображение particle-new.png
как png с именем particle-new.png
который мы имеем в нашей particle-new.png
папке:
particleTexture = THREE.ImageUtils.loadTexture('textures/particle-new.png');
Мы заканчиваем функцию init()
, добавляя наш контейнер tweetTowers
в нашу сцену. С этим в нашей сцене нам не нужно беспокоиться о добавлении любой из наших башен непосредственно в сцену, мы просто добавляем их непосредственно в наш объект tweetTowers
.
scene.add(tweetTowers);
Реагируя на твиты
Вы помните, что как только наш сервер обнаруживает твиты через Twitter с нашим ключевым словом «пицца», он генерирует событие под названием «твит». Наш клиентский JavaScript теперь будет следить за этим событием и отвечать:
socket.on('tweet', function(tweet) { // Our response });
Код ответа — это вызов функции generateTower()
, которая добавит башню к нашей сцене, представляющую этот твит. Мы передаем ему четыре значения:
generateTower({ color: parseInt('0x'+ tweet.user.profile_background_color), startingCoords: { x: getRandomArbitrary(-1*range, range), y: 0, z: getRandomArbitrary(-1*range, range) }, speed: 5, size: (tweet.text.length / 140) * 100 });
-
color
— это цвет нашей частицы. Мы передаем цвет фона профиля пользователя. Это позволяет нам показывать разные цвета, чтобы представить разных голодных пользователей, пишущих в Твиттере о пицце. -
startingCoords
кордах башня будет расположена. Мы хотим, чтобы они располагались вокруг нас, поэтому мы помещаем их между нашей переменной диапазона выше (это должно быть в диапазоне от -100 до 100) по осям x и z. Если бы мы поместили их случайным образом на y, они начинались бы на разных уровнях выше и ниже от земли, а не выстраивались в линию как здания. Мы определенно не хотим этого, поэтому мы гарантируем, что все они расположены в позицииgetRandomArbitrary()
0.getRandomArbitrary()
— это простой генератор случайных чисел между двумя значениями. -
speed
определяет, как далеко друг от друга оказываются наши частицы (или скорость, с которой башня поднимается, если они движутся вверх). -
size
— это то, сколько частиц будет в нашей башне. Мы усредняем его в процентах, предполагая максимальную длину Твиттера в 140 символов.
Отображение башни
Наша функция generateTower()
сама начинается с определения переменной towerGeometry
. Это объект THREE.Geometry
который будет содержать позиции всех наших частиц в башне. Сохранение всех точек, отслеживаемых в пределах одного объекта Geometry
может помочь сократить время обработки, поскольку Three.js нужно только отслеживать каждый объект башни и его точки, а не диапазон независимых частиц. Позже в коде мы предоставим геометрию объекту THREE.PointCloud
который может интерпретировать эти точки в наших частицах.
function generateTower(options) { var towerGeometry = new THREE.Geometry(); // The rest of our code }
Затем мы устанавливаем JavaScript-объект под названием particleMovements
котором хранится информация о том, где наши частицы будут начинаться и заканчиваться в пределах башни, а также как далеко друг от друга они будут находиться (значения, которые мы передали ранее):
var particleMovements = { start: 0, end: options.size, speed: options.speed };
Переменная currentCoords
отслеживает последнюю позицию частицы в башне. Мы инициализируем его в 0,0,0
. startingCoords
где будет расположена башня, анализируются из вызова функции ранее. Если у нас нет начальных координат из вызова функции, мы инициализируем их так же, как currentCoords
:
var currentCoords = {x: 0, y: 0, z: 0}, startingCoords = options.startingCoords ? options.startingCoords : currentCoords;
Затем мы перебираем размер нашей башни, чтобы создать каждую частицу. Мы устанавливаем текущие координаты для увеличения y на наше значение скорости, умноженное на i
. Наши значения x и z остаются в исходных точках, поскольку мы движемся только вверх.
for (var i = 0; i With those co-ordinates defined for this particle, we attach that particle's position as a vertex in our towerGeometry object: [code language="js"] towerGeometry.vertices.push(new THREE.Vector3(currentCoords.x, currentCoords.y, currentCoords.z));
Это гарантирует, что наше расположение частиц установлено правильно. Далее, мы определяем, как будут выглядеть частицы в этой башне в пределах переменнойicleMaterial. Наши частицы будут помещены в объект THREE.PointCloud
и поэтому для их оформления мы будем использовать материал THREE.PointCloudMaterial
:
particleMaterial = new THREE.PointCloudMaterial({ map: particleTexture, color: options.color, blending: THREE.AdditiveBlending, transparent: true, size: 4 });
-
map
определяет изображение, которое мы будем использовать для частицы, мы передаем фактуруparticleTexture
мы определили ранее. -
color
переходит в цвет, которым мы хотим, чтобы частица была (по умолчанию0xffffff
в Three.js). -
blending
устанавливает, как частицы смешиваются в сцене.THREE.AdditiveBlending
добавляет цвет текстуры к той, что за ней. -
transparent
гарантирует, что может произойти смешивание, так как для работы требуется уровень прозрачности. -
size
— это размер наших частиц.
Наконец, мы определяем облако точек нашей башни в переменной tower
. Мы передаем нашу геометрию, содержащую точки, на которых мы хотим видеть каждую частицу, а также материал, который мы определили для каждой из них выше.
var tower = new THREE.PointCloud(towerGeometry, particleMaterial);
Мы добавляем эту башню в наш tweetTowers
коллекции tweetTowers
и затем проверяем, сколько башен находится в сцене. Если у нас больше башен, чем максимально допустимый, мы скрываем самую старую башню, чтобы уменьшить нагрузку на устройство. Если у вас есть проблемы с производительностью, скорее всего, они будут немного лучше, если вы уменьшите maxTowerCount
!
tweetTowers.add(tower); if (tweetTowers.children.length > maxTowerCount) { tweetTowers.children[tweetTowers.children.length - maxTowerCount].visible = false; }
Запуск нашего кода
Чтобы запустить эту демонстрацию локально, вам понадобится Node и вам нужно будет выполнить обычные команды. Установите все зависимости для проекта:
npm install
Затем запустите это:
node index.js
Чтобы проверить это на своем смартфоне, вам нужно либо убедиться, что ваш смартфон находится в той же локальной сети и найти IP-адрес вашего компьютера, либо использовать службу туннелирования, такую как ngrok (я рассматриваю, как использовать ngrok в статье о доступе к Localhost Откуда угодно )
Вы также можете разместить сервер Node где-нибудь. Я лично использовал Heroku , однако это полностью личное предпочтение.
Как только вы где-нибудь запустите сервер, откройте Chrome для мобильных устройств и посетите его! Наденьте свой Google Cardboard или другую подобную гарнитуру, и вы должны увидеть, что примерно через полминуты это выглядит примерно так:
Вывод
Это должно было дать вам хороший обзор использования Node, Socket.IO и Three.js для создания визуализации виртуальной реальности в 3D веб-API. Сама демоверсия может быть доработана, добавив больше ключевых слов, фильтров, сделав ее более гладкой с большим количеством частиц и так далее. Есть много возможностей! Не стесняйтесь выйти и попробовать сделать свой собственный фантастический опыт из этой демонстрации!
У меня также есть другие демонстрации здесь, в SitePoint, которые используют похожие концепции, но вместо этого приводят их в опыт дополненной реальности. Если вам интересно, Filtering Reality с помощью JavaScript и Google Cardboard исследует взятие камеры со своего смартфона и добавление к ней фильтров, а Augmented Reality в браузере с Awe.js исследует весь путь и расширяет элементы в поле вашего зрения с помощью всегда мощная комбинация Three.js и Awe.js!
Если вы решаете собрать собственную визуализацию виртуальной реальности из демонстрации в этой статье (или комбинировать ее с элементами из упомянутых примеров AR), оставьте примечание в комментариях или свяжитесь со мной в Twitter ( @thatpatrickguy) Я достану гарнитуру и посмотрю!