Commit eb6c83d0 authored by Master Q's avatar Master Q

init

parents
node_modules/
yarn.lock
\ No newline at end of file
This diff is collapsed.
{
"name": "webpackthreeprolesson",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server",
"build": "webpack"
},
"author": "",
"license": "ISC",
"dependencies": {
"three": "^0.138.3"
},
"devDependencies": {
"@types/three": "^0.138.0",
"ts-loader": "^9.2.8",
"typescript": "^4.6.2",
"webpack": "^5.70.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4"
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three</title>
<style>
</style>
</head>
<body>
<!-- 这里的 bundle 就是 webpack 临时打包出来 -->
<script src="bundle.js"></script>
</body>
</html>
\ No newline at end of file
import { Camera, Matrix4, Object3D, Scene, Vector3 } from 'three'
type CSS2DObjctInfer = {
element?: HTMLElement,
isCSS2DObject?: boolean,
} & Object3D
export class CSS2DObjct extends Object3D {
isCSS2DObject: boolean = true
element: HTMLElement
constructor(element: HTMLElement=document.createElement('div'), style?: Partial<CSSStyleDeclaration>, attributes?: any) {
super()
this.element = element
this.element.style.position = 'absolute'
this.element.style.userSelect = 'none'
this.element.setAttribute('draggle', 'false')
if (style) {
for (var k in style) {
this.element.style[k] = style[k]
}
}
if (attributes) {
for (var k in attributes) {
// @ts-expect-error
this.element[k] = attributes[k]
}
}
this.addEventListener('removed', () => {
this.traverse(function(obj: any) {
if (obj.element instanceof Element && obj.element.parentNode !== null) {
obj.element.parentNode.removeChild(obj.element)
}
})
})
}
copy(source: this, recursive: boolean) {
super.copy(source, recursive)
// @ts-expect-error
this.element = source.element.cloneNode(true)
return this
}
}
const _vector = new Vector3();
const _viewMatrix = new Matrix4();
const _viewProjectionMatrix = new Matrix4();
const _a = new Vector3();
const _b = new Vector3();
interface CSS2DRendererProps {
element?: HTMLElement
}
export class CSS2DRenderer {
domElement: HTMLElement
render: (scene: any, camera: any) => void
setSize: (width: any, height: any) => void
constructor( parameters: CSS2DRendererProps = {} ) {
const _this = this;
let _width: number, _height: number;
let _widthHalf: number, _heightHalf: number;
const cache = {
objects: new WeakMap()
};
const domElement = parameters.element !== undefined ? parameters.element : document.createElement( 'div' );
domElement.style.overflow = 'hidden';
domElement.style.pointerEvents = 'none'
this.domElement = domElement;
// this.getSize = function () {
// return {
// width: _width,
// height: _height
// };
// };
this.render = function ( scene, camera ) {
if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
if ( camera.parent === null ) camera.updateMatrixWorld();
_viewMatrix.copy( camera.matrixWorldInverse );
_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
renderObject( scene, scene, camera );
zOrder( scene );
};
this.setSize = function ( width, height ) {
_width = width;
_height = height;
_widthHalf = _width / 2;
_heightHalf = _height / 2;
domElement.style.width = width + 'px';
domElement.style.height = height + 'px';
};
function renderObject( object: CSS2DObjctInfer, scene: Scene, camera: Camera ) {
if ( object.isCSS2DObject ) {
// console.log(object)
// object.onBeforeRender( _this, scene, camera );
_vector.setFromMatrixPosition( object.matrixWorld );
_vector.applyMatrix4( _viewProjectionMatrix );
const element = object.element;
if ( /apple/i.test( navigator.vendor ) ) {
// https://github.com/mrdoob/three.js/issues/21415
element.style.transform = 'translate(-50%,-50%) translate(' + Math.round( _vector.x * _widthHalf + _widthHalf ) + 'px,' + Math.round( - _vector.y * _heightHalf + _heightHalf ) + 'px)';
} else {
element.style.transform = 'translate(-50%,-50%) translate(' + ( _vector.x * _widthHalf + _widthHalf ) + 'px,' + ( - _vector.y * _heightHalf + _heightHalf ) + 'px)';
}
element.style.display = ( object.visible && _vector.z >= - 1 && _vector.z <= 1 ) ? '' : 'none';
const objectData = {
distanceToCameraSquared: getDistanceToSquared( camera, object )
};
cache.objects.set( object, objectData );
if ( element.parentNode !== domElement ) {
domElement.appendChild( element );
}
// object.onAfterRender( _this, scene, camera );
}
for ( let i = 0, l = object.children.length; i < l; i ++ ) {
renderObject( object.children[i], scene, camera );
}
}
function getDistanceToSquared( object1: Camera, object2: CSS2DObjctInfer ) {
_a.setFromMatrixPosition( object1.matrixWorld );
_b.setFromMatrixPosition( object2.matrixWorld );
return _a.distanceToSquared( _b );
}
function filterAndFlatten( scene: { traverse: (arg0: (object: any) => void) => void } ) {
const result: any[] = [];
scene.traverse( function ( object: { isCSS2DObject: any } ) {
if ( object.isCSS2DObject ) result.push( object );
} );
return result;
}
function zOrder( scene: any ) {
const sorted = filterAndFlatten( scene ).sort( function ( a, b ) {
const distanceA = cache.objects.get( a ).distanceToCameraSquared;
const distanceB = cache.objects.get( b ).distanceToCameraSquared;
return distanceA - distanceB;
} );
const zMax = sorted.length;
for ( let i = 0, l = sorted.length; i < l; i ++ ) {
sorted[ i ].element.style.zIndex = zMax - i;
}
}
}
}
\ No newline at end of file
This diff is collapsed.
import * as THREE from 'three'
import { CSS2DObjct, CSS2DRenderer } from './CSS2DRender'
import { OrbitControls } from './OrbitControls'
export class EarthAndMoon {
scene: THREE.Scene
camera: THREE.PerspectiveCamera
renderer: THREE.Renderer
twoDRenderer: CSS2DRenderer
worldWidth: number
worldHeight: number
textureLoader: THREE.TextureLoader
clock: THREE.Clock
moon: THREE.Mesh
constructor() {
this.textureLoader = new THREE.TextureLoader()
this.clock = new THREE.Clock()
this.initScene()
this.initCamera()
this.initGeometry()
this.initRenderer()
this.initEvents()
this.loop()
}
/**
* 初始化场景
*/
initScene() {
this.worldHeight = window.innerHeight
this.worldWidth = window.innerWidth
const scene = this.scene = new THREE.Scene()
// 创建 平行光, Three 里面的 光也会是有着很多种的, 比如 PointLight 点光源, DirectionalLight 平行光, SpotLight 聚光灯, AmbientLight 环境光
// 这里的 是模拟太阳光 平行光就 十分合适
const dirLight = new THREE.DirectionalLight(0xffffff)
dirLight.position.set(1, 1, 1)
scene.add(dirLight)
// 创建 debug的 坐标
scene.add(new THREE.AxesHelper(5))
// 创建光源debug
scene.add(new THREE.DirectionalLightHelper(dirLight))
}
/**
* 初始化相机
*/
initCamera() {
// 投影矩阵
// THREE.OrthographicCamera()
this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200)
this.camera.position.set(10, 5, 20)
this.camera.lookAt(this.scene.position)
}
/**
* 初始化渲染器
*/
initRenderer() {
// 创建WebGL渲染器
const renderer = this.renderer = new THREE.WebGLRenderer()
renderer.setPixelRatio( window.devicePixelRatio );
this.renderer.setSize(this.worldWidth, this.worldHeight)
// 把renderer的 domElement 添加到 网页中
document.body.appendChild(this.renderer.domElement)
// 创建 CSS2D 渲染器
// CSS2DRenderer 这里面主要会有一个 render方法 通过入参 scene和 camera 来对 特定的Dom进行CSS渲染设置
// const _css2dRenderer = this.twoDRenderer = new CSS2DRenderer()
// _css2dRenderer.setSize( window.innerWidth, window.innerHeight );
// _css2dRenderer.domElement.style.position = 'absolute';
// _css2dRenderer.domElement.style.top = '0px';
// document.body.appendChild( _css2dRenderer.domElement );
// OrbitControls Three 自带的 鼠标控制器,这里的入参就是 我们的相机
// const controls = new OrbitControls(this.camera, this.renderer.domElement)
// controls.minDistance = 5;
// controls.maxDistance = 100;
}
/**
* 初始化事件
*/
initEvents() {
window.addEventListener( 'resize', () => {
// 计算 camera的角度值
this.camera.aspect = window.innerWidth / window.innerHeight;
// 更新 camerade 的投影矩阵
this.camera.updateProjectionMatrix();
this.renderer.setSize( window.innerWidth, window.innerHeight );
// this.twoDRenderer.setSize( window.innerWidth, window.innerHeight );
});
}
/**
* 初始化几何体
*/
initGeometry() {
this.initEarthGeometry()
this.initMoonGeometry()
}
/**
* 初始化地球几何体
*/
initEarthGeometry() {
const textureLoader = this.textureLoader
// 创建球体
const earthGeometry = new THREE.SphereGeometry(1, 16, 16)
// MeshPhongMaterial 高光纹理,可以进行光照的漫反射和镜面反射
const earthMaterial = new THREE.MeshPhongMaterial({
specular: 0x333333, // 反光
shininess: 5, // 反光范围
map: textureLoader.load('./earth_atmos_2048.jpg'), // 地球上的贴图
specularMap: textureLoader.load( './earth_specular_2048.jpg' ),
normalMap: textureLoader.load( './earth_normal_2048.jpg' ),
normalScale: new THREE.Vector2( 0.85, 0.85 )
})
// 创建地球 THREE.Mesh 入参就是 几何体和 纹理
const earthMesh = new THREE.Mesh(earthGeometry, earthMaterial)
this.scene.add(earthMesh)
// CSS2DObjct 继承 Object3D 所以就可以 通过 add方法 添加到场景中,这里就直接加入到 earthMesh中 后面就可以通过
// twoDRenderer 进行渲染, 因为这里面会有着 isCSS2DObject 来进行区分
// const earthLabel = new CSS2DObjct(document.createElement('div'), {
// marginTop: '-1em',
// color: '#fff',
// }, {
// className: 'label',
// textContent: 'Earth'
// })
// earthLabel.position.set( 0, 1, 0 );
// earthMesh.add(earthLabel)
}
/**
* 初始化月球几何体
*/
initMoonGeometry() {
// 下面就基本和 地球是一致的
const textureLoader = this.textureLoader
const moonGeometry = new THREE.SphereGeometry(0.75, 16, 16)
const moonMaterial = new THREE.MeshPhongMaterial({
shininess: 5, // 反光范围
map: textureLoader.load('./moon_1024.jpg'),
})
const moonMesh = this.moon = new THREE.Mesh(moonGeometry, moonMaterial)
moonMesh.position.x = 3
this.scene.add(moonMesh)
// const moonLabel = new CSS2DObjct(document.createElement('div'), {marginTop: '-1em',
// color: '#fff',}, {
// className: 'label',
// textContent: 'Moon'
// })
// moonLabel.position.set(0, 0.75, 0)
// moonMesh.add(moonLabel)
}
/**
* 帧回调
*/
loop() {
const elapsed = this.clock.getElapsedTime();
// 计算月球的位置
this.moon.position.set( Math.sin( elapsed ) * 5, 0, Math.cos( elapsed ) * 5 );
this.renderer.render(this.scene, this.camera)
// this.twoDRenderer.render(this.scene, this.camera)
requestAnimationFrame(this.loop.bind(this))
}
}
new EarthAndMoon()
\ No newline at end of file
{
"compilerOptions": {
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"lib": [
"DOM",
"ES2015",
"ES5"
],
"sourceMap": true,
"noEmitOnError": true,
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": false,
"esModuleInterop": true,
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
},
"exclude": [
"node_modules"
]
}
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: {
index: './src/main.ts'
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /(node_modules)/,
use: [
{
loader: 'ts-loader',
}
],
exclude: /node_modules/
}
],
},
resolve: {
alias: {
'@': './src',
},
extensions: ['.ts', '.js', 'tsx']
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist')
},
devServer: {
port: '0.0.0.0',
hot: true,
open: true,
port: 9015
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment