Статьи

Создание полнофункциональной 3D-галереи изображений с помощью React VR

React VR — это библиотека JavaScript от Facebook, которая уменьшает усилия по созданию приложения WebVR . Вы можете сравнить React VR с A-Frame от Mozilla , но вместо написания HTML, с React VR мы используем JavaScript для создания сцены WebVR.

React VR основан на библиотеке WebGL three.js и платформе React Native. Это означает, что мы можем использовать теги JSX, компоненты React Native, например <View> или <Text> , или концепции React Native, например макет flexbox. Чтобы упростить процесс создания сцены WebVR, React VR имеет встроенную поддержку 3D-сеток, источников света, видео, 3D-форм или сферических изображений.

через GIPHY

В этой статье мы хотим использовать React VR для создания средства просмотра сферических изображений. Для этого мы будем использовать четыре равносторонних фотографии, которые я снял на React Conf 2017 с помощью камеры Theta S. Галерея будет иметь четыре кнопки для обмена изображениями, которые будут работать с мышью и / или гарнитурой VR. Вы можете скачать равносторонние изображения, а также графические кнопки здесь . И последнее, но не менее важное: мы рассмотрим, как анимации работают с React VR, добавив простой переход кнопок.

Для разработки мы используем браузер типа Chrome на рабочем столе. Чтобы проверить, работает ли стереоскопический рендеринг для устройств VR, мы используем телефон Samsung с Gear VR. Теоретически, любой мобильный браузер с поддержкой WebVR должен отображать наше приложение стереоскопическим способом для использования с GearVR, Google Cardboard или даже Google Daydream. Но библиотека и API все еще находятся в стадии разработки, поэтому поддержка может быть ненадежной. Вот хорошее резюме браузеров, в настоящее время поддерживающих функции WebVR.

Настройка разработки и структура проекта

Начнем с установки инструмента React VR CLI. Затем создайте новый проект React VR со всеми его зависимостями в новой папке с именем GDVR_REACTVR_SITEPOINT_GALLERY :

 npm install -g react-vr-cli react-vr init GDVR_REACTVR_SITEPOINT_GALLERY cd GDVR_REACTVR_SITEPOINT_GALLERY 

Чтобы запустить локальный сервер разработки, мы запустим скрипт npm и перейдем по http://localhost:8081/vr/ в Chrome.

 npm start 

Если вы видите черно-белую комнату с лестницами, колоннами и надписью «привет», все в порядке.

через GIPHY

Наиболее важные файлы и папки, созданные с помощью React VR CLI:

  • index.vr.js . Это точка входа в приложение. В настоящее время файл содержит весь исходный код стандартной сцены React VR, как мы уже видели в браузере.
  • static_assets . Эта папка должна содержать все ресурсы, используемые в приложении. Мы поместим равносторонние изображения и графические кнопки в эту папку.

Мы хотим, чтобы наш проект состоял из трех компонентов:

  • компонент Canvas , который содержит код для изображений полной сферы
  • Компонент Button , который создает кнопку VR для обмена изображениями
  • компонент пользовательского интерфейса , который создает пользовательский интерфейс из четырех компонентов Button.

Каждый из трех компонентов будет иметь свой собственный файл, поэтому давайте создадим папку components для этих файлов. Затем, прежде чем мы начнем создавать компонент Canvas, давайте удалим пример кода scaffolded из файла index.vr.js чтобы он выглядел так:

 /* index.vr.js */ import React from 'react'; import { AppRegistry, View, } from 'react-vr'; export default class GDVR_REACTVR_SITEPOINT_GALLERY extends React.Component { render() { return ( <View> </View> ); } }; AppRegistry.registerComponent('GDVR_REACTVR_SITEPOINT_GALLERY', () => GDVR_REACTVR_SITEPOINT_GALLERY); 

Добавление сферического изображения в сцену

Чтобы добавить сферическое изображение на сцену, мы создадим новый файл Canvas.js в папке components :

 /* Canvas.js */ import React from 'react'; import { asset, Pano, } from 'react-vr'; class Canvas extends React.Component { constructor(props) { super(props); this.state = { src: this.props.src, } } render() { return ( <Pano source={asset(this.state.src)}/> ); } }; export default Canvas; 

В первых шести строках кода мы импортируем зависимости. Затем мы объявляем наш компонент Canvas и определяем, как он отображается, используя синтаксис JSX.

Если вы хотите узнать больше о JSX, я рекомендую вам ознакомиться с разделом «Начало работы с React и JSX» .

Взгляд на код JSX показывает, что компонент Canvas возвращает только один компонент — компонент React VR <Pano> . У него есть параметр, source asset , который использует функцию asset для загрузки изображения из папки static_assets . Аргумент ссылается на состояние, которое мы инициализировали в функции конструктора.

В нашем случае мы не хотим определять путь в самом компоненте Canvas, а используем файл index.vr.js для определения всех путей к изображениям. Вот почему объект state.src ссылается на объект state.src компонента.

Ознакомьтесь с документацией ReactJS для React.Component, если вы хотите узнать больше о состоянии и свойствах.

Давайте продолжим, изменив файл index.vr.js для использования компонента Canvas и рендеринга его в сцену:

 /* index.vr.js */ import React from 'react'; import { AppRegistry, View, } from 'react-vr'; import Canvas from './components/Canvas'; export default class GDVR_REACTVR_SITEPOINT_GALLERY extends React.Component { constructor() { super(); this.state = { src: 'reactconf_00.jpg', }; } render() { return ( <View> <Canvas src={this.state.src} /> </View> ); } }; AppRegistry.registerComponent('GDVR_REACTVR_SITEPOINT_GALLERY', () => GDVR_REACTVR_SITEPOINT_GALLERY); 

Помимо уже используемых зависимостей React VR нам необходимо импортировать наш пользовательский компонент Canvas. Далее мы объявляем класс приложения в шестой строке:

 /* index.vr.js */ import Canvas from './components/Canvas'; 

Затем мы добавляем компонент <Canvas> в качестве дочернего компонента компонента <View> . Мы используем src в качестве поддержки компонента, потому что мы ссылаемся на него в компоненте Canvas. Взгляд в браузере должен теперь показать панорамное изображение, и мы уже должны иметь возможность взаимодействовать с ним.

через GIPHY

Создайте компонент пользовательского интерфейса для четырех кнопок

Теперь мы хотим создать четыре кнопки, которые пользователь может вызвать, чтобы поменять изображения. Поэтому мы добавим два новых компонента: компонент пользовательского интерфейса и его дочерний компонент, компонент Button. Давайте начнем с компонента Button:

 /* Button.js */ import React from 'react'; import { asset, Image, View, VrButton, } from 'react-vr'; class Button extends React.Component { onButtonClick = () => { this.props.onClick(); } render () { return ( <View style={{ alignItems: 'center', flexDirection: 'row', margin: 0.0125, width: 0.7, }} > <VrButton onClick={this.onButtonClick} > <Image style={{ width: 0.7, height: 0.7, }} source={asset(this.props.src)} > </Image> </VrButton> </View> ); } }; export default Button; 

Для создания кнопки мы используем компонент <VrButton> React VR, который мы импортируем в шестой строке. Кроме того, мы используем компонент изображения для добавления изображений активов к каждой кнопке, поскольку <VrButton> компонент <VrButton> не имеет внешнего вида. Как и раньше, мы используем опору для определения источника изображения. Еще одна функция, которую мы используем дважды в этом компоненте, — это style , чтобы добавить значения макета для каждой кнопки и ее изображения. <VrButton> также использует прослушиватель событий onClick .

Чтобы добавить четыре компонента Button к нашей сцене, мы будем использовать родительский компонент пользовательского интерфейса, который мы добавим в качестве дочернего в index.vr.js позже. Перед написанием компонента пользовательского интерфейса, давайте создадим объект конфигурации, определяющий отношение между равноугольными изображениями, изображениями кнопок и самими кнопками. Для этого мы объявляем константу сразу после операторов импорта в файле index.vr.js :

 /* index.vr.js */ const Config = [ { key: 0, imageSrc: 'reactconf_00.jpg', buttonImageSrc: 'button-00.png', }, { key: 1, imageSrc: 'reactconf_01.jpg', buttonImageSrc: 'button-01.png', }, { key: 2, imageSrc: 'reactconf_02.jpg', buttonImageSrc: 'button-02.png', }, { key: 3, imageSrc: 'reactconf_03.jpg', buttonImageSrc: 'button-03.png', } ]; 

Компонент пользовательского интерфейса будет использовать значения, определенные в конфигурации, для обработки событий взгляда и щелчка:

 /* UI.js */ import React from 'react'; import { View, } from 'react-vr'; import Button from './Button'; class UI extends React.Component { constructor(props) { super(props); this.buttons = this.props.buttonConfig; } render () { const buttons = this.buttons.map((button) => <Button key={button.key} onClick={()=>{ this.props.onClick(button.key); }} src={button.buttonImageSrc} /> ); return ( <View style={{ flexDirection: 'row', flexWrap: 'wrap', transform: [ {rotateX: -12}, {translate: [-1.5, 0, -3]}, ], width: 3, }} > {buttons} </View> ); } }; export default UI; 

Чтобы установить источник изображения, мы используем значения конфигурации, которые мы уже добавили в файл index.vr.js . Мы также используем prop onClick для обработки события click, которое мы также добавим через несколько секунд в файл index.vr.js . Затем мы создаем столько кнопок, сколько определено в объекте конфигурации кнопок, чтобы добавить их позже в код JSX, который будет отображаться на сцене:

 /* UI.js */ const buttons = this.buttons.map((button) => <Button key={button.key} onClick={()=>{ this.props.onClick(button.key); }} src={button.buttonImageSrc} /> ); 

Теперь все, что нам нужно сделать, это добавить компонент пользовательского интерфейса в сцену, определенную в файле index.vr.js . Поэтому мы импортируем компонент пользовательского интерфейса сразу после импорта компонента Canvas:

 /* index.vr.js */ import UI from './components/UI'; 

Далее мы добавляем компонент <Canvas> на сцену:

 /* index.vr.js */ <View> <Canvas src={this.state.src} /> <UI buttonConfig={Config} onClick={(key)=>{ this.setState({src: Config[key].imageSrc}); }} /> </View> 

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

Если вас интересует жизненный цикл компонента React, вы можете прочитать о React.Component в документации по React .

 /* Canvas.js */ componentWillReceiveProps(nextProps) { this.setState({src: nextProps.src}); } 

Теперь проверка в браузере должна быть успешной, и нажатие на изображение кнопки должно изменить сферическое изображение.

через GIPHY

Добавить анимацию для переходов состояния кнопки

Чтобы сделать кнопки более восприимчивыми к пользовательским взаимодействиям, мы хотим добавить некоторые состояния при наведении и переходы между режимом по умолчанию и режимом наведения. Для этого мы будем использовать функции Animated library и Easing , а затем напишем в функции для каждого перехода: animateIn и animateOut :

 /* Button.js */ import React from 'react'; import { Animated, asset, Image, View, VrButton, } from 'react-vr'; const Easing = require('Easing'); class Button extends React.Component { constructor(props) { super(); this.state = { animatedTranslation: new Animated.Value(0), }; } animateIn = () => { Animated.timing( this.state.animatedTranslation, { toValue: 0.125, duration: 100, easing: Easing.in, } ).start(); } animateOut = () => { Animated.timing( this.state.animatedTranslation, { toValue: 0, duration: 100, easing: Easing.in, } ).start(); } onButtonClick = () => { this.props.onClick(); } render () { return ( <Animated.View style={{ alignItems: 'center', flexDirection: 'row', margin: 0.0125, transform: [ {translateZ: this.state.animatedTranslation}, ], width: 0.7, }} > <VrButton onClick={this.onButtonClick} onEnter={this.animateIn} onExit={this.animateOut} > <Image style={{ width: 0.7, height: 0.7, }} source={asset(this.props.src)} > </Image> </VrButton> </Animated.View> ); } }; export default Button; 

После добавления зависимостей мы определяем новое состояние для хранения значения перевода, которое мы хотим анимировать:

 /* Button js */ constructor(props) { super(); this.state = { animatedTranslation: new Animated.Value(0), }; } 

Далее мы определяем две анимации, каждая в отдельной функции, которые описывают анимацию, воспроизводимую, когда курсор входит в кнопку и когда курсор выходит из кнопки:

 /* Button.js */ animateIn = () => { Animated.timing( this.state.animatedTranslation, { toValue: 0.125, duration: 100, easing: Easing.in, } ).start(); } animateOut = () => { Animated.timing( this.state.animatedTranslation, { toValue: 0, duration: 100, easing: Easing.in, } ).start(); } 

Чтобы использовать значение state.animatedTranslation в коде JSX, мы должны сделать компонент <View> анимируемым, добавив <Animated.view> :

 /* Button.js */ <Animated.View style={{ alignItems: 'center', flexDirection: 'row', margin: 0.0125, transform: [ {translateZ: this.state.animatedTranslation}, ], width: 0.7, }} > 

Мы вызовем функцию, когда onButtonEnter прослушиватели событий onButtonEnter и onButtonExit :

 /* Button.js */ <VrButton onClick={this.onButtonClick} onEnter={this.animateIn} onExit={this.animateOut} > 

Тест нашего кода в браузере должен показать переходы между позициями на оси z каждой кнопки:

через GIPHY

Сборка и тестирование приложения

Откройте свое приложение в браузере, который поддерживает WebVR, и перейдите на сервер разработки, используя не http://localhost:8081/vr/index.html , а ваш IP-адрес, например, http://192.168.1.100:8081/vr/index.html . Затем нажмите на кнопку « View in VR , чтобы открыть полноэкранный режим и начать стереоскопический рендеринг.

через GIPHY

Чтобы загрузить ваше приложение на сервер, вы можете запустить скрипт npm run bundle , который создаст новую папку build каталоге vr с скомпилированными файлами. На вашем веб-сервере вы должны иметь следующую структуру каталогов:

 Web Server ├─ static_assets/ │ ├─ index.html ├─ index.bundle.js └─ client.bundle.js 

Дополнительные ресурсы

Это все, что нам нужно было сделать, чтобы создать небольшое приложение WebVR с React VR. Вы можете найти весь код проекта на GitHub .

В React VR есть еще несколько компонентов, которые мы не обсуждали в этом руководстве:

  • Есть компонент Text для рендеринга текста.
  • Для добавления света в сцену можно использовать четыре различных компонента освещения: AmbientLight , DirectionalLight , PointLight и Spotlight .
  • Компонент Sound добавляет пространственный звук к месту в 3D-сцене.
  • Для добавления видео можно использовать компонент Video или компонент VideoPano . Специальный компонент VideoControl добавляет элементы управления для воспроизведения видео и громкости.
  • С помощью компонента « Model мы можем добавить в приложение трехмерные модели в формате obj .
  • Компонент CylindricalPanel можно использовать для выравнивания дочерних элементов по внутренней поверхности цилиндра — например, для выравнивания элементов пользовательского интерфейса.
  • Есть три компонента для создания 3D примитивов: компонент sphere компонент plane компонент box .

Кроме того, React VR все еще находится в стадии разработки, что также является причиной его запуска только в браузере Carmel Developer Preview. Если вы хотите узнать больше о React VR, вот несколько интересных ресурсов:

И если вы хотите глубже изучить WebVR, вам могут подойти эти статьи:

Вы уже работали с React VR? Вы сделали какие-нибудь крутые проекты с этим? Я хотел бы услышать о ваших мнениях и опыте в комментариях!


Если вам понравилась эта статья и вы хотите узнать о React с нуля, ознакомьтесь с нашим курсом React The ES6 Way .


Эта статья была рецензирована Морицем Крегером и Тимом Севериеном . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!