Научу вас писать классную 3D-лендинговую страницу с помощью three.js

Vue.js three.js
Научу вас писать классную 3D-лендинговую страницу с помощью three.js

Предисловие:

Всем привет, я директор Ян xx media (пожалуйста, придумайте слово xx самостоятельно).

Основные технологии, использованные в этой статье: vue3, three.js

Сначала посмотрим на готовый продукт:

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

Предварительный просмотр эскизов стационарных телефонов:

login2.gif

Без лишних слов, давайте сразу к делу

Основы Three.js

Представьте, что нужно, чтобы оказаться в виртуальном 3D-мире?Во-первых, должно быть трехмерное пространство, за ним источник света, и самое главное, чтобы была пара глаз.. Давайте посмотрим, как создать трехмерный мир в three.js!

  1. Создать сцену
  2. Установите источник света
  3. Создайте камеру, задайте положение камеры и ориентацию объектива камеры.
  4. Создайте 3D-рендерер и используйте его для рендеринга созданной сцены.

На данный момент вы создали визуальную 3D-страницу с помощью three.js, это очень просто, верно!

О сцене

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

[Лендинг] Пример создания сцены:

const scene = new THREE.Scene()
// 在场景中添加雾的效果,Fog参数分别代表‘雾的颜色’、‘开始雾化的视线距离’、刚好雾化至看不见的视线距离’
scene.fog = new THREE.Fog(0x000000, 0, 10000)
// 盒模型的深度
const depth = 1400
// 在场景中添加一个圆球盒模型
// 1.创建一个立方体
const geometry = new THREE.BoxGeometry(1000, 800, depth)
// 2.加载纹理
const texture = new THREE.TextureLoader().load('bg.png')
// 3.创建网格材质(原料)
const material = new THREE.MeshBasicMaterial({map: texture, side: THREE.BackSide})
// 4.生成网格
const mesh = new THREE.Mesh(geometry, material)
// 5.把网格放入场景中
scene.add(mesh)

Об источниках света

Установите цвет и интенсивность источника света для сцены, а также тип источника света (окружающий свет, точечный свет, параллельный свет и т. д.) и расположение источника света.

[Целевая страница] Пример создания источника света:

// 1.创建环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 1)
// 2.创建点光源,位于场景右下角
const light_rightBottom = new THREE.PointLight(0x0655fd, 5, 0)
light_rightBottom.position.set(0, 100, -200)
// 3.把光源放入场景中
scene.add(light_rightBottom)
scene.add(ambientLight)

О камере (важно)

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

Ортогональная камера: размер объекта в окончательном визуализированном изображении остается неизменным независимо от того, насколько далеко или близко объект находится от камеры.Очень полезно для рендеринга 2D-сцен или элементов пользовательского интерфейса.. Как показано на рисунке:

Примечания к рисунку:

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

Перспективная камера: используется для имитации того, что видит человеческий глаз.Это наиболее распространенный режим проекции, используемый при рендеринге 3D-сцен.. Как показано на рисунке:

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

Несколько параметров о перспективной камере,new THREE.PerspectiveCamera(fov, width / height, near, far)

  • fov (field of view) — вертикальное поле зрения усеченной камеры
  • аспект(ширина/высота) — соотношение сторон усеченного конуса камеры
  • ближний — ближний торец усеченной камеры
  • far — дальняя сторона усеченного конуса камеры
/**
 * 为了避免边缘变形,这里将fov角度设置小一些,距离拉远一些
 * 固定视域角度,求需要多少距离才能满足完整的视野画面
 * 15度等于(Math.PI / 12)
 */
const container = document.getElementById('login-three-container')
const width = container.clientWidth
const height = container.clientHeight
const fov = 15
const distance = width / 2 / Math.tan(Math.PI / 12)
const zAxisNumber = Math.floor(distance - depth / 2)
const camera = new THREE.PerspectiveCamera(fov, width / height, 1, 30000)
camera.position.set(0, 0, zAxisNumber)
const cameraTarget = new THREE.Vector3(0, 0, 0)
camera.lookAt(cameraTarget)

О рендерере

использоватьWebGLВизуализируйте свою тщательно созданную сцену. он создаст холст для рендеринга

[Целевая страница] Пример создания рендерера:

// 获取容器dom
const container = document.getElementById('login-three-container')
// 创建webgl渲染器实例
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
// 设置渲染器画布的大小
renderer.setSize(width, height)
// 把画布实例(canvas)放入容器中
container.appendChild(renderer.domElement)
// 渲染器渲染场景
renderer.render(scene, camera)

Следует отметить, что созданная таким образом сцена не имеет эффекта анимации, поскольку в этот раз рендерится только картинка этого кадра. Чтобы заставить объекты в сцене двигаться, нам нужно использовать requestAnimationFrame, поэтому мы можем написать функцию цикла

//动画刷新
const loopAnimate = () => {
    requestAnimationFrame(loopAnimate)
    scene.rotateY(0.001)
    renderer.render(scene, camera)
}
loopAnimate()

Идеальный эффект

Создайте верхний левый глобус

// 加载纹理
const texture = THREE.TextureLoader().load('earth_bg.png')
// 创建网格材质
const material = new THREE.MeshPhongMaterial({map: texture, blendDstAlpha: 1})
// 创建几何球体
const sphereGeometry = new THREE.SphereGeometry(50, 64, 32)
// 生成网格
const sphere = new THREE.Mesh(sphereGeometry, material)
// 为了单独操作球体的运动效果,我们把球体放到一个组中
const Sphere_Group = new THREE.Group()
const Sphere_Group.add(sphere)
// 设置该组(球体)在空间坐标中的位置
const Sphere_Group.position.x = -400
const Sphere_Group.position.y = 200
const Sphere_Group.position.z = -200
// 加入场景
scene.add(Sphere_Group)
// 使球能够自转,需要在loopAnimate中加上
Sphere_Group.rotateY(0.001)

заставить землю вращаться

// 渲染星球的自转
const renderSphereRotate = () => {
    if (sphere) {
      Sphere_Group.rotateY(0.001)
    }
}
// 使球能够自转,需要在loopAnimate中加上
const loopAnimate = () => {
    requestAnimationFrame(loopAnimate)
    renderSphereRotate()
    renderer.render(scene, camera)
}

Создать звезды

// 初始化星星
const initSceneStar = (initZposition: number): any => {
    const geometry = new THREE.BufferGeometry()
    const vertices: number[] = []
    const pointsGeometry: any[] = []
    const textureLoader = new THREE.TextureLoader()
    const sprite1 = textureLoader.load('starflake1.png')
    const sprite2 = textureLoader.load('starflake2.png')
    parameters = [
      [[0.6, 100, 0.75], sprite1, 50],
      [[0, 0, 1], sprite2, 20]
    ]
    // 初始化500个节点
    for (let i = 0; i < 500; i++) {
      /**
       * const x: number = Math.random() * 2 * width - width
       * 等价
       * THREE.MathUtils.randFloatSpread(width)
       * _.random使用的是lodash库中的生成随机数
       */
      const x: number = THREE.MathUtils.randFloatSpread(width)
      const y: number = _.random(0, height / 2)
      const z: number = _.random(-depth / 2, zAxisNumber)
      vertices.push(x, y, z)
    }

    geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3))

    // 创建2种不同的材质的节点(500 * 2)
    for (let i = 0; i < parameters.length; i++) {
      const color = parameters[i][0]
      const sprite = parameters[i][1]
      const size = parameters[i][2]

      materials[i] = new THREE.PointsMaterial({
        size,
        map: sprite,
        blending: THREE.AdditiveBlending,
        depthTest: true,
        transparent: true
      })
      materials[i].color.setHSL(color[0], color[1], color[2])
      const particles = new THREE.Points(geometry, materials[i])
      particles.rotation.x = Math.random() * 0.2 - 0.15
      particles.rotation.z = Math.random() * 0.2 - 0.15
      particles.rotation.y = Math.random() * 0.2 - 0.15
      particles.position.setZ(initZposition)
      pointsGeometry.push(particles)
      scene.add(particles)
    }
    return pointsGeometry
}
const particles_init_position = -zAxisNumber - depth / 2
let zprogress = particles_init_position
let zprogress_second = particles_init_position * 2
const particles_first = initSceneStar(particles_init_position)
const particles_second = initSceneStar(zprogress_second)

заставить звезды двигаться

// 渲染星星的运动
const renderStarMove = () => {
    const time = Date.now() * 0.00005
    zprogress += 1
    zprogress_second += 1

    if (zprogress >= zAxisNumber + depth / 2) {
      zprogress = particles_init_position
    } else {
      particles_first.forEach((item) => {
        item.position.setZ(zprogress)
      })
    }
    if (zprogress_second >= zAxisNumber + depth / 2) {
      zprogress_second = particles_init_position
    } else {
      particles_second.forEach((item) => {
        item.position.setZ(zprogress_second)
      })
    }

    for (let i = 0; i < materials.length; i++) {
      const color = parameters[i][0]

      const h = ((360 * (color[0] + time)) % 360) / 360
      materials[i].color.setHSL(color[0], color[1], parseFloat(h.toFixed(2)))
    }
}

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

Создавайте облака и следы движения

// 创建曲线路径
const route = [
    new THREE.Vector3(-width / 10, 0, -depth / 2),
    new THREE.Vector3(-width / 4, height / 8, 0),
    new THREE.Vector3(-width / 4, 0, zAxisNumber)
]
const curve = new THREE.CatmullRomCurve3(route, false)
const tubeGeometry = new THREE.TubeGeometry(curve, 100, 2, 50, false)
const tubeMaterial = new THREE.MeshBasicMaterial({
  opacity: 0,
  transparent: true
})
const tube = new THREE.Mesh(tubeGeometry, tubeMaterial)
// 把创建好的路径加入场景中
scene.add(tube)
// 创建平面几何
const clondGeometry = new THREE.PlaneGeometry(500, 200)
const textureLoader = new THREE.TextureLoader()
const cloudTexture = textureLoader.load('cloud.png')
const clondMaterial = new THREE.MeshBasicMaterial({
  map: cloudTexture,
  blending: THREE.AdditiveBlending,
  depthTest: false,
  transparent: true
})
const cloud = new THREE.Mesh(clondGeometry, clondMaterial)
// 将云加入场景中
scene.add(cloud)

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

заставить облако двигаться

let cloudProgress = 0
let scaleSpeed = 0.0006
let maxScale = 1
let startScale = 0
// 初始化云的运动函数
const cloudMove = () => {
  if (startScale < maxScale) {
    startScale += scaleSpeed
    cloud.scale.setScalar(startScale)
  }
  if (cloudProgress > 1) {
    cloudProgress = 0
    startScale = 0
  } else {
    cloudProgress += speed
    if (cloudParameter.curve) {
      const point = curve.getPoint(cloudProgress)
      if (point && point.x) {
        cloud.position.set(point.x, point.y, point.z)
      }
    }
  }

}

Завершите эффекты, связанные с three.js

Наконец, поместите функцию cloudMove в функцию loopAnimate, чтобы реализовать движение облака. На данный момент представлены все части целевой страницы, связанные с three.js. Остальная часть лунной земли, окно входа в систему и астронавты реализованы с помощью настроек позиционирования и уровня и анимации css3, поэтому я не буду подробно обсуждать их здесь.

Код для каждой части вышеперечисленного не является полным по согласованности, а также немного отличается от полного кода для страницы входа. Вышеизложенное больше для того, чтобы представить, как реализована каждая часть. Полный код, выложил на гитхаб, почти каждая строчка комментариев помечена, надеюсь поможет попасть в яму three.js, адрес:GitHub.com/ya N Zeng Yong…

Эпилог

Раньше я использовал react+three.js для написания визуальной карты знаний о 3D.Если эта статья будет полезна для вас и получит хороший отклик, я напишу статью о том, как использовать three.js для создания карты знаний о 3D.

Наконец, я думаю, что суть 3D-визуализации заключается в дизайне, хорошие материалы и хорошая модель могут мгновенно улучшить эффект вашей страницы в N раз.

официальный сайт three.js