feat: Olympic Container添加飘动的旗帜🚩,样式优化.
|
|
@ -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人像优化》
|
||||
* 《y2k!3D千年虫风格,y2k的事你少管》
|
||||
* 《喜羊羊你可真厉害,沸羊羊我讨厌你》
|
||||
|
||||
## 附录
|
||||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
Before Width: | Height: | Size: 886 KiB After Width: | Height: | Size: 205 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 857 KiB After Width: | Height: | Size: 844 KiB |
|
Before Width: | Height: | Size: 358 B After Width: | Height: | Size: 324 B |
|
Before Width: | Height: | Size: 486 KiB After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 287 KiB |
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||