分享HTML显示2D/3D时间

news2024/12/23 23:04:42

效果截图

实现代码

<!DOCTYPE html>
<head>
<title>three.js+cannon.js Web 3D</title>
    <meta charset="utf-8">
	<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
	<meta name="renderer" content="webkit">
	<meta name="force-rendering" content="webkit">
	<meta http-equiv="X-UA-Compatible" content="IE=10,chrome=1">
	<meta data-rh="true" name="keywords" content="three.js,JavaScript,cannon.js">
	<meta data-rh="true" name="description" content="three.js+cannon.js Web 3D">
	<meta data-rh="true" property="og:title" content="THREE.JS and CANNON.JS">
	<meta data-rh="true" property="og:url" content="">
	<meta data-rh="true" property="og:description" content="three.js+cannon.js Web 3D">
	<meta data-rh="true" property="og:image" content="">
	<meta data-rh="true" property="og:type" content="article">
	<meta data-rh="true" property="og:site_name" content="">
    <link rel="icon" href="">
    
    <style>
	    .fullscreen {
		  margin: 0px;
		  padding: 0px;
		  width: 100vw;
		  height: 100vh;
		  overflow: hidden;
		}
 
		html, body {
		  overflow: hidden;
		  font-family: '宋体', sans-serif;
		  color: #2f2f2f;
		}
		
		#threeImage{
		  display: none;
		}
		
	</style>
 
	<script type="importmap">
    {
      "imports": {
        "three": "https://cdn.jsdelivr.net/npm/three@0.162.0/+esm",
        "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.162.0/examples/jsm/",
		"lil-gui": "https://threejsfundamentals.org/3rdparty/dat.gui.module.js",
        "@tweenjs/tween.js": "https://cdn.jsdelivr.net/npm/@tweenjs/tween.js@23.1.1/dist/tween.esm.js",
		"cannonjs": "https://cdn.bootcdn.net/ajax/libs/cannon.js/0.6.2/cannon.min.js"
      }
    }
  </script>
 
  </head>
 
 
  <body class="fullscreen">
    
	<div id="threeImage" >
	     <img id="testImage" src=""  crossorigin="anonymous"/>
	</div>
      
    <canvas></canvas>
 
	<script type="module">
	
	    import * as THREE from 'three';
		import * as TWEEN from '@tweenjs/tween.js';
		import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
        import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
		import { GUI } from 'lil-gui';
		import 'cannonjs';
		
		let container;
		let scene, camera, renderer, controls;
		let NEAR = 0.1, FAR = 1000;
		let light, ambient, stats, info, mouseX, mouseY;
		let SHADOW_MAP_WIDTH = 512;
		let SHADOW_MAP_HEIGHT = 512;
		let MARGIN = 0;
		let SCREEN_WIDTH = window.innerWidth;
		let SCREEN_HEIGHT = window.innerHeight - 2 * MARGIN;
		//物理世界
		let world;
		// 创建默认材质
        let defaultMaterial = null
        //创建材质
        let bMaterial = null
		let cube, tempCubes = new THREE.Object3D();
		let color = "#12345678";

		function setup() {
		  setupScene();
		  setupCamera();
		  setupRenderer();
		  setupLights();
		  setPlane();
		  setControls();
		  initCannon();
		  setupEventListeners();
		  animate();
		  addGUI();
		}
		
		function setupScene() {
		  scene = new THREE.Scene();
		  scene.fog = new THREE.Fog( 0x222222, 1000, FAR );
		  scene.background  = new THREE.Color(0xf5e6d3);
		}
 
		function setupCamera() {
		  let res = SCREEN_WIDTH / SCREEN_HEIGHT;
		  camera = new THREE.PerspectiveCamera(45, res, NEAR, FAR);
		  //camera.up.set(0,0,1);
		  //camera.position.set(0,30,20);
		  //camera.position.z = 19;
		  //camera.position.y = -45;
		  
		  camera.position.set(0, 30, 30)
		  //camera.lookAt(0, 0, 0);
		  //camera.lookAt(new THREE.Vector3(0, 0, 0));
		}
		
		let rotWorldMatrix;
		function rotateAroundWorldAxis(object, axis, radians) {
		    //object:需要旋转的物体,axis:旋转中心轴向量,radians:
			rotWorldMatrix = new THREE.Matrix4();
			rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);
			rotWorldMatrix.multiply(object.matrix);          
			// pre-multiply
			object.matrix = rotWorldMatrix;
			object.rotation.setFromRotationMatrix(object.matrix);
		}
 
		function setupRenderer() {
		  renderer = new THREE.WebGLRenderer({ 
		      clearColor: 0x000000, 
		      clearAlpha: 1, 
		      antialias:  true,// 抗锯齿
		      logarithmicDepthBuffer: true // 使用Three进行加载模型时,总会遇到模型相接处或某些区域出现频闪问题或内容被相邻近元素覆盖掉的情况,对数缓存开启可解决,使用对数缓存
		      });
		  renderer.setSize(window.innerWidth, window.innerHeight);
		  //renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
		  renderer.setClearColor( scene.fog.color, 1 );
		  renderer.autoClear = false;
		  renderer.shadowMapEnabled = true;
		  renderer.shadowMapSoft = true;
		  //开启阴影效果 设置阴影类型
          // BasicShadowMap 能够给出没有经过过滤的阴影映射 —— 速度最快,但质量最差。
          // PCFShadowMap 为默认值,使用Percentage-Closer Filtering (PCF)算法来过滤阴影映射。
          // PCFSoftShadowMap 和PCFShadowMap一样使用 Percentage-Closer Filtering (PCF)算法过滤阴影映射,但在使用低分辨率阴影图时具有更好的软阴影。
          // VSMShadowMap 使用Variance Shadow Map (VSM)算法来过滤阴影映射。当使用VSMShadowMap时,所有阴影接收者也将会投射阴影
    	  renderer.shadowMap.enabled = true;
    	  renderer.shadowMap.type = THREE.BasicShadowMap
    	  renderer.shadowMap.autoUpdate = false
    	  renderer.shadowMap.needsUpdate = true
		  // 是否使用传统照明模式,默认为是,关闭传统照明模式即可模仿物理光照,光亮随距离可递减
	      renderer.useLegacyLights = false;
	      // 设置色调映射
	      renderer.toneMapping = THREE.ACESFilmicToneMapping;
	      // 曝光强度
	      renderer.toneMappingExposure = 0.8
	      renderer.outputEncoding = THREE.sRGBEncoding;
  
		  container = document.createElement( 'div' );
		  document.body.appendChild( container );
		  //document.body.appendChild(renderer.domElement);
		  //renderer.domElement.style.position = "relative";
		  //renderer.domElement.style.top = MARGIN + 'px';
		  container.appendChild( renderer.domElement );
		}
		
		function setPlane(){
		    let planeGeometry = new THREE.PlaneGeometry(60, 20);
    		let planeMaterial = new THREE.MeshBasicMaterial({color: 0xffffff});
    		let plane = new THREE.Mesh(planeGeometry, planeMaterial);
    		// 几何体绕着x轴旋转-90度
    		plane.rotateX(-Math.PI/2);
    		// 设置平面网格为接受阴影的投影面
    		plane.receiveShadow = true;
    		
    		scene.add(plane);
		}
 
		function setupLights() {
		  let ambientLight = new THREE.AmbientLight(0xffffff);
		  ambientLight.castShadow = true;
		  scene.add(ambientLight);
		  
		  // LIGHTS
		  ambient = new THREE.AmbientLight( 0xffffff );
		  ambient.castShadow = true;
		  scene.add( ambient );
		  
		  // 添加聚光灯1
          addSpotlight(50,50,50);
          // 添加聚光灯2
          addSpotlight(-50,50,50);
          // 添加聚光灯3
          addSpotlight(50,50,-50);
          // 添加聚光灯4
          addSpotlight(-50,50,-50);
		  
		  addLight();
 
		}
 
		
		function addLight(){
		    light = new THREE.SpotLight( 0xffffff );
    		light.position.set( 30, 30, 30 );
    		light.target.position.set( 0, 0, 0 );
    
    		light.castShadow = true;
    
    		light.shadowCameraNear = 10;
    		light.shadowCameraFar = 100;//camera.far;
    		light.shadowCameraFov = 30;
    
    		light.shadowMapBias = 0.0039;
    		light.shadowMapDarkness = 0.5;
    		light.shadowMapWidth = SHADOW_MAP_WIDTH;
    		light.shadowMapHeight = SHADOW_MAP_HEIGHT;
    
    		light.shadowCameraVisible = true;
    
    		scene.add( light );
		}
 
		function setupEventListeners() {
		  window.addEventListener("resize", onWindowResize);
		}
		
		function onDocumentMouseMove( event ) {
			mouseX = ( event.clientX - windowHalfX );
			mouseY = ( event.clientY - windowHalfY );
		}
 
		function onWindowResize( event ) {
			SCREEN_WIDTH = window.innerWidth;
			SCREEN_HEIGHT = window.innerHeight;
 
			renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
			
			//camera.aspect = window.innerWidth / window.innerHeight;
			//renderer.setSize(window.innerWidth, window.innerHeight);
 
			camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
			camera.updateProjectionMatrix();
 
			//controls.screen.width = SCREEN_WIDTH;
			//controls.screen.height = SCREEN_HEIGHT;
 
			camera.radius = ( SCREEN_WIDTH + SCREEN_HEIGHT ) / 4;
		}
		
		//let clock = new THREE.Clock()
 
		function render () {
	        renderer.clear();
	        renderer.render(scene, camera);
			controls.update();
			//let delta = clock.getDelta()
            //world.step(delta)
            // 更新物理世界 step ( dt , [timeSinceLastCalled] , [maxSubSteps=10] )
            // dt:固定时间戳(要使用的固定时间步长)
            // [timeSinceLastCalled]:自上次调用函数以来经过的时间
            // [maxSubSteps=10]:每个函数调用可执行的最大固定步骤数
            // * 设置更新物理世界world的步长timestep
            //  * 这里选用60Hz的速度,即1.0 / 60.0
            world.step(1.0 / 60.0)
            //world.fixedStep()
		}
		
		function animate() {
			render();
			requestAnimationFrame(animate);
		}
		
		function addTexture(url){
		    // 加载纹理
              const textureLoader = new THREE.TextureLoader()
			  
			  const texture = textureLoader.load(url)
            
              //textureLoader.load(url, (texture) => {
              //  texture.mapping = THREE.EquirectangularReflectionMapping
              //  scene.background = texture
              //  scene.environment = texture
              //  // 背景模糊强度
              //  scene.backgroundBlurriness = 0.01
              //})
			  return texture
		}
		
		function setControls(){
		    controls = new OrbitControls( camera, renderer.domElement );
		    controls.enableDamping = true;
		    controls.rotateSpeed = 1.0;
			controls.zoomSpeed = 1.2;
			controls.panSpeed = 0.2;
			controls.noZoom = false;
			controls.noPan = false;
			controls.staticMoving = false;
			controls.dynamicDampingFactor = 0.3;
			const radius = 10;
			controls.minDistance = 0.0;
			controls.maxDistance = radius * 10;
			
			controls.enablePan = true
            controls.dampingFactor = 0.25
            controls.screenSpacePanning = false
            controls.enableZoom = true
            controls.zoomScale = 10
            controls.minZoom = 1
            controls.maxZoom = 100
            //controls.minPolarAngle = 1 * -Math.PI / 180
            //controls.maxPolarAngle = 90 * Math.PI / 180
            //controls.minAzimuthAngle = 90 * -Math.PI / 180
            //controls.maxAzimuthAngle = 90 * Math.PI / 180
			//controls.keys = [ 65, 83, 68 ]; // [ rotateKey, zoomKey, panKey ]
			//controls.screen.width = SCREEN_WIDTH;
			//controls.screen.height = SCREEN_HEIGHT;
			
			/*
			// Trackball controls
			controls = new THREE.TrackballControls( camera, renderer.domElement );
			controls.rotateSpeed = 1.0;
			controls.zoomSpeed = 1.2;
			controls.panSpeed = 0.2;
			controls.noZoom = false;
			controls.noPan = false;
			controls.staticMoving = false;
			controls.dynamicDampingFactor = 0.3;
			var radius = 100;
			controls.minDistance = 0.0;
			controls.maxDistance = radius * 1000;
			//controls.keys = [ 65, 83, 68 ]; // [ rotateKey, zoomKey, panKey ]
			controls.screen.width = SCREEN_WIDTH;
			controls.screen.height = SCREEN_HEIGHT;
			*/
		}
		
		function addSpotlight (x,y,z){
            const spotLight2 = new THREE.SpotLight(0xffffff, 1)
            spotLight2.position.set(x, y, z)
            spotLight2.target.position.set( 0, 0, 0 )
            spotLight2.castShadow = true
            spotLight2.shadow.camera.near = 0.1
            spotLight2.shadow.camera.far = 30
            spotLight2.shadow.camera.fov = 30
            spotLight2.shadow.mapSize.width = 256
            spotLight2.shadow.mapSize.height = 256
            // 设置灯光 bias ,解决自阴影问题
            spotLight2.shadow.bias = -0.0008
            spotLight2.power = 1
            scene.add(spotLight2)
        
            // 使用辅助器对灯光和阴影进行调整
            //const cameraHelper = new THREE.SpotLightHelper(spotLight2)
            //scene.add(cameraHelper)
        }
		
		function initCannon() {
			// 初始化物理世界
			world = new CANNON.World()
			// 设置物理世界重力加速度 单位:m/s² 重力加速度x、y、z分量值,假设y轴竖直向上,这样重力就速度就是y方向负方向。
			world.gravity.set(0, -9.82, 0)

			// npm install cannon-es-debugger
			// 加入 cannon-es-debugger 可以展示模型的物理世界的轮廓
			// scene: 场景
			// 物理世界
			// 第三个参数为可选参数,其中的的onInit方法返回场景中的每个刚体和对应的物理世界的轮廓的three mesh
			// const cannonDebugger = CannonDebugger(scene, world)
			// const cannonDebugger = CannonDebugger(scene, world, {
			//   onInit(body: CANNON.Body, mesh: THREE.Mesh) {
			//     // 
			//     mesh.visible = true
			//     console.log(body);
			//   },
			// })
			// 还要在每帧更新调用中更新    Update the CannonDebugger meshes
			//  cannonDebugger.update() 

			// 创建默认材质
			defaultMaterial = new CANNON.Material('default')
			//创建足球材质
			bMaterial = new CANNON.Material('bMaterial')
			// 定义两种材质之间的摩擦因数和弹力系数 设置地面材质和小球材质之间的碰撞反弹恢复系数
			const defaultContactMaterial = new CANNON.ContactMaterial(defaultMaterial, bMaterial, {
				friction: 5,
				restitution: 0.5, //反弹恢复系数
			})
			// 把关联的材质添加到物理世界中
			world.addContactMaterial(defaultContactMaterial)

			// NaiveBroadphase Cannon 默认的算法。检测物体碰撞时,一个基础的方式是检测每个物体是否与其他每个物体发生了碰撞
			// GridBroadphase 网格检测。轴对齐的均匀网格 Broadphase。将空间划分为网格,网格内进行检测。
			// SAPBroadphase(Sweep-and-Prune) 扫描-剪枝算法,性能最好。
			// 默认为 NaiveBroadphase,建议替换为 SAPBroadphase
			// 碰撞算法
			world.broadphase = new CANNON.SAPBroadphase(world)
			//world.broadphase = new CANNON.NaiveBroadphase();

			// 创建一个物理世界的平面
			const planeShape = new CANNON.Plane()
			// 创建一个刚体
			const planeBody = new CANNON.Body({
			  mass: 0, // 设置质量为0,不受碰撞的影响
			  shape: planeShape,
			  position: new CANNON.Vec3(0, 1, 0)
			})
			// 改变平面默认的方向,法线默认沿着z轴,旋转到平面向上朝着y方向
			// 设置刚体旋转(设置旋转X轴)旋转规律类似threejs 平面
			planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
			// 将刚体添加到物理世界当中
			world.addBody(planeBody)

			const textureLoader = new THREE.TextureLoader();

			let texture = textureLoader.load('https://ts2.cn.mm.bing.net/th?id=OJ.nhM9wE4TnIlRRg');

			texture.wrapS = THREE.RepeatWrapping;
			texture.wrapT = THREE.RepeatWrapping;

			texture.repeat.set(1,1);
			texture.needsPMREMUpdate = true
			texture.minFilter = THREE.LinearFilter
			texture.magFilter = THREE.LinearFilter
			
			scene.environment = texture;

			let cubeGeometry = new THREE.BoxGeometry(1,1,1);
			// http://www.webgl3d.cn/pages/21003f/ PBR材质金属度、粗糙度以及环境贴图
			const cMaterial = new THREE.MeshPhysicalMaterial({ 
				map: texture,
				side: THREE.DoubleSide,
				normalMap: texture,
				normalScale: new THREE.Vector2(1.1,1.1),
				specularMap: texture,
				specular: 0xffffff,
				shininess: 10,
				envMap: texture,
				color: color, 
				metalness: 0.5,//像金属的程度, 非金属材料,如木材或石材,使用0.0,金属使用1.0。
				roughness: 0.5,//模型表面的光滑或者说粗糙程度,越光滑镜面反射能力越强,越粗糙,表面镜面反射能力越弱,更多地表现为漫反射。 
				clearcoat: 1,
				clearcoatRoughness: 0.01,
				envMapIntensity: 2.5,
				opacity: 0.5,
				transparent: true 
			});

			cube = new THREE.Mesh(cubeGeometry,cMaterial);

			cube.position.x=0;
			cube.position.y=2;
			cube.position.z=0;
			cube.castShadow = true;
			scene.add(cube);

          }
          
          const calcMeshCenter = (group)=>{
                /**
                 * 包围盒全自动计算:模型整体居中
                 */
                let box3 = new THREE.Box3()
                // 计算层级模型group的包围盒
                // 模型group是加载一个三维模型返回的对象,包含多个网格模型
                box3.expandByObject(group)
                // 计算一个层级模型对应包围盒的几何体中心在世界坐标中的位置
                let center = new THREE.Vector3()
                box3.getCenter(center)
                // console.log('查看几何体中心坐标', center);
            
                // 重新设置模型的位置,使之居中。
                group.position.x = group.position.x - center.x
                group.position.y = group.position.y - center.y
                group.position.z = group.position.z - center.z
	      }
		  
		  const gui = new GUI({
			  width: 280,
			  title: 'Setting',
			  autoPlace: true
		  })
		  
		  var params = new function() {
				this.color = 0x123456;
				this.position = -35;
				this.visible = true;
			};
		  
		  function addGUI(){
		    gui.addColor(params, "color").onChange(e => {
				cube.material.color.set(e);
			});
			gui.add(params, "position", -172, 28).onChange(e => {
				tempCubes.position.x = e
			});
		  }

		
		const canvas = document.querySelector('canvas');
		const ctx = canvas.getContext('2d',{
			//频繁读取画布信息
			willReadFrequently: true
		});

		function initCanvasSize(){
			//canvas绘制的图形是位图,即栅格图像或点阵图像,当将它渲染到高清屏时,会被放大,每个像素点会用devicePixelRatio的平方个物理像素点来渲染,因此图片会变得模糊。
			//将画布尺寸设置为画板尺寸的window.devicePixelRatio倍
			canvas.width = window.innerWidth * window.devicePixelRatio;
			canvas.height = "150"//window.innerHeight * window.devicePixelRatio;
		}
		
		initCanvasSize();

		//获取[min,max]范围内的随机数
		function getRandom(min, max){
		    return Math.floor(Math.random()*(max+1-min)+min);
		}
		
		//通过构造函数生成圆形颗粒
		class Particle{
			constructor(){
				//生成圆圈的半径
				const r = Math.min(canvas.width,canvas.height)/2;
				//画布的中心点
				const cx = canvas.width/2;
				const cy = canvas.height/2;
				//大圆上的随机角度并换算成弧度
				const rad = getRandom(0,360)*(Math.PI/180); 
				//粒子的坐标
				this.x = cx + r*Math.cos(rad);
				this.y = cy + r*Math.sin(rad);
				//粒子的尺寸
				this.size = getRandom(1*window.devicePixelRatio,5*window.devicePixelRatio);
			}
			
			randomHexColor() { //随机生成十六进制颜色
			 const hex = Math.floor(Math.random() * 16777216).toString(16); //生成ffffff以内16进制数
			 while (hex.length < 6) { //while循环判断hex位数,少于6位前面加0凑够6位
			  hex = '0' + hex;
			 }
			 return '#' + hex; //返回‘#'开头16进制颜色
			}
			
			draw(){
				ctx.beginPath();
				ctx.fillStyle = color;
				//ctx.fillStyle = this.randomHexColor();
				ctx.arc(this.x,this.y,this.size,0,2*Math.PI);
				ctx.fill();
			}
			moveTo(targetX,targetY){
			   
			   //设定一个缓动时间 500 ms毫秒
			   //起始位置
			   const startX = this.x;
			   const startY = this.y;
			   const duration = 500;
			   //横向速度 目标位置 减去 当前位置 再除以运动的时间
			   const xSpeed = (targetX - startX)/duration;
			   //纵向速度 目标位置 减去 当前位置 再除以运动的时间
			   const ySpeed = (targetY - startY)/duration;
			   //起始时间
			   const startTime = Date.now();
			   //增加动画效果
			   const _move = ()=>{
			   //运动时间
			   const t = Date.now() - startTime;
			   //x方向运动距离 起始位置 加 速度乘以时间
			   const x = startX + xSpeed * t;
			   //y方向运动距离 起始位置 加 速度乘以时间
			   const y = startY + ySpeed * t;
			   //x,y缓动
			   this.x = x;
			   this.y = y;
			   //超过设定时间取消运动
			   if(t>=duration){
				 // 赋值为目标位置
				 this.x = targetX
				 this.y = targetY
			   }
			   //重新注册raf
			   requestAnimationFrame(_move)
			   }
			   //执行移动
			   _move();
			}
		}
		
		//点数据组
		const partciles = [];
		//需要绘制的文本
		let text = null;
		//填充点数据
		for(let i = 0;i<1;i++){
			//画单个点
			const p = new Particle();
			//启动绘制
			//p.draw();
			//添加进数组
		    partciles.push(p);
		}
		//清空画布
		function clear(){
		    //清空画布
			ctx.clearRect(0,0,canvas.width,canvas.height);
			tempCtx.clearRect(0,0,tempCanvas.width,tempCanvas.height);
			// 将缓存 canvas 复制到旧的 canvas
			//ctx.drawImage(tempCanvas,0,0,canvas.width,canvas.height);
		}
		
		// 新建一个 canvas 作为缓存 canvas
		const tempCanvas = document.createElement('canvas');
		// 设置缓冲画布的宽高
		tempCanvas.width = canvas.width; 
		tempCanvas.height = canvas.height;
		const tempCtx = tempCanvas.getContext('2d');
		
		//绘制
		function draw(){
		    //在每次绘制文本之前清空画布
		    clear();
		    // 开始绘制
			//let img = document.createElement('img');
			//let data = canvas.toDataURL();
			//img.src = data;
			// 缓存 canvas 绘制完成
			//tempCtx.drawImage(img,0,0);
			
			update();
			//遍历数组中的所有点
			partciles.forEach((p)=>{
				//绘制点
				p.draw();
				//p.moveTo(0,0);
			})
			//下一次绘制时注册RAF,重新绘制
			requestAnimationFrame(draw);
		}
		//生成文字
		function getText(){
		   //得到当前时间 并做截取
		   return new Date().toTimeString().substring(0,8);
		}
		//绘制文字
		function update(){
		   //获得文本
		   const newText = getText();
		   if(newText ==  text){
		      //相同就不绘制
			  return;
		   }

		   text = newText;
		   //绘制 文本
		   const { width, height } = tempCanvas;
		   
		   if(drImage){
		        // let temp = localStorage.getItem("threeImage");
				// CSSImageValue 或 HTMLCanvasElement 或 HTMLImageElement 或 HTMLVideoElement 或 ImageBitmap 或 OffscreenCanvas 或 SVGImageElement 或 VideoFrame
		        //tempCtx.drawImage(document.getElementById("testImage"),0,0,tempCanvas.width,tempCanvas.height);
				
				tempCtx.fillStyle = "#000";
				tempCtx.textBaseline = "middle";
				tempCtx.font = `${100*window.devicePixelRatio}px '华文细黑', sans-serif`;
				//计算出绘制出的文本的宽度
				const textWidth = tempCtx.measureText(text).width;
				tempCtx.fillText(text,(width - textWidth)/3,height/2);
				//imageBox.appendChild(tempCanvas);
		   }

		   //拿到画布上黑色像素点的坐标
		   const points = getPoints();
		   //console.log(points);
		   //如果数组长度小于文本坐标的长度 删除多余部分即可
		   if(points.length < partciles.length){
		       partciles.splice(points.length);
		   }
		   
		   scene.remove(tempCubes); // 更新前删除旧数码显示
		   
		   tempCubes = null;
		   tempCubes = new THREE.Object3D();
		   
		   for(let i=0;i<points.length;i++){
			   let p = partciles[i];
			   //粒子不足创建即可
			   if(!p){
				 p = new Particle();
				 partciles.push(p);
			   }
			   const [x,y] = points[i];
			   p.moveTo(x,y);
			   
			   let tempCube = cube.clone();
			   tempCube.position.set((x/10),(y/10),(y/10));
			   tempCubes.add(tempCube);
		   }
		   
		   if(tempCubes){
			    //calcMeshCenter(tempCubes);
				rotateAroundWorldAxis(tempCubes,new THREE.Vector3(1,0,0), Math.PI);
			    tempCubes.position.set(-36,10,0);
			    tempCubes.rotateX(-Math.PI/2);
			    tempCubes.scale.set(0.5, 0.5, 0.5);
			    //flipMesh(tempCubes);
			    let position = new THREE.Vector3();
				let quaternion = new THREE.Quaternion();
				let scale = new THREE.Vector3();

				tempCubes.matrixWorld.decompose( position, quaternion, scale );
			    tempCubes.updateMatrixWorld( true );
				
				
			    scene.add(tempCubes);
		   }
		   
		}
		
		function flipMesh(object3D) {
			object3D.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
			reverseWindingOrder(object3D);
		}

		function reverseWindingOrder(object3D) {

			// TODO: Something is missing, the objects are flipped alright but the light reflection on them is somehow broken

			if (object3D.type === "Mesh") {

				var geometry = object3D.geometry;

				for (var i = 0, l = geometry.faces.length; i < l; i++) {

					var face = geometry.faces[i];
					var temp = face.a;
					face.a = face.c;
					face.c = temp;

				}

				var faceVertexUvs = geometry.faceVertexUvs[0];
				for (i = 0, l = faceVertexUvs.length; i < l; i++) {

					var vector2 = faceVertexUvs[i][0];
					faceVertexUvs[i][0] = faceVertexUvs[i][2];
					faceVertexUvs[i][2] = vector2;
				}

				geometry.computeFaceNormals();
				geometry.computeVertexNormals();
			}

			if (object3D.children) {

				for (var j = 0, jl = object3D.children.length; j < jl; j++) {

					reverseWindingOrder(object3D.children[j]);
				}
			}
		}
		
		function getPoints(){
			// 得到画布上制定范围内的像素点信息
			const { width, height, data} = tempCtx.getImageData(0,0,tempCanvas.width,tempCanvas.height);
			//console.log(data);
			const points = [];
			//像素点取稀释点
			const gap = 4;
			for(let i = 0; i< width;i+=gap){
				for(let j = 0; j< height;j+=gap){
					//通过行号、列号 计算像素点的下标
					const index = (i+j*width)*4;
					const r = data[index];
					const g = data[index+1];
					const b = data[index+2];
					const a = data[index+3];
					//判断是否透明
					if(r===0&&g===0&&b===0&&a===255){
					    //console.log(r,g,b,a);
						points.push([i,j])
					}
				}
			}
			return points;
		}
		
		let imageBox = document.getElementById('threeImage')
	
		const loadImage = (url) => {
		  return new Promise((resolve, reject) => {
			const img = new Image()
			img.onload = () => {
			    resolve(img);
			}
			img.onerror = (err) => {
			  reject(err)
			}
			img.src = url
			img.alt = "threeImage";
		  })
		}
		
		function startDownload(url) {
		  document.getElementById("testImage").src = url;
		  document.getElementById("testImage").width = window.innerWidth
		  document.getElementById("testImage").height = window.innerHeight
	 
		  loadImage(url).then((resImage)=>{
				imageReceived(resImage)
		  }).catch((err)=>{
			   throw new Error(err); // reject promise
		  });
		  //let imageURL = url;
		  //let imageDescription = "three image";
	 
		  //downloadedImg = new Image();
		  //downloadedImg.crossOrigin = "Anonymous";
		  //downloadedImg.addEventListener("load", imageReceived, false);
		  //downloadedImg.alt = imageDescription;
		  //downloadedImg.src = imageURL;
		}
		
		let drImage = null
		
		function imageReceived(downloadedImg) {
		  const c = document.createElement("canvas");
		  let context = c.getContext("2d");
	 
		  c.width = window.innerWidth || downloadedImg.width;
		  c.height = window.innerHeight || downloadedImg.height;
		  c.innerText = downloadedImg.alt;
		  
		  //downloadedImg.setAttribute('crossOrigin', 'anonymous');
		  //context.drawImage(downloadedImg, 0, 0, c.width, c.height);
		  //imageBox.appendChild(c);
		  //确保图片被加载完成 使用drawImage绘制到画布上面
		  drImage = downloadedImg; //画布已被跨域数据污染的操作

		  try {
			localStorage.setItem("threeImage", c.toDataURL("image/png"));
			draw();
			// 复杂问题拆解成简单化问题
			// 画单个点,画很多个点,画很多个运动的点
			// 提升代码掌控能力,开发思维
		  } catch (err) {
			console.error(`Error: ${err}`);
		  }
		}
		
		//需要服务器端支持 CORS policy: No 'Access-Control-Allow-Origin' 
		startDownload('https://ts2.cn.mm.bing.net/th?id=OJ.nhM9wE4TnIlRRg');
		
		setup();


	</script>
 
 
  </body>
 
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1847459.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vulnhub靶场之FunBox-11

一.环境搭建 1.靶场描述 As always, its a very easy box for beginners. Add to your /etc/hosts: funbox11 This works better with VirtualBox rather than VMware. 2.靶场下载 https://www.vulnhub.com/entry/funbox-scriptkiddie,725/ 3.靶场启动 二.信息收集 1.寻找靶…

2. zabbix-agent获取监控数据的三种方式

zabbix-agent获取监控数据的三种方式 一、监控其他主机1、在被监控机安装部署zabbix-agent2、在web界面添加监控主机3、系统级别的监控数据参考 二、zabbix-agent监控的三种方式1、利用自带key监控数据1.1 示例1: 监控node01网卡的流入流量1.2 常用的key 2、自定义key的监控2.1…

Python基础用法 之 运算符

1.算数运算符 符号作用说明举例加与“”相同 - 减与“-”相同*乘 与“ ”相同 9*218/除 与“ ”相同 9/24.5 、6/32.0//求商&#xff08;整数部分&#xff09; 两个数据做除法的 商 9//24%取余&#xff08;余数部分&#xff09; 是两个数据做除法的 余数 9%21**幂、次方2**…

IDEA 配置方法模板无法获取到参数值和返回值(methodParameters()、methodReturnType()获取不到值)

问题现象&#xff1a; 我在 review 同事代码时候&#xff0c;发现方法上有注释&#xff0c;但是注释上又没有方法参数和返回值&#xff0c;这不是IDEA 配置了方法模板就可以自动生成的嘛&#xff0c;我出于好奇去问了下该同事是怎么回事&#xff0c;该同事有点不好意思的说我配…

客户集中度高,毛利率下滑,江苏永成的IPO之路能走通吗?

撰稿|行星 来源|贝多财经 近年来&#xff0c;汽车市场蓬勃向上&#xff0c;助推上游配套产业链进入增长热潮。 行业利好前景下&#xff0c;不少汽车上游供应商开始向资本市场进发&#xff0c;希望借助上市拓宽融资渠道&#xff0c;加速业务拓展和技术创新&#xff0c;在产业…

【数据库备份完整版】物理备份、逻辑备份,mysqldump、mysqlbinlog的备份方法

【数据库备份完整版】物理备份、逻辑备份&#xff0c;mysqldump、mysqlbinlog的备份方法 一、物理备份二、逻辑备份1.mysqldump和binlog备份的方式&#xff1a;2.mysqldump完整备份与恢复数据2.1 mysqldump概念2.2 mysqldump备份2.3 数据恢复2.4 **使用 Cron 自动执行备份**2.5…

Elk安装及使用

es安装及使用 单机版安装 集群安装 132 node-01 133 node-02 135 node-03 日志用户权限有问题 看日志 解决方案&#xff1a; 出现错误后&#xff0c;再次重启前&#xff0c;需要删除三个节点/data/下的内容 9300-http 9300-tcp logstasha安装及使用 Ssh错误 Yum安装默认路…

海量数据处理——bitMap/BloomFilter、hash + 统计 + 堆/归并/快排

前言&#xff1a;海量数据处理是面试中一道常考的问题&#xff0c; 生活中也容易遇到这种问题。 通常就是有一个大文件&#xff0c; 让我们对这个文件进行一系列操作——找出现次数最多的数据、求交集、是否重复出现等等。 因为文件的内容太多&#xff0c; 我们的内存通常是放不…

Java基础 - 练习(五)根据今天日期获取一周内的日期(基姆拉尔森公式)

基姆拉尔森计算公式用于计算一周内的日期。比如给你年月日&#xff0c;从而计算今天是星期几。 基姆拉尔森公式 Week (d2*m3*(m1)/5yy/4-y/100y/4001) mod 7&#xff0c; 3<m<14Week的取值范围是0 ~ 6&#xff0c;其中0代表星期日&#xff0c;1 ~ 6分别代表星期一到星期…

【考研408计算机组成原理】数值表示和运算之快速数值转换

苏泽 “弃工从研”的路上很孤独&#xff0c;于是我记下了些许笔记相伴&#xff0c;希望能够帮助到大家 另外&#xff0c;利用了工作之余的一点点时间&#xff0c;整理了一套考研408的知识图谱&#xff0c; 我根据这一套知识图谱打造了这样一个408知识图谱问答系统 里面的每一…

Java学习笔记(二)变量原理、常用编码、类型转换

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍Java变量原理、常用编码、类型转换详细使用以及部分理论知识 🍉欢迎点赞 👍 收藏 ⭐留言评论 📝私信必回哟😁 🍉博主收将持续更新学习记录获,友友们有任何问题可以在评论区留言 1、变量原理 1.1、变量的介绍 变量是程…

在寻找电子名片在线制作免费生成?5个软件帮助你快速制作电子名片

在寻找电子名片在线制作免费生成&#xff1f;5个软件帮助你快速制作电子名片 当你需要快速制作电子名片时&#xff0c;有几款免费在线工具可以帮助你实现这个目标。这些工具提供了丰富的设计模板和元素&#xff0c;让你可以轻松地创建个性化、专业水平的电子名片。 1.一键logo…

逻辑回归(Logistic Regression)及其在机器学习中的应用

&#x1f680;时空传送门 &#x1f50d;逻辑回归原理&#x1f4d5;Sigmoid函数&#x1f388;逻辑回归模型 &#x1f4d5;损失函数与优化&#x1f388;损失函数&#x1f680;优化算法 &#x1f50d;逻辑回归的应用场景&#x1f340;使用逻辑回归预测客户流失使用scikit-learn库实…

如何在Ubuntu上安装WordPress

如何在Ubuntu上安装WordPress 执行系统更新 apt update && apt upgrade第一步 安装 Apache apt install apache2确认 Apache 安装是否成功. systemctl status apache2安装成功后 打开浏览器输入 http://server-ip-address 第二步 安装 MySQL apt install mariad…

硬件开发笔记(二十一):外部搜索不到的元器件封装可尝试使用AD21软件的“ManufacturerPart Search”功能

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/139869584 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

[保姆级教程]uniapp实现底部导航栏

文章目录 前置准备工作安装HBuilder-X新建uniapp项目教程使用HBuilder-X启动uniapp项目教程 实现底部导航栏package.json中配置导航栏详细配置内容 前置准备工作 安装HBuilder-X 详细步骤可看上文》》 新建uniapp项目教程 详细步骤可看上文》》 使用HBuilder-X启动uniapp项…

后仿真中 module path polarity 问题

目录 一 未知极性 二 正极性 三 负极性 不知道大家有没有遇到这个问题:什么?我们知道的module path delay 指的是定义在specify...endspecify block 中的语句,指示输入-输出的延迟信息。 这里的module path 竟然还有极性问题,今天,来学习一下。 模块路径的极性是一…

CCAA质量管理【学习笔记】​​ 备考知识点笔记(七)质量相关法律法规及《管理体系审核员准则》2021修订3

5、质量管理体系基础考试大纲 3.3法律法规和其他要求 a)《中华人民共和国民法典》第三编 合同&#xff1b; b)《中华人民共和国消费者权益保护法》 c)《中华人民共和国产品质量法》 d) 中国认证认可协会相关人员注册与管理要求 目 录 前 言 第一章 总则 1.1 引言 1.2 适…

最详细的Selenium+Pytest自动化测试框架实战

前言 selenium自动化 pytest测试框架 本章你需要 一定的python基础——至少明白类与对象&#xff0c;封装继承 一定的selenium基础——本篇不讲selenium&#xff0c; 测试框架简介 测试框架有什么优点呢&#xff1a; 代码复用率高&#xff0c;如果不使用框架的话&#xff…

华测监测预警系统2.2 UserEdit.aspx SQL注入致RCE漏洞复现(CVE-2023-5827)

0x01 产品简介 华测监测预警系统2.2是一套针对地质灾害监测预警的科学、完善平台,实现了地质灾害防治管理的科学化、信息化、标准化和可视化。该系统由上海华测导航技术有限公司开发,主要服务于山体滑坡、地裂缝等地质灾害的自动化预警。 0x02 漏洞概述 华测监测预警系统2…