首页 > 编程语言 >运用JavaScript代码,使用Three.js框架在网页中实现3D效果,零基础入门Three.js,包含具体实例。

运用JavaScript代码,使用Three.js框架在网页中实现3D效果,零基础入门Three.js,包含具体实例。

时间:2024-06-01 09:59:21浏览次数:9  
标签:const ca JavaScript Three js new THREE Math


在这里插入图片描述

不经意间看到了某个大佬做的网站~ 实在是太帅啦!
查了查实现该效果的技术 —— 原来是Three.js
如果你也感兴趣的话,那就让我们来从零开始学习Three.js动态3D效果吧✨


一、了解

Three.js是基于原生WebGL封装运行的三维引擎,在所有WebGL引擎中,Three.js是国内文资料最多、使用最广泛的三维引擎。

Threejs是一款WebGL三维引擎,它可以用来做许多许多地场景应用

示例

在这里插入图片描述

那么不热爱前端的你,是否会因为这张图片而热爱呢?
没错,这张图片就是用Three.js框架实现的。

二、安装

本教程采用的安装方法是第一种,所以需要大家将Three.js文件下载到本地,添加到自己的项目中。

1.本地安装

创建Three.Js文件夹
创建index.html文件
创建js文件夹 将下载的three.js包放入js文件夹中


在这里插入图片描述

百度网盘链接:

链接: https://pan.baidu.com/s/1pSA-txD1hA7SknK0_htTvg?pwd=cad6
提取码:cad6

网盘里有Three.js文件,大家可以直接使用,如果大家想使用Three.js框架的其他模块,可自行去GitHub上下载,地址在下面:

官方GitHub地址: https://github.com/mrdoob/three.js/tree/master

2.使用 NPM 和构建工具进行安装

注:这种方法对新手十分不友好,新手建议使用第一种方法

由于项目需要的依赖越多,就越有可能遇到静态托管无法轻易解决的问题。而使用构建工具,导入本地 JavaScript 文件和 npm 软件包将会解决问题,无需导入映射(import maps)。

  1. 安装 Node.js。我们需要它来管理依赖项和运行构建工具。
  2. 在项目文件夹中通过 终端 安装 three.js 和构建工具 Vite。Vite将在开发过程中使用,但不会被打包成为最终网页的一部分。当然,除了 Vite 你也可以使用其他支持导入 ES Modules的现代构建工具。
# three.js
npm install --save three

# vite
npm install --save-dev vite
  1. 在终端运行:
npx vite

4.如果一切顺利,你会在终端中看到一个类似 http://localhost:5173 的 URL,打开该 URL 就能看到你的网络应用程序。

之后,当你准备部署网络应用程序时,只需要让 Vite 运行生产构建 - npx vite build。应用程序使用的所有内容都将被编译、优化并复制到 dist/ 文件夹中。该文件夹中的内容就可以托管到你的网站上了。

3.从CDN导入

  • 在 index.html 中,我们需要添加一个导入映射(import map)来定义从哪里获取软件包。将下面的代码放在
    <head></head> 标签内、样式(styles)之后。
<script type="importmap">
  {
    "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@<version>/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/three@<version>/examples/jsm/"
    }
  }
</script>
  • 不要忘记将上述链接中的 替换为 three.js 的实际版本,如 “v0.149.0”。最新版本可在 npm
    版本列表中找到。

三、基础教程

这一部分将对 three.js 来做一个简要的介绍;我们将开始搭建一个场景,其中包含一个正在旋转的立方体。

创建一个场景

为了真正能够让你的场景借助 three.js 来进行显示,我们需要以下几个对象:场景(scene)、相机(camera)和渲染器(renderer),这样我们就能透过摄像机渲染出场景。

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

上面是一段js代码,在上面的代码中,我们建立了场景、相机和渲染器。

three.js 里有几种不同的相机,在这里,我们使用的是 PerspectiveCamera(透视摄像机)。

第一个参数是视野角度(FOV)。视野角度就是无论在什么时候,你所能在显示器上看到的场景的范围,它的单位是角度(与弧度区分开)。

第二个参数是长宽比(aspect ratio)。 也就是你用一个物体的宽除以它的高的值。比如说,当你在一个宽屏电视上播放老电影时,可以看到图像仿佛是被压扁的。

接下来的两个参数是近截面(near)和远截面(far)。 当物体某些部分比摄像机的远截面远或者比近截面近的时候,这些部分将不会被渲染到场景中。或许现在你不用担心这个值的影响,但未来为了获得更好的渲染性能,你将可以在你的应用程序里去设置它。

接下来是渲染器。这里是施展魔法的地方。除了我们在这里用到的 WebGLRenderer 渲染器之外,Three.js 同时提供了其他几种渲染器。

除了创建一个渲染器的实例之外,我们还需要在我们的应用程序里设置一个渲染器的尺寸。比如说,我们可以使用所需要的渲染区域的宽高,来让渲染器渲染出的场景填充满我们的应用程序。因此,我们可以将渲染器宽高设置为浏览器窗口宽高。

如果你希望保持你的应用程序的尺寸,但是以较低的分辨率来渲染,你可以在调用 setSize 时,将 updateStyle(第三个参数)设为 false。例如,假设你的 <canvas> 标签现在已经具有了 100% 的宽和高,调用 setSize(window.innerWidth/2, window.innerHeight/2, false) 将使得你的应用程序以四分之一的大小来进行渲染。

最后一步很重要,我们将 renderer(渲染器)的dom元素(renderer.domElement)添加到我们的 HTML 文档中。这就是渲染器用来显示场景给我们看的 <canvas> 元素。

“嗯,看起来很不错,那你说的那个立方体在哪儿?”

接下来,我们就来添加立方体。

const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );

camera.position.z = 5;

要创建一个立方体,我们需要一个 BoxGeometry(立方体)对象. 这个对象包含了一个立方体中所有的顶点(vertices)和面(faces)。

接下来,对于这个立方体,我们需要给它一个材质,来让它有颜色。Three.js 自带了几种材质,在这里我们使用的是 MeshBasicMaterial。所有的材质都存有应用于他们的属性的对象。在这里为了简单起见,我们只设置一个color属性,值为 0x00ff00,也就是绿色。这里所做的事情,和在 CSS 或者 Photoshop 中使用十六进制(hex colors)颜色格式来设置颜色的方式一致。

第三步,我们需要一个 Mesh(网格)。 网格包含一个几何体以及作用在此几何体上的材质,我们可以直接将网格对象放入到我们的场景中,并让它在场景中自由移动。

默认情况下,当我们调用 scene.add() 的时候,物体将会被添加到 (0,0,0) 坐标。但将使得摄像机和立方体彼此在一起。为了防止这种情况的发生,我们只需要将摄像机稍微向外移动一些即可。

渲染场景

现在,如果将之前写好的代码复制到HTML文件中,你不会在页面中看到任何东西。这是因为我们还没有对它进行真正的渲染。为此,我们需要使用一个被叫做“**渲染循环”(render loop)**或者“动画循环”(animate loop)的东西。

function animate() {
	requestAnimationFrame( animate );
	renderer.render( scene, camera );
}
animate();

在这里我们创建了一个使渲染器能够在每次屏幕刷新时对场景进行绘制的循环(在大多数屏幕上,刷新率一般是60次/秒)。

使立方体动起来

在开始之前,如果你已经将上面的代码写入到了你所创建的文件中,你可以看到一个绿色的方块。让我们来做一些更加有趣的事 —— 让它旋转起来。

将下列代码添加到 animate() 函数中 renderer.render 调用的上方:

cube.rotation.x += 0.01;
cube.rotation.y += 0.01;

这段代码每帧都会执行(正常情况下是60次/秒),这就让立方体有了一个看起来很不错的旋转动画。基本上来说,当应用程序运行时,如果你想要移动或者改变任何场景中的东西,都必须要经过这个动画循环。

结果

祝贺你!你现在已经成功完成了你的第一个 three.js 应用程序。

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>My first three.js app</title>
		<script src="js/three.js"></script>
		<style>
			body {
				margin: 0;
			}
		</style>
	</head>
	<body>
		<script>
			const scene = new THREE.Scene();
			const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

			const renderer = new THREE.WebGLRenderer();
			renderer.setSize(window.innerWidth, window.innerHeight);
			document.body.appendChild(renderer.domElement);

			const geometry = new THREE.BoxGeometry(1, 1, 1);
			const material = new THREE.MeshBasicMaterial({
				color: 0x00ff00
			});
			const cube = new THREE.Mesh(geometry, material);
			scene.add(cube);

			camera.position.z = 5;

			function animate() {
				requestAnimationFrame(animate);

				cube.rotation.x += 0.01;
				cube.rotation.y += 0.01;

				renderer.render(scene, camera);
			}

			animate();
		</script>
	</body>
</html>

在这里插入图片描述

四、其他示例

示例1:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="js/three.js"></script>
		<style type="text/css">
			body {
				background-color: #222;
			}

			canvas {
				width: 100%;
				height: 100%;
			}
		</style>
	</head>
	<body>
	</body>
	<script type="text/javascript">
		var scene = new THREE.Scene();
		var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
		var renderer = new THREE.WebGLRenderer();

		renderer.setSize(window.innerWidth, window.innerHeight);
		document.body.appendChild(renderer.domElement);

		var spotLight = new THREE.SpotLight(0xffffff);
		spotLight.position.set(100, 100, 100);
		spotLight.castShadow = true; 
		spotLight.shadowMapWidth = 1024;
		spotLight.shadowMapHeight = 1024;
		spotLight.shadowCameraNear = 500;
		spotLight.shadowCameraFar = 4000;
		spotLight.shadowCameraFov = 30;
		scene.add(spotLight);

		function Mat() {
			var material = new THREE.MeshPhongMaterial({
				color: new THREE.Color("rgb(35,35,213)"),
				emissive: new THREE.Color("rgb(64,128,255)"),
				specular: new THREE.Color("rgb(93,195,255)"),

				shininess: 1,
				shading: THREE.FlatShading,
				wireframe: 1,
				transparent: 1,
				opacity: 0.15
			});
			return material;
		}

		var geometry = new THREE.SphereGeometry(50, 20, 20, 0, Math.PI * 2, 0, Math.PI);

		var earth = new THREE.Mesh(geometry, Mat());

		scene.add(earth);

		camera.position.z = 90;

		function render() {
			requestAnimationFrame(render);
			earth.rotation.x += 0.01;
			earth.rotation.y += 0.01;
			renderer.render(scene, camera);
		}
		render();
	</script>
</html>

示例二(JavaScript编写):

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
	</body>
	<script type="text/javascript">
		"use strict"; 
		const body = document.getElementsByTagName("body").item(0);
		body.style.background = "#000";
		const TP = 2 * Math.PI;
		const CSIZE = 400;

		const ctx = (() => {
			let d = document.createElement("div");
			d.style.textAlign = "center";
			body.append(d);
			let c = document.createElement("canvas");
			c.width = c.height = 2 * CSIZE;
			d.append(c);
			return c.getContext("2d");
		})();
		ctx.translate(CSIZE, CSIZE);
		ctx.lineCap = "round";

		onresize = () => {
			let D = Math.min(window.innerWidth, window.innerHeight) - 40;
			ctx.canvas.style.width = ctx.canvas.style.height = D + "px";
		}

		const getRandomInt = (min, max, low) => {
			if (low) return Math.floor(Math.random() * Math.random() * (max - min)) + min;
			else return Math.floor(Math.random() * (max - min)) + min;
		}

		function Color() {
			const CBASE = 144;
			const CT = 255 - CBASE;
			this.getRGB = (ct) => {
				let red = Math.round(CBASE + CT * (Math.cos(this.RK2 + ct / this.RK1)));
				let grn = Math.round(CBASE + CT * (Math.cos(this.GK2 + ct / this.GK1)));
				let blu = Math.round(CBASE + CT * (Math.cos(this.BK2 + ct / this.BK1)));
				return "rgb(" + red + "," + grn + "," + blu + ")";
			}
			this.randomize = () => {
				this.RK1 = 360 + 360 * Math.random();
				this.GK1 = 360 + 360 * Math.random();
				this.BK1 = 360 + 360 * Math.random();
				this.RK2 = TP * Math.random();
				this.GK2 = TP * Math.random();
				this.BK2 = TP * Math.random();
			}
			this.randomize();
		}

		var color = new Color();

		var stopped = true;
		var start = () => {
			if (stopped) {
				stopped = false;
				requestAnimationFrame(animate);
			} else {
				stopped = true;
			}
		}
		body.addEventListener("click", start, false);

		var dur = 2000;
		var tt = getRandomInt(0, 1000);
		var pause = 0;
		var trans = false;
		var animate = (ts) => {
			if (stopped) return;
			tt++;
			draw();
			requestAnimationFrame(animate);
		}

		var KF = Math.random();

		var Circle = function() {
			this.dir = false;
			this.r = 80;
			this.randomize = () => {
				this.ka = 0; //TP*Math.random();
				this.ka2 = 200;
			}
			this.randomize();
			this.setRA = () => {
				this.a2 = TP / 4 + 1.57 * (1 + Math.sin(tt / this.ka2)) / 2;
				if (this.dir) this.a2 = -this.a2;
			}
			this.setPath2 = () => {
				if (this.p) {
					if (this.cont) {
						this.a = TP / 2 + this.p.a - this.p.a2;
						this.x = this.p.x + (this.p.r - this.r) * Math.cos(this.p.a - this.p.a2);
						this.y = this.p.y + (this.p.r - this.r) * Math.sin(this.p.a - this.p.a2);
					} else {
						this.a = this.p.a - this.p.a2;
						this.x = this.p.x + (this.p.r + this.r) * Math.cos(this.p.a - this.p.a2);
						this.y = this.p.y + (this.p.r + this.r) * Math.sin(this.p.a - this.p.a2);
					}
				} else {
					this.x = this.r * Math.cos(this.a);
					this.y = this.r * Math.sin(this.a);
				}
				this.path = new Path2D();
				this.path.arc(this.x, this.y, this.r, TP / 2 + this.a, this.a - this.a2, this.dir);
			}
		}

		onresize();

		var ca = [];
		var reset = () => {
			ca = [new Circle()];
			ca[0].p = false;
			ca[0].a = 0; //TP*Math.random();
			ca[0].setRA();
			ca[0].x = ca[0].r * Math.cos(ca[0].a);
			ca[0].y = ca[0].r * Math.sin(ca[0].a);
		}
		reset();

		var addCircle = (c) => {
			let c2 = new Circle();
			c2.a = c.a - c.a2;
			c2.dir = !c.dir;
			c2.p = c;
			c2.cont = false;
			c2.r = c.r * 0.8;
			if (Math.random() < KF)
				c2.ka2 = c.ka2 * 0.8;
			ca.push(c2);
			let c3 = new Circle();
			c3.a = TP / 2 + c.a - c.a2;
			c3.dir = c.dir;
			c3.p = c;
			c3.cont = true;
			c3.r = c.r * 0.8;
			if (Math.random() < 1 - KF)
				c3.ka2 = c.ka2 * 0.8;
			ca.push(c3);
		}

		const dmx = new DOMMatrix([-1, 0, 0, 1, 0, 0]);
		const dmy = new DOMMatrix([1, 0, 0, -1, 0, 0]);
		const dmxy = new DOMMatrix([-1, 0, 0, -1, 0, 0]);
		//const dmq=new DOMMatrix([0,1,-1,0,0,0]);
		const getXYPath = (spath) => {
			this.level = 1;
			let p = new Path2D(spath);
			p.addPath(p, dmxy);
			return p;
		}

		var draw = () => {
			ca[0].a = tt / 1000;
			for (let i = 0; i < ca.length; i++) ca[i].setRA();
			for (let i = 0; i < ca.length; i++) ca[i].setPath2();
			let pa = new Array(8);
			for (let i = 0; i < pa.length; i++) pa[i] = new Path2D();
			for (let i = 0; i < ca.length; i++) {
				if (i == 0) pa[0].addPath(ca[i].path);
				else if (i < 3) pa[1].addPath(ca[i].path);
				else if (i < 7) pa[2].addPath(ca[i].path);
				else if (i < 15) pa[3].addPath(ca[i].path);
				else if (i < 31) pa[4].addPath(ca[i].path);
				else if (i < 63) pa[5].addPath(ca[i].path);
				else if (i < 127) pa[6].addPath(ca[i].path);
				else pa[7].addPath(ca[i].path);
			}
			ctx.clearRect(-CSIZE, -CSIZE, 2 * CSIZE, 2 * CSIZE);

			for (let i = 0; i < pa.length; i++) {
				let pth = getXYPath(pa[i]);
				ctx.strokeStyle = color.getRGB(tt - 180 * i);
				ctx.lineWidth = Math.max(3, 24 - 3 * i);
				ctx.stroke(pth);
			}
		}

		for (let i = 0; i < 127; i++) addCircle(ca[i]);

		start();
	</script>
</html>

在这里插入图片描述
在这里插入图片描述

标签:const,ca,JavaScript,Three,js,new,THREE,Math
From: https://blog.csdn.net/weixin_49837118/article/details/139363827

相关文章

  • 1v1视频软件源码,通过jsonp跨域的代码分析
    1v1视频软件源码,通过jsonp跨域的代码分析通常为了减轻1v1视频软件源码web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实......
  • JS设计模式学习【待吸收】
    版权:千锋HTML5大前端教研院,源自大前端私房菜JS设计模式一.设计模式介绍设计模式是我们在解决问题的时候针对特定问题给出的简洁而优化的处理方案在JS设计模式中,最核心的思想:封装变化。将变与不变分离,确保变化的部分灵活、不变的部分稳定。二.构造器模式类似......
  • Nginx 实战-02-nginx proxy_pass 服务代理访问 使用笔记 ubuntu nodejs
    前言大家好,我是老马。很高兴遇到你。我们为java开发者实现了java版本的nginxhttps://github.com/houbb/nginx4j如果你想知道servlet如何处理的,可以参考我的另一个项目:手写从零实现简易版tomcatminicat手写nginx系列如果你对nginx原理感兴趣,可以阅读:从零......
  • oidc-client.js踩坑吐槽贴
    前言前面选用了IdentityServer4做为认证授权的基础框架,感兴趣的可以看上篇<微服务下认证授权框架的探讨>,已经初步完成了authorization-code与implicit的简易demo(html+js在IIS部署的站点),并实现了SSO,本想着将Demo迁移到vue工程是轻而易举,毕竟也没啥东西,最终拿到access_tok......
  • JavaScript语法(二):你知道哪些JavaScript语句?
    我们在上一节课中已经讲过了JavaScript语法的顶层设计,接下来我们进入到更具体的内容。JavaScript遵循了一般编程语言的“语句-表达式”结构,多数编程语言都是这样设计的。我们在上节课讲的脚本,或者模块都是由语句列表构成的,这一节,我们就来一起了解一下语句。在JavaScrip......
  • JavaScript语法(四):新加入的**运算符,哪里有些不一样呢?
    上一节我们已经给你介绍了表达式的一些结构,其中关于赋值表达式,我们讲完了它的左边部分,而留下了它右边部分,那么,我们这节课一起来详细讲解。在一些通用的计算机语言设计理论中,能够出现在赋值表达式右边的叫做:右值表达式(RightHandSideExpression),而在JavaScript标准中,规定了在等......
  • JavaScript执行(四):try里面放return,finally还会执行吗?
    在前面几篇文章中,我们已经了解了关于执行上下文、作用域、闭包之间的关系。今天,我们则要说一说更为细节的部分:语句。语句是任何编程语言的基础结构,与JavaScript对象一样,JavaScript语句同样具有“看起来很像其它语言,但是其实一点都不一样”的特点。我们比较常见的语句包括......
  • JavaScript执行(三):你知道现在有多少种函数吗?
    在前一篇文章中,我们大致了解了执行上下文是什么,也知道了任何语句的执行都会依赖特定的上下文。一旦上下文被切换,整个语句的效果可能都会发生改变。那么,切换上下文的时机就显得非常重要了。在JavaScript,切换上下文最主要的场景是函数调用。在这一课,我们就来讲讲函数调用切换......
  • 详解 JS 中的事件循环、宏/微任务、Primise对象、定时器函数,以及其在工作中的应用和注
    为什么会突然想到写这么一个大杂烩的博文呢,必须要从笔者几年前的一次面试说起当时的我年轻气盛,在简历上放了自己的博客地址,而面试官应该是翻了我的博客,好几道面试题都是围绕着我的博文来提问其中一个问题,直接使得空气静止了五分钟,然后面试官结束了这次面试,那就是:如何手写一个简......
  • const filePath = fileURLToPath(new URL('plopfile.js', import.meta.url)); 解释一
    这段代码的作用是获取当前文件所在目录下的plopfile.js文件的绝对路径。这里是逐步解释:import.meta.url:这是ESModules中的一个元属性,它提供了当前模块的绝对URL。在Node.js环境中,当你在一个模块文件中访问import.meta.url时,它会返回该模块文件的文件系统路径转换成的URL格......