系列文章目录
前言
Global Warming项目是基于react框架写的threejs项目,本文将记录从html+threejs嵌入react fiber框架中的过程。
一、项目效果图
背景中的地球仪将绕着y轴匀速转动。
二、细说代码部分
1.拆分组件
效果图分成两部分:
将地球的材质贴图放在arc文件下的textures文件中,component文件下放结果图拆分出来的两个组件。
-
地球仪earth
-
文字topSection
最终,将component汇集在App.jsx中,以下是App.jsx的代码。
- js和jsx不同,jsx的全称为 JavaScript XML。
- react定义的一种类似于XML的JS扩展语法: JS + XML本质是
React.createElement(component, props, ...children)
方法的语法糖。- 作用: 用来简化创建虚拟DOM。
import "./App.css";
import styled from "styled-components";
import { Canvas } from "@react-three/fiber";
import { Suspense } from "react";
import { Earth } from "./components/earth";
import { TopSection } from "./components/topSection";
//CanvasContainer样式
const CanvasContainer = styled.div`
width: 100%;
height: 100%;
`;
function App() {
return (
// Canvas容器
<CanvasContainer>
<TopSection />
<Canvas>
{/* loading的时候:加载状态 */}
<Suspense fallback={null}>
<Earth />
</Suspense>
</Canvas>
</CanvasContainer>
);
}
export default App;
Canvas 组件在幕后做了一些重要的设置工作:
- 它设置了一个Scene和一个Camera,这是渲染所需的基本构建块
- 它会自动处理调整大小
- 它每一帧都渲染我们的场景
细节:
- 可以通过修改
const CanvasContainer = styled.div
…`中的代码,进而实现修改canvas容器的样式。 - 通过
<TopSection />
引入文字部分内容。 <Canvas>
中包含的内容:
1.<Suspense fallback={null}>
:表示在页面加载过程中呈现的状态,在null位置可以填入自定义的loading页面设计。
2. 通过<Earth />
引入地球仪部分。
此处参考博客:使用react-three-fiber加载glb格式3D文件,并播放3D模型自带动画
2.地球仪部分(重点)
代码如下(示例):
import React, { useRef } from "react";
import { useFrame, useLoader } from "@react-three/fiber";
import { OrbitControls, Stars } from "@react-three/drei";
import * as THREE from "three";
import EarthDayMap from "../../assets/textures/8k_earth_daymap.jpg";
import EarthNormalMap from "../../assets/textures/8k_earth_normal_map.jpg";
import EarthSpecularMap from "../../assets/textures/8k_earth_specular_map.jpg";
import EarthCloudsMap from "../../assets/textures/8k_earth_clouds.jpg";
import { TextureLoader } from "three";
export function Earth(props) {
// 加载材质图
const [colorMap, normalMap, specularMap, cloudsMap] = useLoader(
TextureLoader,
[EarthDayMap, EarthNormalMap, EarthSpecularMap, EarthCloudsMap]
);
//
const earthRef = useRef();
const cloudsRef = useRef();
useFrame(({ clock }) => {
const elapsedTime = clock.getElapsedTime();
earthRef.current.rotation.y = elapsedTime / 6;
cloudsRef.current.rotation.y = elapsedTime / 6;
});
return (
<>
{/* 光强度:太阳,无处不在的光 */}
<ambientLight intensity={1} />
{/* 点光 */}
<pointLight color="#f6f3ea" position={[2, 0, 5]} intensity={1.2} />
<Stars
radius={300}
depth={60}
count={20000}
factor={7}
saturation={0}
fade={true}
/>
<mesh ref={cloudsRef} position={[0, 0, 3]}>
<sphereGeometry args={[1.005, 32, 32]} />
{/* 光亮表面的材质 */}
<meshPhongMaterial
map={cloudsMap}
opacity={0.4}
depthWrite={true}
transparent={true}
side={THREE.DoubleSide}
/>
{/* 叠加 */}
</mesh>
<mesh ref={earthRef} position={[0, 0, 3]}>
<sphereGeometry args={[1, 32, 32]} />
<meshPhongMaterial specularMap={specularMap} />
<meshStandardMaterial
map={colorMap}
normalMap={normalMap}
metalness={0.4}
roughness={0.7}
/>
{/* <OrbitControls
enableZoom={true}
enablePan={true}
enableRotate={true}
zoomSpeed={0.6}
panSpeed={0.5}
rotateSpeed={0.4}
/> */}
</mesh>
</>
);
}
细节:
- three.js拥有的组件,放在react中以标签形式出现,且一律小写。
- 加载材质图:
2.1 通过import 别名 from "url"
引入图片;
2.2 通过加载器useLoader(加载器类型,[…加载内容]) - useRef:
此处涉及到react三大组件之一的ref。参考博客:react中ref的使用
reference表示引用。
- 使用场景:直接使用dom元素的某个方法,或者直接使用自定义组件中的某个方法。
- 功能:
- 作用于内置的html组件,得到的是真实的dom
- ref作用于类组件,得到的是类的实例
- ref不能作用于函数组件
-
useFrame:专注于动画渲染。
-
Stars:将基于着色器的星空添加到场景中。这是brei自带的组件。官网文档如图所示。其中包含的参数有,radius=半径;depth=深度;count=数量;factor=因子;saturation=饱和度;fade speed=衰减速度。
-
mesh:
网格就是一系列的多边形组成的,三角形或者四边形,网格一般由顶点来描绘,我们看见的三维开发的模型就是由一系列的点组成的。
几何体是不能被渲染的,只有几何体和材质结合成网格才能被渲染到屏幕上。
3. 文字部分
import React from "react";
import styled from "styled-components";
const TopSectionContainer = styled.div`
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: #1756dd32;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 13%;
z-index: 99;
`;
const Logo = styled.h1`
margin: 0;
color: #fff;
font-weight: 800;
font-size: 80px;
`;
const Slogan = styled.h4`
margin: 0;
color: #fff;
font-weight: 700;
font-size: 30px;
margin-top: 10px;
`;
const Paragraph = styled.p`
margin: 0;
margin-top: 3em;
color: #fff;
font-size: 14px;
line-height: 1.5;
font-weight: 500;
max-width: 30%;
text-align: center;
`;
const DonateButton = styled.button`
outline: none;
border: none;
background-color: #27b927;
color: #fff;
font-size: 16px;
font-weight: 700;
border-radius: 8px;
padding: 8px 2em;
margin-top: 3em;
cursor: pointer;
border: 2px solid transparent;
transition: all 350ms ease-in-out;
&:hover {
background-color: transparent;
border: 2px solid #27b927;
}
`;
const MadeBy = styled.h3`
color: #fff;
position: fixed;
bottom: 5px;
left: 50%;
transform: translateX(-50%);
`;
export function TopSection() {
return (
<TopSectionContainer>
<Logo>Global Warming</Logo>
<Slogan>Keep it cool for safe living</Slogan>
<Paragraph>
You can help us cool off our world and have it go back to it's best
state ever by donating to help fix our only world and our beloved EARTH!
Be cool and let the earth be cool. Let the ice burgs to live. Globe is
warming and will set to fire. Stop polluting, it will cost extra.
</Paragraph>
<DonateButton>Donate</DonateButton>
{/* <MadeBy>
<u>Made By:</u> Islem Maboud
</MadeBy> */}
</TopSectionContainer>
);
}
细节:
- 使用自定义标签时注意首字母大写;
- 通过
styled.
标明css标签,并定义样式;