Прочитав это, вы также можете реализовать плагин 360-градусной панорамы.

three.js

Управляемое чтение

Эта статья начинается с основ рисования и подробно рассказывает, как использоватьThree.jsРазработать полнофункциональный панорамный плагин.

Давайте сначала посмотрим на эффект плагина:

если ты правThree.jsуже знакомы, или вы хотите пропустить основную теорию, то вы можете перейти прямо изПанорамный просмотрначать искать.

этого проектаgithubадрес:GitHub.com/con AR DL i/голосование…

1. Прояснить отношения

1.1 OpenGL

OpenGLдля рендеринга2D、3DМежъязыковой, кроссплатформенный интерфейс прикладного программирования для количественной графики.(API).

Этот интерфейс350состоит из нескольких различных вызовов функций, используемых для рисования сложных трехмерных сцен из простых графических элементов.

OpenGL ESдаOpenGL3D графикаAPIподмножество , ориентированное на мобильные телефоны,PDAи встроенные устройства, такие как игровые приставки.

на основеOpenGLобычно используетсяCилиCppРазработка, не очень дружелюбная к фронтенд-разработчикам.

1.2 WebGL

WebGLПучокJavaScriptа такжеOpenGL ES 2.0объединены, чтобы предоставить разработчикам переднего плана использованиеJavaScriptзаписывать3Dспособность воздействовать.

WebGLдляHTML5 CanvasАппаратное обеспечение3Dдля ускорения рендеринга, поэтомуWebРазработчики могут использовать системную видеокарту для более плавного отображения в браузере.3Dсцены и модели, а также создавать сложную навигацию и визуализацию данных.

1.3 Canvas

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

Общее использованиеCanvasвсе используют это2dизcontextфункция, выполнять2d

В отличие от этого,WebGLтрехмерный и может быть нарисован3Dграфика,WebGL, для отображения в браузере ему нужен носитель, т.е.Canvas, отличается от предыдущего2dcontext, также изCanvasполучено вwebglcontext.

1.4 Three.js

Поймем это буквально:Threeпредставлять3D,jsпредставлятьJavaScript, даже используяJavaScriptразвивать3DЭффект.

Three.jsэто использоватьJavaScriptправильноWebGLИнтерфейс инкапсулирован и упрощен, чтобы сформировать простой в использовании3Dбиблиотека.

Использовать напрямуюWebGLСтоимость разработки относительно высока для разработчиков, и она требует от вас дополнительных знаний в области компьютерной графики.

Three.jsВ определенной степени упрощены некоторые нормы и трудные для понимания понятия, и многиеAPIУпрощенный, что значительно снижает затраты на обучение и разработку 3D-эффектов.

Давайте посмотрим на использованиеThree.jsЗнания, которые необходимо знать.

2. Базовые знания Three.js

использоватьThree.jsЧтобы нарисовать трехмерный эффект, необходимы как минимум следующие шаги:

  • Создайте сцену, которая содержит трехмерное пространство —Sence

  • Добавьте элемент, который нужно отрисовать, на сцену и задайте форму, материал, тень и т. д. элемента.

  • Учитывая положение для наблюдения за сценой и угол обзора, мы используем объект камеры (Camera) контролировать

  • Используйте визуализатор для нарисованного элемента (Renderer) для рендеринга и, наконец, рендеринга в браузере

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

2.1 Сценарий

Сцены позволяют указать, какие объектыthree.jsРендерить и где рендерить.

Мы размещаем объекты, источники света и камеры в сцене.

Очень просто, просто создайтеSceneэкземпляр может быть.

 _scene = new Scene();

2.2 Элементы

Далее со сценой нам нужно, что должно отображаться на сцене.

Сложная трехмерная сцена часто состоит из множества элементов, которые могут быть какой-то нестандартной геометрией (Geometry) или сложные модели импортированы внешне.

Three.jsдает нам многоGeometry,НапримерSphereGeometry(сфера),TetrahedronGeometry(тетраэдр),TorusGeometry(тор) и так далее.

существуетThree.js, материал (Material) определяет конкретную форму, в которой отображается геометрия. Он включает в себя свойства, отличные от формы геометрии, такие как цвет, текстура, прозрачность и т. д.Materialа такжеGeometryдополняют друг друга и должны использоваться в комбинации.

В приведенном ниже коде мы создаем прямоугольный параллелепипед, задаем ему базовый материал сетки (MeshBasicMaterial)

    var geometry = new THREE.BoxGeometry(200, 100, 100);
    var material = new THREE.MeshBasicMaterial({ color: 0x645d50 });
    var mesh = new THREE.Mesh(geometry, material);
            _scene.add(mesh);

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

Основной сетчатый материал (MeshBasicMaterial) не подвержен влиянию света, он не дает теней, давайте изменим геометрию на материал, на который воздействует свет: сетка стандартного материала (Standard Material) и добавьте к нему немного освещения:

    var geometry = new THREE.BoxGeometry(200, 100, 100);
    var material = new THREE.MeshStandardMaterial({ color: 0x645d50 });
    var mesh = new THREE.Mesh(geometry, material);
    _scene.add(mesh);
    // 创建平行光-照亮几何体
    var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
     directionalLight.position.set(-4, 8, 12);
    _scene.add(directionalLight);
    // 创建环境光
    var ambientLight = new THREE.AmbientLight(0xffffff);
    _scene.add(ambientLight);

С рендерингом света геометрия выглядит интереснее3DЭффект,Three.jsЕсть много видов средних источников света, выше мы использовали окружающий свет (AmbientLight) и параллельный свет (DirectionalLight).

Окружающее освещение окрашивает все в сцене.

Направленный свет можно рассматривать как солнечные лучи, освещающие сцену с большого расстояния. Он направлен и может также активировать отражение света на объектах.

Кроме этих двух огней,Three.jsТакже предусмотрено несколько других источников света, которые подходят для визуализации различных материалов в различных ситуациях и могут быть выбраны в соответствии с реальной ситуацией.

2.3 Система координат

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

В трехмерном мире координаты определяют положение элемента в трехмерном пространстве, а начало системы координат является точкой отсчета координат.

Чаще всего мы используем три длины от начала координат (расстояниеxось, расстояниеyось, расстояниеzоси) для определения положения, которое является декартовой системой координат.

При определении системы координат мы обычно используем большой, указательный и средний пальцы, и они друг друга90Проводить. недурноXось, указательный палец представляетYОсь, средний палец представляетZось.

Это создает две системы координат: левую и правую.

Three.jsВ качестве системы координат используется правосторонняя система координат.

Мы можем добавить систему координат к нашей сцене, чтобы мы могли четко видеть, где находятся элементы:

 var axisHelper = new THREE.AxisHelper(600);
 _scene.add(axisHelper);

где красный представляетXось, зеленый представляетYось, синий представляетZось.

2.4 Камера

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

камера(Camera) указывает, где мы просматриваем 3D-сцену и под каким углом.

2.4.1 Разница между двумя камерами

В настоящее времяThree.jsПредусмотрено несколько разных камер, две наиболее часто используемые и две, используемые в плагине ниже:PerspectiveCamera(прозрачная камера),OrthographicCamera(Орфографическая проекционная камера).

Изображение выше ясно объясняет разницу между двумя камерами:

правильноOrthographicCamera(Камера ортогональной проекции) Не имеет эффекта перспективы, то есть на размер объекта не влияет расстояние и расстояние, что соответствует ортогональной проекции в проекции. В большинстве геометрических фигур, рисуемых в наших учебниках по математике, используется эта проекция.

слеваPerspectiveCamera(Перспективная камера), это соответствует нашему нормальному человеческому видению, близкому большому и далекому маленькому, что соответствует перспективной проекции в проекции.

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

2.4.2 Конструктивные параметры

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

_camera = new OrthographicCamera(left, right, top, bottom, near, far);

OrthographicCameraполучает шесть параметров,left, right, top, bottomСоответствует расстоянию вверх, вниз, влево, вправо, далеко и близко соответственно. Элементы, превышающие эти расстояния, не будут отображаться в поле зрения и не будут отображаться браузером. На самом деле эти шесть расстояний составляют куб, поэтомуOrthographicCameraвсегда находится внутри этого куба.

_camera = new PerspectiveCamera(fov, aspect, near, far);

PerspectiveCameraполучает четыре параметра,near,farТо же, что и выше, соответствующее самому дальнему и самому близкому расстоянию, которое может наблюдать камера;fovпредставляет наблюдаемый угол горизонтального диапазона,fovЧем больше диапазон, тем шире можно наблюдать горизонтальный диапазон;aspectпредставляет собой отношение наблюдаемого расстояния в горизонтальном и вертикальном направлениях, поэтомуfovа такжеaspectМожно определить диапазон, который можно наблюдать в вертикальном диапазоне.

2.4.3 позиция, смотреть на

Есть еще два момента, которые необходимо знать о камере.positionсвойства, одинlookAtфункция:

positionСвойство указывает, где находится камера.

lookAtФункция указывает направление, в котором смотрит камера.

Фактическиpositionстоимость иlookAtПолученный параметр является типомVector3Объект, который используется для представления координат в трехмерном пространстве, имеет три свойства:x、y、zсоответственно представляют расстояниеxось, расстояниеyось, расстояниеzрасстояние оси.

Ниже мы позволяем направлению наблюдения камеры указывать на начало координат, аx、y、zравен 0, два других параметра не равны 0, и посмотрите, что происходит с полем зрения:

_camera = new OrthographicCamera(-window.innerWidth / 2, window.innerWidth / 2, window.innerHeight / 2, -window.innerHeight / 2, 0.1, 1000);
 _camera.lookAt(new THREE.Vector3(0, 0, 0))

 _camera.position.set(0, 300, 600); // 1 - x为0

 _camera.position.set(500, 0, 600); // 2 - y为0

 _camera.position.set(500, 300, 0); // 3 - z为0

видеть ясноpositionОпределяет начальную точку нашего поля зрения, но направление, в котором указывает линза, остается прежним.

Ниже мыpositionИсправлено изменение направления взгляда камеры:

_camera = new OrthographicCamera(-window.innerWidth / 2, window.innerWidth / 2, window.innerHeight / 2, -window.innerHeight / 2, 0.1, 1000);
_camera.position.set(500, 300, 600); 

_camera.lookAt(new THREE.Vector3(0, 0, 0)) // 1 - 视野指向原点

_camera.lookAt(new THREE.Vector3(200, 0, 0)) // 2 - 视野偏向x轴

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

2.4.4 Сравнение двух камер

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

var geometry = new THREE.BoxGeometry(200, 100, 100);
var material = new THREE.MeshStandardMaterial({ color: 0x645d50 });
var mesh = new THREE.Mesh(geometry, material);
_scene.add(mesh);

var geometry = new THREE.SphereGeometry(50, 100, 100);
var ball = new THREE.Mesh(geometry, material);
ball.position.set(200, 0, -200);
_scene.add(ball);

Поле зрения камеры ортогональной проекции:

_camera = new OrthographicCamera(-window.innerWidth / 2, window.innerWidth / 2, window.innerHeight / 2, -window.innerHeight / 2, 0.1, 1000);
_camera.position.set(0, 300, 600);
_camera.lookAt(new THREE.Vector3(0, 0, 0))

Поле зрения перспективной камеры:

_camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1100);
_camera.position.set(0, 300, 600);
_camera.lookAt(new THREE.Vector3(0, 0, 0))

Как видно, это подтверждает наши вышеизложенныеТеория о двух камерах

2.5 Рендерер

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

Three.jsОн также предоставляет нам несколько разных рендереров, здесь мы в основном смотрим наWebGLРендерер(WebGLRenderer). Как подсказывает название:WebGLИспользование рендерераWebGLчтобы нарисовать сцену, достаточно использоватьGPUАппаратное ускорение для повышения производительности рендеринга.

_renderer = new THREE.WebGLRenderer();

вам нужно использоватьThree.jsНарисованные элементы добавляются в браузер. Для этого процесса требуется носитель. Как мы уже говорили выше, этот носительCanvas, вы можете пройти_renderer.domElementдобраться до этогоCanvas, и придать ему значение trueDOMсередина.

 _container = document.getElementById('conianer');
 _container.appendChild(_renderer.domElement);

использоватьsetSizeФункция устанавливает диапазон, который вы хотите отобразить, на самом деле он изменяет вышеуказанныйCanvasОбъем:

_renderer.setSize(window.innerWidth, window.innerHeight);

Теперь, когда вы указали вектор рендеринга и диапазон векторов, вы можете передатьrenderФункция визуализирует сцену и камеру, указанные выше:

_renderer.render(_scene, _camera);

На самом деле, если вы выполните приведенный выше код последовательно, экран может быть по-прежнему черным как смоль, и никакие элементы не будут отображаться.

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

2.6 requestAnimationFrame

window.requestAnimationFrame()Сообщите браузеру, что вы хотите выполнить анимацию, и попросите браузер вызвать указанную функцию обратного вызова, чтобы обновить анимацию перед следующей перерисовкой.

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

window.requestAnimationFrame(callback);

Если вы хотите продолжить обновление следующего кадра анимации перед следующей перерисовкой браузера, сама функция обратного вызова должна быть вызвана снова.window.requestAnimationFrame().

Использование корейских функций означает, что вы можетеrequestAnimationFrameПостоянно выполняя операции рисования, браузер в режиме реального времени знает, что ему нужно отображать.

Конечно, иногда вам не нужно рисовать в реальном времени, вы также можете использоватьcancelAnimationFrameНемедленно остановите этот рисунок:

window.cancelAnimationFrame(myReq);

Давайте посмотрим на простой пример:

        var i = 0;
        var animateName;
        animate();
        function animate() {
            animateName = requestAnimationFrame(animate);
            console.log(i++);
            if (i > 100) {
                cancelAnimationFrame(animateName);
            }
        }

Давайте посмотрим на эффект выполнения:

Мы используемrequestAnimationFrameа такжеThree.jsРендерер используется в комбинации, чтобы можно было рисовать 3D-анимацию в реальном времени:

        function animate() {
            requestAnimationFrame(animate);
            _renderer.render(_scene, _camera);
        }

С помощью приведенного выше кода мы можем просто реализовать некоторые эффекты анимации:

        var y = 100;
        var option = 'down';
        function animateIn() {
            animateName = requestAnimationFrame(animateIn);
            mesh.rotateX(Math.PI / 40);
            if (option == 'up') {
                ball.position.set(200, y += 8, 0);
            } else {
                ball.position.set(200, y -= 8, 0);
            }
            if (y < 1) { option = 'up'; }
            if (y > 100) { option = 'down' }
        }

2.7 Резюме

Вышеуказанные знанияThree.jsСамые базовые знания также являются самыми важными и самыми важными.

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

Следующие главы покажут вам, как использоватьThree.jsСделайте это в действии — внедрите плагин 360-градусной панорамы.

Этот плагин состоит из двух частей, первая часть предназначена для предварительного просмотра панорамы.

Вторая часть — настроить маркер панорамы и привязать координаты превью.

Давайте сначала посмотрим на раздел предварительного просмотра панорамы:

3. Панорамный просмотр

3.1 Основная логика

  • Оберните панораму вокруг внутренней стенки сферы

  • Установите точку наблюдения в центре сферы.

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

  • Колесо мыши может масштабировать, увеличивать и изменять перспективу панорамы.

  • Монтировать некоторые маркеры на панораму по координатам, такие как текст, значки и т. д., и может добавлять события, такие как события кликов

3.2 Инициализация

Сначала построим необходимую инфраструктуру:

Сцена, камера (выберите удаленную камеру, это сделает панораму более реалистичной), рендерер:


_scene = new THREE.Scene();
initCamera();
initRenderer();
animate();

// 初始化相机
function initCamera() {
    _camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1100);
    _camera.position.set(0, 0, 2000);
    _camera.lookAt(new THREE.Vector3(0, 0, 0));
}

// 初始化渲染器
function initRenderer() {
    _renderer = new THREE.WebGLRenderer();
    _renderer.setSize(window.innerWidth, window.innerHeight);
    _container = document.getElementById('panoramaConianer');
    _container.appendChild(_renderer.domElement);
}

// 实时渲染
function animate() {
    requestAnimationFrame(animate);
    _renderer.render(_scene, _camera);
}

Ниже мы добавляем к сцене сферу и наматываем панораму как материал на сферу:

var mesh = new THREE.Mesh(new THREE.SphereGeometry(1000, 100, 100),
new THREE.MeshBasicMaterial(
        { map: ImageUtils.loadTexture('img/p3.png') }
    ));
_scene.add(mesh);

Тогда сцена, которую мы видим, должна выглядеть так:

Это не тот эффект, который нам нужен, мы хотим видеть панораму изнутри сферы, и панорама прикрепляется к внутренней стене внешней сферы, а не выложена снаружи:

Нам просто нужноMaterialизscaleДля одного свойства задано отрицательное значение, и материал можно прикрепить к внутренней части геометрии:

 mesh.scale.x = -1;

Затем мы перемещаем центральную точку камеры в центр шара:

 _camera.position.set(0, 0, 0);

Теперь мы внутри панорамы:

3.3 Обработка событий

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

мониторная мышьmousedownсобытие, с которого начнется перетаскивание маркера_isUserInteractingУстановить какtrue, и запишите координаты стартового экрана, а также стартовую камеруlookAtкоордината.

_container.addEventListener('mousedown', (event)=>{
  event.preventDefault();
  _isUserInteracting = true;
  _onPointerDownPointerX = event.clientX;
  _onPointerDownPointerY = event.clientY;
  _onPointerDownLon = _lon;
  _onPointerDownLat = _lat;
});

мониторная мышьmousemoveсобытие, когда_isUserInteractingдляtrueКогда текущая камера рассчитывается в режиме реального времениlookAtреальные координаты.

_container.addEventListener('mousemove', (event)=>{
  if (_isUserInteracting) {
    _lon = (_onPointerDownPointerX - event.clientX) * 0.1 + _onPointerDownLon;
    _lat = (event.clientY - _onPointerDownPointerY) * 0.1 + _onPointerDownLat;
  }
});

мониторная мышьmouseupсобытие, будет_isUserInteractingУстановить какfalse.

_container.addEventListener('mouseup', (event)=>{
 _isUserInteracting = false;
});

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

function animate() {
  requestAnimationFrame(animate);
  calPosition();
  _renderer.render(_scene, _camera);
  _renderer.render(_sceneOrtho, _cameraOrtho);
}

function calPosition() {
  _lat = Math.max(-85, Math.min(85, _lat));
  var phi = tMath.degToRad(90 - _lat);
  var theta = tMath.degToRad(_lon);
  _camera.target.x = _pRadius * Math.sin(phi) * Math.cos(theta);
  _camera.target.y = _pRadius * Math.cos(phi);
  _camera.target.z = _pRadius * Math.sin(phi) * Math.sin(theta);
  _camera.lookAt(_camera.target);
}

мониторmousewheelСобытие, увеличение и уменьшение панорамы, обратите внимание, что здесь указан максимальный диапазон увеличенияmaxFocalLengthи минимальный диапазон увеличенияminFocalLength.

_container.addEventListener('mousewheel', (event)=>{
  var ev = ev || window.event;
  var down = true;
  var m = _camera.getFocalLength();
  down = ev.wheelDelta ? ev.wheelDelta < 0 : ev.detail > 0;
  if (down) {
    if (m > minFocalLength) {
      m -= m * 0.05
      _camera.setFocalLength(m);
    }
  } else {
    if (m < maxFocalLength) {
      m += m * 0.05
      _camera.setFocalLength(m);
    }
  }
});

Давайте посмотрим на эффект:

3.4 Добавить отметку

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

Давайте посмотрим, как добавить маркеры на панораму и как добавить события к этим маркерам.

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

  _cameraOrtho = new THREE.OrthographicCamera(-window.innerWidth / 2, window.innerWidth / 2, window.innerHeight / 2, -window.innerHeight / 2, 1, 10);
  _cameraOrtho.position.z = 10;
  _sceneOrtho = new Scene();

Использовать спрайтовые материалы (SpriteMaterial) для реализации разметки текста или разметки изображения:

// 创建文字标记
function createLableSprite(name) {
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  const metrics = context.measureText(name);
  const width = metrics.width * 1.5;
  context.font = "10px 宋体";
  context.fillStyle = "rgba(0,0,0,0.95)";
  context.fillRect(2, 2, width + 4, 20 + 4);
  context.fillText(name, 4, 20);
  const texture = new Texture(canvas);
  const spriteMaterial = new SpriteMaterial({ map: texture });
  const sprite = new Sprite(spriteMaterial);
  sprite.name = name;
  const lable = {
    name: name,
    canvas: canvas,
    context: context,
    texture: texture,
    sprite: sprite
  };
  _sceneOrtho.add(lable.sprite);
  return lable;
}
// 创建图片标记
function createSprite(position, url, name) {
  const textureLoader = new TextureLoader();
  const ballMaterial = new SpriteMaterial({
    map: textureLoader.load(url)
  });
  const sp = {
    pos: position,
    name: name,
    sprite: new Sprite(ballMaterial)
  };
  sp.sprite.scale.set(32, 32, 1.0);
  sp.sprite.name = name;
  _sceneOrtho.add(sp.sprite);
  return sp;
}

Создав эти маркеры, мы визуализируем их на сцене.

Мы должны сообщить сцене, где находятся эти маркеры. Для интуитивного понимания нам нужно дать этим маркерам координаты. Эта координата очень похожа на широту и долготу. Мы называем этоlonа такжеlat, в частности, как мы даны в следующих разделах:Маркер панорамыбудет представлен подробно.

В этом процессе были испытаны в общей сложности два преобразования координат:

Первое преобразование: преобразовать «широту и долготу» в трехмерные пространственные координаты, о которых мы говорили выше.x、y、zформа координат.

использоватьgeoPosition2Worldфункция для преобразования, чтобы получитьVector3объект, мы можем разместить текущую камеру_cameraПередайте этот объект в качестве параметраprojectметод, это позволит получить нормализованную координату, на основе этой координаты может помочь нам определить, находится ли маркер в поле зрения, например, следующий код, если нормализованная координата находится в-1а также1, он появится в нашем поле зрения, и мы точно его отрендерим.

Второе преобразование: преобразование координат трехмерного пространства в координаты экрана.

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

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

оgeoPosition2Worldа такжеworldPostion2ScreenРеализация двух функций, если интересно, можете зайти ко мнеgithubГлядя на исходный код, я не буду объяснять это здесь, потому что это потребует много профессиональных знаний. 😅

var wp = geoPosition2World(_sprites.lon, _sprites.lat);
var sp = worldPostion2Screen(wp, _camera);
var test = wp.clone();
test.project(_camera);
if (test.x > -1 && test.x < 1 && test.y > -1 && test.y < 1 && test.z > -1 && test.z < 1) {
    _sprites[i].sprite.scale.set(32, 32, 32);
    _sprites[i].sprite.position.set(sp.x, sp.y, 1);
}else {
    _sprites[i].sprite.scale.set(1.0, 1.0, 1.0);
    _sprites[i].sprite.position.set(0, 0, 0);
}

Теперь, когда маркер добавлен на панораму, добавим к нему событие клика:

Three.jsне предоставляется отдельно, т.к.SpriteДля добавления событий мы можем использовать рейкастеры (Raycaster)реализовать.

RaycasterПредоставляет возможность подобрать мышь:

пройти черезsetFromCameraФункция для установления связи между координатами текущего клика (нормализованными) и камерой.

пройти черезintersectObjectsЧтобы определить, какие объекты в наборе объектов были поражены (нажаты), получите массив объектов попадания.

Таким образом, мы можем получить щелкнутый объект и выполнить некоторую обработку на его основе:

_container.addEventListener('click', (event)=>{
  _mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  _mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  _raycaster.setFromCamera(_mouse, _cameraOrtho);
  var intersects = _raycaster.intersectObjects(_clickableObjects);
  intersects.forEach(function (element) {
    alert("点击到了: " + element.object.name);
  });
});

Нажмите на маркер, чтобы перейти к следующей панораме:

4. Панорамная метка

Чтобы панорама знала, где я хочу разместить маркер, мне нужен инструмент, чтобы связать исходное изображение с местоположением на панораме:

Из-за этой части кода иThree.jsЭто не имеет большого значения, здесь я рассказываю только о базовой логике реализации, если вам интересно, вы можете перейти на мойgithubПроверьте склад.

4.1 Требования

  • Установите взаимосвязь между координатами и панорамой и назначьте панораме набор виртуальных координат.

  • На тайловой панораме можно добавлять маркеры в любую позицию и получать координаты маркеров

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

4.2 Координаты

существует2DНа плоскости мы можем отслеживать события мыши на экране, и все, что мы можем получить, это текущие координаты мыши, которые нам нужно сделать, это преобразовать координаты мыши в трехмерные пространственные координаты.

Кажется невозможным, как можно преобразовать 2D-координаты в 3D-координаты?

Однако мы можем использовать промежуточную координату для преобразования, мы можем назвать ее «широта и долгота».

Перед этим давайте посмотрим, что такое широта и долгота, о которых мы часто говорим.

4.3 Долгота и широта

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

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

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

Широта: угол между нормалью точки на сфере (нормалью поверхности, касательной к сфере с этой точкой касания) и плоскостью экватора. Север положительный, а юг отрицательный.

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

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

4.4 Преобразование координат

На основе вышеприведенного анализа мы можем дать виртуальную «широту и долготу» панораме самолета. Мы используемCanvasНарисуйте для него «сетку широты и долготы»:

Преобразование координат мыши в «широту и долготу»:

function calLonLat(e) {
  var h = _setContainer.style.height.split("px")[0];
  var w = _setContainer.style.width.split("px")[0];
  var ix = _setContainer.offsetLeft;
  var iy = _setContainer.offsetTop;
  iy = iy + h;
  var x = e.clientX;
  var y = e.clientY;
  var lonS = (x - ix) / w;
  var lon = 0;
  if (lonS > 0.5) {
    lon = -(1 - lonS) * 360;
  } else {
    lon = 1 * 360 * lonS;
  }
  var latS = (iy - y) / h;
  var lat = 0;
  if (latS > 0.5) {
    lat = (latS - 0.5) * 180;
  } else {
    lat = (0.5 - latS) * 180 * -1
  }
  lon = lon.toFixed(2);
  lat = lat.toFixed(2);
  return { lon: lon, lat: lat };
}

Таким образом можно связать определенную точку на карте плоскости с трехмерными координатами.Конечно, это тоже требует определенного преобразования.Если вам интересно, вы можете перейти к исходному коду для изучения.geoPosition2Worldа такжеworldPostion2Screenдве функции.

Пять, сменная упаковка

В приведенном выше коде мы реализуем функции предварительного просмотра панорамы и разметки панорамы, далее мы инкапсулируем эти функции в плагины.

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

5.1 Пакет предварительного просмотра панорамы

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

var options = {
  container: 'panoramaConianer',
  url: 'resources/img/panorama/pano-7.jpg',
  lables: [],
  widthSegments: 60,
  heightSegments: 40,
  pRadius: 1000,
  minFocalLength: 1,
  maxFocalLength: 100,
  sprite: 'label',
  onClick: () => { }
}
  • container:domконтейнерid
  • url: путь к изображению
  • lables: Массив маркеров на панораме в формате{position:{lon:114,lat:38},logoUrl:'lableLogo.png',text:'name'}
  • widthSegments: Количество горизонтальных разрезов
  • heightSegments: Количество вертикальных секций (маленькое значение означает грубое, быстрое, большое значение — медленное)
  • pRadius: Радиус панорамной сферы, рекомендуется использовать значение по умолчанию.
  • minFocalLength: минимальное расстояние увеличения объектива
  • maxFocalLength: Объектив самая большая ничья ближе
  • sprite: отображаемый тип разметкиlabel,icon
  • onClick: отмеченное событие клика

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

Мы можем объявить некоторую конфигурацию по умолчанию в плагинеoptions, пользователь использует конструктор для передачи параметров, а затем используетObject.assignПереопределить входящую конфигурацию на конфигурацию по умолчанию.

Далее вы можете использоватьthis.defдля доступа к этим переменным, а затем просто измените жестко запрограммированный код на эти конфигурации.

options = {
    // 默认配置...
}

function tpanorama(opt) {
  this.render(opt);
}

tpanorama.prototype = {
  constructor: this,
  def: {},
  render: function (opt) {
    this.def = Object.assign(options, opt);
    // 初始化操作...
  }
}

5.2 Инкапсуляция панорамного маркера

Основная логика аналогична приведенной выше, и ниже приведены некоторые извлеченные параметры.

var setOpt = {
  container: 'myDiv',//setting容器
  imgUrl: 'resources/img/panorama/3.jpg',
  width: '',//指定宽度,高度自适应
  showGrid: true,//是否显示格网
  showPosition: true,//是否显示经纬度提示
  lableColor: '#9400D3',//标记颜色
  gridColor: '#48D1CC',//格网颜色
  lables: [],//标记   {lon:114,lat:38,text:'标记一'}
  addLable: true,//开启后双击添加标记  (必须开启经纬度提示)
  getLable: true,//开启后右键查询标记  (必须开启经纬度提示)
  deleteLbale: true,//开启默认中键删除 (必须开启经纬度提示)
}

6. Отпустите

Далее мы рассмотрим, как сделать написанный плагин доступным для пользователей.

В основном мы рассматриваем два сценария: прямую ссылку иnpm install

6.1 Прямые ссылкиJS

Чтобы не загрязнять глобальные переменные, мы используем самовыполняющуюся функцию(function(){}())Оберните код и выставьте плагин, который мы написали, в глобальные переменные.window.

я вставил этоoriginSrcПод содержанием.

(function (global, undefined) {

    function tpanorama(opt) {
        // ...
    }

    tpanorama.prototype = {
        // ...
    }

    function tpanoramaSetting(opt) {
        // ...
    }

    tpanoramaSetting.prototype = {
        // ...
    }

    global.tpanorama = tpanorama;
    global.tpanoramaSetting = panoramaSetting;
}(window))

6.2 Использованиеnpm install

Экспортируйте письменный плагин напрямую:

module.exports = tpanorama;
module.exports = panoramaSetting;

я вставил этоsrcПод содержанием.

В то же время мы будемpackage.jsonсерединаmainСвойство указывает на файл, который мы хотим экспортировать:"main": "lib/index.js",Потомname,description,versionполная информация.

Теперь мы можем начать публикацию, прежде всего у вас должен бытьnpmУчетная запись и войдите в систему, если у вас нет учетной записи, используйте следующую команду для создания учетной записи.

npm adduser --registry http://registry.npmjs.org

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

npm login --registry http://registry.npmjs.org

После успешного входа вы можете опубликовать:

npm publish --registry http://registry.npmjs.org

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

После успешного выпуска, непосредственно вnpm官网Я видел твою сумку.

Затем вы можете напрямую использоватьnpm install tpanoramaУстановите его, затем используйте:

var { tpanorama,tpanoramaSetting } = require('tpanorama');

6.3 вавилонская компиляция

Наконец, не забывайте, что независимо от того, как мы используем вышеизложенное, мы должны использоватьbabelОн может быть представлен пользователям только после компиляции.

существуетscriptsсоздатьbuildКоманда, которая компилирует исходный файл и, наконец, предоставляет его пользователю, будетlibа такжеorigin.

"build": "babel src --out-dir lib && babel originSrc --out-dir origin",

Вы также можете указать некоторые другие команды для пользовательского тестирования, например, я поместил все написанные примеры вexamplesв , то вscriptsОпределенныйexpamleЗаказ:

"example": "npm run webpack && node ./server/www"

Таким образом, пользователь клонирует код и запускает его локально.npm run exampleВы можете отлаживать.

7. Резюме

этого проектаgithubадрес:GitHub.com/con AR DL i/голосование…

Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, ставьте лайк и подписывайтесь.

Если вы хотите читать больше качественных статей, вы можете подписаться на меняблог на гитхабе, твоя звезда✨, лайки и внимание - движущая сила моего постоянного творчества!

Подписавшись на официальную учетную запись, ответьте на [Добавить группу], чтобы включить вас в высококачественную группу внешнего интерфейса.