feat: Olympic Container添加飘动的旗帜🚩,样式优化.

This commit is contained in:
dragonir 2022-01-29 17:17:08 +08:00
parent 5b3f768eed
commit a02942a7cc
18 changed files with 7779 additions and 257 deletions

View File

@ -1,4 +1,4 @@
# Three.js实现3D冬奥会创意页面
# Three.js实现3D冬奥主题会创意页面
<!-- 迎冬奥向未来 -->
@ -11,4 +11,20 @@ https://anyconv.com/tw/max-zhuan-obj/
在blender2.8中怎么给模型贴图
blender 模型贴图 https://jingyan.baidu.com/article/363872ecf6367f2f4ba16f95.html
blender 模型贴图 https://jingyan.baidu.com/article/363872ecf6367f2f4ba16f95.html
## 背景
## 效果
## 实现
## 总结
## 下期预告
* 《Metahuman元人类记一次Three.js人像优化》
* 《y2k3D千年虫风格y2k的事你少管》
* 《喜羊羊你可真厉害,沸羊羊我讨厌你》
## 附录

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 KiB

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 857 KiB

After

Width:  |  Height:  |  Size: 844 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 358 B

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

View File

@ -0,0 +1,28 @@
.olympic_loading {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
background: #FFFFFF url('./images/panda.png') no-repeat left center;
background-size: auto 60%;
display: flex;
justify-content: space-around;
align-items: center;
font-size: 120px;
color: #2D2D2D;
text-shadow: 0 1px 0 hsl(174,5%,80%),
0 2px 0 hsl(174,5%,75%),
0 3px 0 hsl(174,5%,70%),
0 4px 0 hsl(174,5%,66%),
0 5px 0 hsl(174,5%,64%),
0 6px 0 hsl(174,5%,62%),
0 7px 0 hsl(174,5%,61%),
0 8px 0 hsl(174,5%,60%),
0 0 5px rgba(0,0,0,.05),
0 1px 3px rgba(0,0,0,.2),
0 3px 5px rgba(0,0,0,.2),
0 5px 10px rgba(0,0,0,.2),
0 10px 10px rgba(0,0,0,.2),
0 20px 20px rgba(0,0,0,.3);
}

View File

@ -2,13 +2,18 @@
import React from 'react';
import Stats from "three/examples/jsm/libs/stats.module";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js";
import Animations from '../../assets/utils/animations';
import landModel from './models/land.glb';
import pandaModel from './models/panda.gltf';
import treeModel from './models/panda.gltf';
import bingdundunModel from './models/bingdundun.glb';
import flagModel from './models/flag.glb';
import skyTexture from './images/sky.jpg';
import snowTexture from './images/snow.png';
import treeTexture from './images/tree.png';
import flagTexture from './images/flag.png';
require('./libs/GLTFLoader.js');
import './index.css';
export default class Olympic extends React.Component {
@ -16,18 +21,22 @@ export default class Olympic extends React.Component {
super(props);
}
state = {
loadingProcess: 0
}
componentDidMount() {
this.initThree()
}
initThree = () => {
var container, controls, stats;
var container, controls, stats, mixer;
var camera, scene, renderer, light, land = null, meshes = [], points = [];
var fiveCyclesGroup = new THREE.Group();
var fiveCyclesGroup = new THREE.Group(), clock = new THREE.Clock();
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var _this = this;
init();
animate();
function init() {
@ -41,47 +50,74 @@ export default class Olympic extends React.Component {
scene = new THREE.Scene();
scene.background = new THREE.TextureLoader().load(skyTexture);
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, -2, 20);
camera.position.set(0, 30, 100);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// var axes = new THREE.AxisHelper(30);
// scene.add(axes);
// 性能工具
stats = new Stats();
document.documentElement.appendChild(stats.dom);
// stats = new Stats();
// document.documentElement.appendChild(stats.dom);
const cubeGeometry = new THREE.BoxGeometry(0.001, 0.001, 0.001);
const cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xdc161a });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(0, 0, 0,);
light = new THREE.DirectionalLight(0xdddddd, 1);
light.intensity = 1.2;
light.position.set(-5, 8, 3);
cube.position.set(0, 0, 0);
light = new THREE.DirectionalLight(0xffffff, 1);
light.intensity = 1;
light.position.set(16, 16, 8);
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 = - 5;
light.shadow.camera.left = - 5;
light.shadow.camera.right = 10;
light.shadow.camera.top = 40;
light.shadow.camera.bottom = -40;
light.shadow.camera.left = -40;
light.shadow.camera.right = 40;
scene.add(light);
var ambientLight = new THREE.AmbientLight(0xdddddd);
// 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(0xcfffff);
ambientLight.intensity = 1;
scene.add(ambientLight);
const manager = new THREE.LoadingManager();
manager.onStart = (url, loaded, total) => {};
manager.onLoad = () => { console.log('Loading complete!')};
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: -1, z: 20 }, { x: 0, y: 0, z: 0 }, 3600, () => {});
}, 800);
} else {
_this.setState({ loadingProcess: Math.floor(loaded / total * 100) });
}
};
// 添加地面
var loader = new GLTFLoader();
var loader = new THREE.GLTFLoader(manager);
loader.load(landModel, function (mesh) {
mesh.scene.traverse(function (child) {
if (child.isMesh) {
meshes.push(child)
child.material.metalness = .1;
child.material.roughness = .8;
// 地面
if (child.name === 'Mesh_2') {
child.material.metalness = .1
child.material.roughness = .8
child.material.color = new THREE.Color(0xffffff)
child.material.metalness = .5;
child.receiveShadow = true;
}
// 围巾
if (child.name === 'Mesh_17') {
child.material.metalness = .2;
child.material.roughness = .8;
}
// 帽子
if (child.name === 'Mesh_17') {}
}
});
mesh.scene.rotation.y = Math.PI / 4;
@ -91,6 +127,39 @@ export default class Olympic extends React.Component {
scene.add(land);
});
// 旗帜
loader.load(flagModel, mesh => {
mesh.scene.traverse(child => {
if (child.isMesh) {
meshes.push(child)
child.castShadow = true;
// 旗帜
if (child.name === 'mesh_0001') {
child.material.metalness = .1;
child.material.roughness = .1;
child.material.map = new THREE.TextureLoader().load(flagTexture);
}
// 旗杆
if (child.name === '柱体') {
child.material.metalness = .6;
child.material.roughness = 0;
child.material.refractionRatio = 1;
child.material.color = new THREE.Color(0xeeeeee);
}
}
});
mesh.scene.rotation.y = Math.PI / 24;
mesh.scene.position.set(2, -7, -1);
mesh.scene.scale.set(4, 4, 4);
// 动画
let meshAnimation = mesh.animations[0];
mixer = new THREE.AnimationMixer(mesh.scene);
let animationClip = meshAnimation;
let clipAction = mixer.clipAction(animationClip).play();
animationClip = clipAction.getClip();
scene.add(mesh.scene);
});
// bingdundun
loader.load(bingdundunModel, function (mesh) {
mesh.scene.traverse(function (child) {
@ -98,99 +167,84 @@ export default class Olympic extends React.Component {
meshes.push(child)
if (child.name === 'oldtiger001') {
child.material.metalness = .5
child.material.roughness = .8
}
if (child.name === 'oldtiger002') {
child.material.transparent = true;
child.material.opacity = .5
child.material.metalness = .2
child.material.roughness = 0
child.material.refractionRatio = 1
child.castShadow = true;
}
}
});
mesh.scene.rotation.y = Math.PI / 24;
mesh.scene.position.set(-8, -10, 0);
mesh.scene.position.set(-8, -12, 0);
mesh.scene.scale.set(24, 24, 24);
scene.add(mesh.scene);
});
// 添加树
let pandaMaterial = new THREE.MeshPhysicalMaterial({
map: new THREE.TextureLoader().load(treeTexture),
transparent: true,
side: THREE.DoubleSide,
metalness: .2,
roughness: .8,
depthTest: true,
depthWrite: false,
skinning: false,
fog: false,
reflectivity: 0.1,
refractionRatio: 0,
let treeMaterial = new THREE.MeshPhysicalMaterial({
map: new THREE.TextureLoader().load(treeTexture),
transparent: true,
side: THREE.DoubleSide,
metalness: .2,
roughness: .8,
depthTest: true,
depthWrite: false,
skinning: false,
fog: false,
reflectivity: 0.1,
refractionRatio: 0,
});
let pandaCustomDepthMaterial = new THREE.MeshDepthMaterial({
depthPacking: THREE.RGBADepthPacking,
map: new THREE.TextureLoader().load(treeTexture),
alphaTest: 0.5
let treeCustomDepthMaterial = new THREE.MeshDepthMaterial({
depthPacking: THREE.RGBADepthPacking,
map: new THREE.TextureLoader().load(treeTexture),
alphaTest: 0.5
});
loader.load(pandaModel, function (mesh) {
loader.load(treeModel, function (mesh) {
mesh.scene.traverse(function (child) {
if (child.isMesh) {
meshes.push(child)
child.material = pandaMaterial
child.custromMaterial = pandaCustomDepthMaterial
child.material = treeMaterial;
child.custromMaterial = treeCustomDepthMaterial;
}
});
mesh.scene.position.set(14, -8, 0);
mesh.scene.position.set(14, -9, 0);
mesh.scene.scale.set(16, 16, 16);
scene.add(mesh.scene);
let tree2 = mesh.scene.clone();
tree2.position.set(8, -8, -14);
tree2.position.set(10, -8, -15);
tree2.scale.set(18, 18, 18);
scene.add(tree2)
let tree3 = mesh.scene.clone();
tree3.position.set(-16, -8, -16);
tree3.scale.set(18, 18, 18);
tree3.position.set(-18, -8, -16);
tree3.scale.set(22, 22, 22);
scene.add(tree3)
});
// 创建五环
const fiveCycles = [
{
key: 'cycle_0',
color: 0x0885c2,
position: { x: -250, y: 0, z: 0 }
},
{
key: 'cycle_1',
color: 0x000000,
position: { x: -10, y: 0, z: 5 }
},
{
key: 'cycle_2',
color: 0xed334e,
position: { x: 230, y: 0, z: 0 }
},
{
key: 'cycle_3',
color: 0xfbb132,
position: { x: -125, y: -100, z: -5 }
},
{
key: 'cycle_4',
color: 0x1c8b3c,
position: { x: 115, y: -100, z: 10 }
}
{ key: 'cycle_0', color: 0x0885c2, position: { x: -250, y: 0, z: 0 }},
{ key: 'cycle_1', color: 0x000000, position: { x: -10, y: 0, z: 5 }},
{ key: 'cycle_2', color: 0xed334e, position: { x: 230, y: 0, z: 0 }},
{ key: 'cycle_3', color: 0xfbb132, position: { x: -125, y: -100, z: -5 }},
{ key: 'cycle_4', color: 0x1c8b3c, position: { x: 115, y: -100, z: 10 }}
];
fiveCycles.map(item => {
let cycleMesh = new THREE.Mesh(new THREE.TorusGeometry(100, 10, 10, 50), new THREE.MeshLambertMaterial({
color: new THREE.Color(item.color),
side: THREE.DoubleSide
}));
cycleMesh.castShadow = true;
cycleMesh.position.set(item.position.x, item.position.y, item.position.z);
meshes.push(cycleMesh);
fiveCyclesGroup.add(cycleMesh);
});
fiveCyclesGroup.scale.set(.03, .03, .03);
fiveCyclesGroup.position.set(0, 8, -5);
fiveCyclesGroup.scale.set(.036, .036, .036);
fiveCyclesGroup.position.set(0, 10, -8);
scene.add(fiveCyclesGroup);
// 创建雪花
@ -226,9 +280,12 @@ export default class Olympic extends React.Component {
controls.enableDamping = true;
controls.enablePan = false;
controls.enableZoom = false;
// 最大仰角
// 垂直旋转角度限制
controls.minPolarAngle = 1.4;
controls.maxPolarAngle = 1.8;
// 水平旋转角度限制
controls.minAzimuthAngle = -.6;
controls.maxAzimuthAngle = .6;
window.addEventListener('resize', onWindowResize, false);
}
@ -238,17 +295,13 @@ export default class Olympic extends React.Component {
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onDocumentMouseMove( event ) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
stats && stats.update();
controls && controls.update();
fiveCyclesGroup && (fiveCyclesGroup.rotation.y += .01)
TWEEN && TWEEN.update();
fiveCyclesGroup && (fiveCyclesGroup.rotation.y += .01);
let vertices = points.geometry.vertices;
vertices.forEach(function (v) {
v.y = v.y - (v.velocityY);
@ -258,6 +311,8 @@ export default class Olympic extends React.Component {
});
// 顶点变动之后需要更新,否则无法实现雨滴特效
points.geometry.verticesNeedUpdate = true;
let time = clock.getDelta();
mixer && mixer.update(time);
}
// 增加点击事件声明raycaster和mouse变量
@ -280,7 +335,15 @@ export default class Olympic extends React.Component {
render () {
return (
<div id="container"></div>
<div>
<div id="container"></div>
{this.state.loadingProcess === 100 ? '' : (
<div className="olympic_loading">
<div className="box">{this.state.loadingProcess} %</div>
</div>
)
}
</div>
)
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1,171 +0,0 @@
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>雪花</title>
<style type="text/css">
body {
width: 100%;
height: 100%;
background-color: #5569ae;
background-image: url("img/bg.png");
background-size: 100% auto;
}
.snow-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 100001;
}
</style>
</head>
<body>
<div class="snow-container"></div>
<script src="jquery.min.js"></script>
<script type="text/javascript" src="three.min.js"></script>
<script type="text/javascript">
function randomRange(t, i) {
return Math.random() * (i - t) + t
}
Particle3D = function (t) {
THREE.Particle.call(this, t), this.velocity = new THREE.Vector3(0, -2, 0), this.velocity.rotateX(randomRange(-
45, 45)), this.velocity.rotateY(randomRange(0, 360)), this.gravity = new THREE.Vector3(0, 0, 0), this
.drag = 1
}, Particle3D.prototype = new THREE.Particle, Particle3D.prototype.constructor = Particle3D, Particle3D.prototype
.updatePhysics = function () {
this.velocity.multiplyScalar(this.drag), this.velocity.addSelf(this.gravity), this.position.addSelf(this
.velocity)
};
var TO_RADIANS = Math.PI / 180;
THREE.Vector3.prototype.rotateY = function (t) {
cosRY = Math.cos(t * TO_RADIANS), sinRY = Math.sin(t * TO_RADIANS);
var i = this.z,
o = this.x;
this.x = o * cosRY + i * sinRY, this.z = o * -sinRY + i * cosRY
}, THREE.Vector3.prototype.rotateX = function (t) {
cosRY = Math.cos(t * TO_RADIANS), sinRY = Math.sin(t * TO_RADIANS);
var i = this.z,
o = this.y;
this.y = o * cosRY + i * sinRY, this.z = o * -sinRY + i * cosRY
}, THREE.Vector3.prototype.rotateZ = function (t) {
cosRY = Math.cos(t * TO_RADIANS), sinRY = Math.sin(t * TO_RADIANS);
var i = this.x,
o = this.y;
this.y = o * cosRY + i * sinRY, this.x = o * -sinRY + i * cosRY
};
$(function () {
var container = document.querySelector(".snow-container");
if (/MSIE 6|MSIE 7|MSIE 8/.test(navigator.userAgent)) {
return
} else {
if (/MSIE 9|MSIE 10/.test(navigator.userAgent)) {
$(container).css("height", $(window).height()).bind("click", function () {
$(this).fadeOut(1000, function () {
$(this).remove()
})
})
}
}
var containerWidth = $(container).width();
var containerHeight = $(container).height();
var particle;
var camera;
var scene;
var renderer;
var mouseX = 0;
var mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var particles = [];
var particleImage = new Image();
particleImage.src = "img/snow.png";
var snowNum = 500;
function init() {
camera = new THREE.PerspectiveCamera(75, containerWidth / containerHeight, 1, 10000);
camera.position.z = 1000;
scene = new THREE.Scene();
scene.add(camera);
renderer = new THREE.CanvasRenderer();
renderer.setSize(containerWidth, containerHeight);
var material = new THREE.ParticleBasicMaterial({
map: new THREE.Texture(particleImage)
});
for (var i = 0; i < snowNum; i++) {
particle = new Particle3D(material);
particle.position.x = Math.random() * 2000 - 1000;
particle.position.y = Math.random() * 2000 - 1000;
particle.position.z = Math.random() * 2000 - 1000;
particle.scale.x = particle.scale.y = 1;
scene.add(particle);
particles.push(particle)
}
container.appendChild(renderer.domElement);
document.addEventListener("mousemove", onDocumentMouseMove, false);
document.addEventListener("touchstart", onDocumentTouchStart, false);
document.addEventListener("touchmove", onDocumentTouchMove, false);
setInterval(loop, 1000 / 40)
}
function onDocumentMouseMove(event) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY
}
function onDocumentTouchStart(event) {
if (event.touches.length == 1) {
event.preventDefault();
mouseX = event.touches[0].pageX - windowHalfX;
mouseY = event.touches[0].pageY - windowHalfY
}
}
function onDocumentTouchMove(event) {
if (event.touches.length == 1) {
event.preventDefault();
mouseX = event.touches[0].pageX - windowHalfX;
mouseY = event.touches[0].pageY - windowHalfY
}
}
function loop() {
for (var i = 0; i < particles.length; i++) {
var particle = particles[i];
particle.updatePhysics();
with (particle.position) {
if (y < -1000) {
y += 2000
}
if (x > 1000) {
x -= 2000
} else {
if (x < -1000) {
x += 2000
}
}
if (z > 1000) {
z -= 2000
} else {
if (z < -1000) {
z += 2000
}
}
}
}
camera.position.x += (mouseX - camera.position.x) * 0.005;
camera.position.y += (-mouseY - camera.position.y) * 0.005;
camera.lookAt(scene.position);
renderer.render(scene, camera)
}
init()
});
</script>
</body>
</html>