feat: 添加zelda container代码.
|
|
@ -7,6 +7,7 @@ import Demo from './containers/Demo/index';
|
|||
import Lunar from './containers/Lunar/index';
|
||||
import Cell from './containers/Cell/index';
|
||||
import Car from './containers/Car/index';
|
||||
import Zelda from './containers/Zelda/index';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
|
|
@ -20,6 +21,7 @@ function App() {
|
|||
<Route element={ <Lunar /> } path="/lunar" />
|
||||
<Route element={ <Cell /> } path="/cell" />
|
||||
<Route element={ <Car /> } path="/car" />
|
||||
<Route element={ <Zelda /> } path="/zelda" />
|
||||
</Routes>
|
||||
</Router>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import carModel from './models/car.gltf';
|
|||
import Animations from '../../assets/utils/animations';
|
||||
import './index.css';
|
||||
|
||||
export default class Earth extends React.Component {
|
||||
export default class Car extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 602 KiB |
|
After Width: | Height: | Size: 225 KiB |
|
|
@ -11,7 +11,9 @@ import earthImage from './images/earth.png';
|
|||
import cellImage from './images/cell.png';
|
||||
import lunarImage from './images/lunar.png';
|
||||
import zeldaImage from './images/zelda.png';
|
||||
import zeldaMapImage from './images/zelda_map.png';
|
||||
import scanImage from './images/scan.png';
|
||||
import carImage from './images/car.png';
|
||||
import './index.css';
|
||||
|
||||
const Item = styled(Paper)(({ theme }) => ({
|
||||
|
|
@ -68,13 +70,27 @@ const workList = [
|
|||
link: 'https://dragonir.github.io/zelda-map/',
|
||||
title: '塞尔达:旷野之息地图',
|
||||
description: '🗺 在地图上标记神庙、查询回忆点!',
|
||||
image: zeldaImage,
|
||||
image: zeldaMapImage,
|
||||
},
|
||||
{
|
||||
link: 'https://dragonir.github.io/h5-scan-qrcode/',
|
||||
title: '浏览器扫码',
|
||||
description: '📷 使用原生浏览器就可以在h5页面实现扫码功能了,试试看!',
|
||||
image: scanImage,
|
||||
},
|
||||
{
|
||||
link: '#/car',
|
||||
title: 'Lamborghini Centenario LP-770',
|
||||
description: '📷车辆模型展示',
|
||||
image: carImage,
|
||||
three: true
|
||||
},
|
||||
{
|
||||
link: '#/zelda',
|
||||
title: '塞尔达:旷野之息3D',
|
||||
description: '📷 林克',
|
||||
image: zeldaImage,
|
||||
three: true
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,170 @@
|
|||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import * as THREE from "three";
|
||||
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
|
||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
||||
import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js";
|
||||
import Stats from "three/examples/jsm/libs/stats.module";
|
||||
import linkModel from './models/link.fbx';
|
||||
import Animations from '../../assets/utils/animations';
|
||||
import './index.css';
|
||||
|
||||
export default class Zelda extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
state = {
|
||||
loadingProcess: 0
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.initThree()
|
||||
}
|
||||
|
||||
initThree = () => {
|
||||
var container, controls, stats;
|
||||
var camera, scene, renderer, light, meshes = [];
|
||||
var _this = this;
|
||||
init();
|
||||
animate();
|
||||
function init() {
|
||||
container = document.getElementById('container');
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.shadowMap.enabled = true;
|
||||
renderer.shadowMap.needsUpdate = true;
|
||||
renderer.outputEncoding = THREE.sRGBEncoding;
|
||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||
|
||||
container.appendChild(renderer.domElement);
|
||||
|
||||
scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x000000);
|
||||
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
camera.position.set(0, 10, 20);
|
||||
camera.lookAt(new THREE.Vector3(0, 0, 0));
|
||||
|
||||
var axes = new THREE.AxisHelper(30);
|
||||
scene.add(axes);
|
||||
|
||||
const cubeGeometry = new THREE.BoxGeometry(0.001, 0.001, 0.001);
|
||||
const cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
|
||||
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
|
||||
cube.position.set(0, 0, 0);
|
||||
light = new THREE.DirectionalLight(0xffffff, 1);
|
||||
light.intensity = .1;
|
||||
light.position.set(12, 16, 6);
|
||||
light.castShadow = true;
|
||||
light.target = cube;
|
||||
light.shadow.mapSize.width = 512 * 12;
|
||||
light.shadow.mapSize.height = 512 * 12;
|
||||
light.shadow.camera.top = 10;
|
||||
light.shadow.camera.bottom = - 10;
|
||||
light.shadow.camera.left = - 10;
|
||||
light.shadow.camera.right = 10;
|
||||
// scene.add(light);
|
||||
|
||||
const lightHelper = new THREE.DirectionalLightHelper(light, 1, 'red');
|
||||
scene.add(lightHelper);
|
||||
const lightCameraHelper = new THREE.CameraHelper(light.shadow.camera);
|
||||
scene.add(lightCameraHelper);
|
||||
|
||||
var ambientLight = new THREE.AmbientLight(0xffffff);
|
||||
scene.add(ambientLight);
|
||||
|
||||
const manager = new THREE.LoadingManager();
|
||||
manager.onStart = (url, loaded, total) => {};
|
||||
manager.onLoad = () => {};
|
||||
manager.onProgress = async(url, loaded, total) => {
|
||||
if (Math.floor(loaded / total * 100) === 100) {
|
||||
_this.loadingProcessTimeout && clearTimeout(_this.loadingProcessTimeout);
|
||||
_this.loadingProcessTimeout = setTimeout(() => {
|
||||
_this.setState({ loadingProcess: Math.floor(loaded / total * 100) });
|
||||
Animations.animateCamera(camera, controls, { x: 0, y: 5, z: 21 }, { x: 0, y: 0, z: 0 }, 2400, () => {});
|
||||
}, 800);
|
||||
} else {
|
||||
_this.setState({ loadingProcess: Math.floor(loaded / total * 100) });
|
||||
}
|
||||
};
|
||||
|
||||
const fbxLoader = new FBXLoader(manager);
|
||||
fbxLoader.load(linkModel, mesh => {
|
||||
mesh.traverse(child => {
|
||||
if (child.isMesh) {
|
||||
meshes.push(mesh);
|
||||
}
|
||||
});
|
||||
mesh.position.set(0, -5, 0);
|
||||
mesh.scale.set(.075, .075, .075);
|
||||
scene.add(mesh);
|
||||
});
|
||||
|
||||
controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.target.set(0, 0, 0);
|
||||
controls.enableDamping = true;
|
||||
window.addEventListener('resize', onWindowResize, false);
|
||||
|
||||
// 性能工具
|
||||
stats = new Stats();
|
||||
document.documentElement.appendChild(stats.dom);
|
||||
|
||||
// 增加点击事件,声明raycaster和mouse变量
|
||||
var raycaster = new THREE.Raycaster();
|
||||
var mouse = new THREE.Vector3();
|
||||
function onMouseClick(event) {
|
||||
// 通过鼠标点击的位置计算出raycaster所需要的点的位置,以屏幕中心为原点,值的范围为-1到1.
|
||||
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
|
||||
// 通过鼠标点的位置和当前相机的矩阵计算出raycaster
|
||||
// 屏幕坐标转标准设备坐标
|
||||
const x1 = ( event.clientX / window.innerWidth ) * 2 - 1;
|
||||
const y1 = -( event.clientX / window.innerHeight ) * 2 + 1;
|
||||
//标准设备坐标(z=0.5这个值比较靠经验)
|
||||
const stdVector = new THREE.Vector3(x1, y1, 0.5);
|
||||
//世界坐标
|
||||
const worldVector = stdVector.unproject(camera);
|
||||
console.log(worldVector);
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
// 获取raycaster直线和所有模型相交的数组集合
|
||||
var intersects = raycaster.intersectObjects(meshes);
|
||||
if (intersects.length > 0) {
|
||||
console.log(intersects[0].object);
|
||||
}
|
||||
}
|
||||
window.addEventListener('click', onMouseClick, false);
|
||||
|
||||
console.log(scene)
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
renderer.render(scene, camera);
|
||||
stats && stats.update();
|
||||
controls && controls.update();
|
||||
TWEEN && TWEEN.update();
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<div id="container"></div>
|
||||
{this.state.loadingProcess === 100 ? '' : (
|
||||
<div className="car_loading">
|
||||
<div className="box">{this.state.loadingProcess} %</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 339 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 8.0 MiB |
|
Before Width: | Height: | Size: 8.0 MiB |
|
Before Width: | Height: | Size: 370 KiB |
|
Before Width: | Height: | Size: 307 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 831 KiB |
|
Before Width: | Height: | Size: 1.8 MiB |