javascript 3d网页 基于 three.js (一)
程序员文章站
2022-07-09 19:58:58
1 "use strict" 2 3 //加载js文件 4 loadFile([ 5 "./js/lib/WebGL.js",//检查 是否支持webGL 插件 6 "./js/lib/three_114.min.js",//3d库 7 "js/func.js" 8 ], main); 9 10 / ......
1 "use strict" 2 3 //加载js文件 4 loadfile([ 5 "./js/lib/webgl.js",//检查 是否支持webgl 插件 6 "./js/lib/three_114.min.js",//3d库 7 "js/func.js" 8 ], main); 9 10 //创建一个ajax请求, 前往index.php验证用户是否已登录 11 //如果ajax返回的是false说明用户还没有登录成功,并前往 view.php, 12 //否则ajax返回用户信息对象 13 function main(){ 14 new ajax({ 15 url:"./php/index.php", 16 method:"get", 17 success:(data)=>{ 18 let d = json.parse(data); 19 if(d === false){location.href = "./login/view.php"; return;} 20 showuserinfo(d.val, new view());//用户信息视图 21 showthreeview();//场景3d视图 22 } 23 }); 24 } 25 26 //退出 27 function exit(){ 28 if(new func().isrun({id:"testid"}) !== true){console.log("你点得太快了,服务器跟不上"); return;} 29 new ajax({ 30 url:"./php/exit.php", 31 method:"get", 32 success:(data)=>{main();} 33 }); 34 } 35 36 //创建html p元素 37 function create(v, fel, content){ 38 let p = v.add(fel, "p"); 39 p.innerhtml = content || ""; 40 return p; 41 } 42 43 //创建登录成功后的主页内容 44 function showuserinfo(user, v){//console.log(user); 45 46 var lid = v.get("loginboxid"); 47 48 var elem_name = create(v, lid, "你好: " + user.username); 49 elem_name.innerhtml += "<input type = 'button' id = 'exitid' value = '退 出' />"; 50 v.get('exitid').onclick = ()=>{exit();}//退出按钮 51 52 create(v, lid, "我知道你的邮箱是: " + user.email); 53 54 switch(user.like.length){ 55 case 1 : 56 create(v, lid, "我还知道你喜欢: " + user.like[0]); 57 break; 58 case 2 : 59 create(v, lid, "我还知道你喜欢: " + user.like[0]); 60 create(v, lid, "还有: " + user.like[1]); 61 break; 62 case 3 : 63 create(v, lid, "我还知道你喜欢: " + user.like[0]); 64 create(v, lid, "还有: " + user.like[1]); 65 create(v, lid, "还有: " + user.like[2]); 66 break; 67 default : break; 68 } 69 lid.style = "visibility:visible;"; 70 //lid居中显示 71 /* let x = math.round((v.client.w/2) - (lid.offsetwidth/2)); 72 let y = math.round((v.client.h/2) - (lid.offsetheight/2)) - 16; 73 lid.style = "left:"+x+"px; top:"+y+"px; visibility:visible;"; */ 74 } 75 76 77 //创建 主页 3d 视图 78 /* 79 键盘 w a s d移动 80 鼠标 点击滑动旋转 81 */ 82 function showthreeview(){ 83 84 var three = new three(); 85 86 //场景 87 var scene = three.createscene(); 88 89 //相机 90 var camera = three.createcamera(); //console.log(camera); 91 92 //渲染器 93 var renderer = three.createrenderer(); 94 95 //灯光 96 var light = three.createlight(scene); 97 98 //控制器 99 three.createcontrol(camera, renderer); 100 101 //动画循环 102 three.animate(scene, camera, renderer); 103 104 //创建地面 105 three.createground(); 106 107 //创建院墙 108 three.createwall({issety:true}); 109 110 //创建铁门 111 three.creategate(); 112 113 console.log(three); 114 alert("键盘 w a s d移动, 鼠标 点击滑动旋转"); 115 }
1 "use strict" 2 3 /**方法类 4 5 */ 6 class func{ 7 8 constructor(){} 9 10 //获取一个字符串的长度 包含中文 11 getstrlen(str){ 12 let len = 0, i, c; 13 for (i = 0; i < str.length; i++){ 14 c = str.charcodeat(i); 15 if((c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f)){len++;}else{len+=2;} 16 } 17 return len; 18 } 19 20 //获取目标范围随机数(n保留浮点数数量) 21 getran(min, max, n){ 22 return number((math.random() * (max - min) + min).tofixed(n || 0));//包含max 23 } 24 25 //获取2点距离 26 getposlen(sdot, edot){ 27 return parseint(math.sqrt(math.pow(math.abs(sdot.x - edot.x), 2) + math.pow(math.abs(sdot.y - edot.y), 2))); 28 } 29 30 //检测是否是pc端 31 ispc(){ 32 let useragent = navigator.useragent; 33 let agents = ["android", "iphone", "symbianos", "windows phone", "ipad", "ipod"]; 34 let ispc = true, v; 35 for(v = 0; v < agents.length; v++){ 36 if (useragent.indexof(agents[v]) > 0){ispc = false; break;} 37 } 38 return ispc; 39 } 40 41 //防止多次提交或执行(在规定时间段内只允许执行一次) 默认 3000ms 42 isrun(object){ 43 //object = {id: string 必须, isexit, time} 44 //isenit : boolean 可选 如果为 true 则删除对应 id 的对象 45 //time : number 可选 默认间隔时间 3000 秒 46 var v = object || {}; 47 if(this.list === undefined){func.prototype.list = {};} 48 if(v.id === undefined || typeof(v.id) !== "string"){return "参数id错误";} 49 if(v.isexit === true){delete(this.list[v.id]); return "删除对象: "+v.id;} 50 var o = this.list[v.id]; 51 if(!o){this.list[v.id] = {time:v.time || 3000, nowtime:new date().gettime()}; return true;} 52 var t = new date().gettime() - o.nowtime; 53 if(t < o.time){return o.time - t;} 54 o.nowtime = new date().gettime(); 55 return true; 56 } 57 58 } 59 60 61 62 /** 创建ajax请求: 63 obj = { 64 url: string 必须, 请求路径 65 method: string 可选, post 或 get请求, 默认post请求 66 data: string 可选, 要发送的数据, 默认为"" 67 asy: boolean 可选, 是否异步执行, 默认为true 68 success: function 可选, 成功回调 69 error: function 可选, 失败回调 70 run: function 可选, 请求中回调 71 } 72 73 xhr.readystate: 74 0: 请求未初始化 75 1: 服务器连接已建立 76 2: 请求已接收 77 3: 请求处理中 78 4: 请求已完成,且响应已就绪 79 xhr.status: 80 200: "ok" 81 404: 未找到页面 82 */ 83 class ajax{ 84 85 constructor(obj){ 86 let o = obj || {} 87 this.url = o.url || null; 88 this.method = o.method || "post"; 89 this.data = o.data || ""; 90 this.asy = o.asy || true; 91 this.callback_suc = o.success || function (){}; 92 this.callback_err = o.error || function (){}; 93 this.callback_run = o.run || function (){}; 94 this.request(); 95 } 96 97 request(){ 98 if(!this.url){this.callback_err(); return;} 99 let xhr = new xmlhttprequest(); 100 xhr.onreadystatechange = (e)=>{ 101 if(e.target.readystate === 4 && e.target.status === 200){ 102 this.callback_suc(e.target.responsetext); 103 return; 104 } 105 this.callback_run(e); 106 } 107 xhr.onerror = (e)=>{ 108 this.callback_err(e); 109 } 110 if(this.method == "get"){ 111 xhr.open(this.method, this.url, this.asy); 112 xhr.send(); 113 return; 114 } 115 xhr.open(this.method, this.url, this.asy); 116 xhr.setrequestheader("content-type", "application/x-www-form-urlencoded"); 117 xhr.send(this.data); 118 } 119 120 } 121 122 123 124 /** 视图类 125 126 */ 127 class view{ 128 129 constructor(){ 130 if(!this.client){view.prototype.client = this.getclient();} 131 } 132 133 //创建html元素 134 add(fel, elemname, id, cls){ 135 //创建一个元素 136 let el = document.createelement(elemname); 137 //设置el id 和 class 138 if(id){el.setattribute('id',id);} 139 if(cls){el.classname = cls;} 140 //把el添加到fel并显示(渲染el) 141 if(fel){fel.appendchild(el);} 142 return el; 143 } 144 145 //删除html元素 146 remove(){ 147 let k, arg = arguments, err = []; 148 for(k = 0; k < arg.length; k++){ 149 if(this.isel(arg[k]) === false){err.push(arg[k]); continue;} 150 arg[k].parentnode.removechild(arg[k]); 151 } 152 if(err.length > 0){return {err:'这里有一些删除失败的元素', arr:err};} 153 return true; 154 } 155 156 //id获取html元素 157 get(id){ 158 return document.getelementbyid(id); 159 } 160 161 //获取可视宽高 162 getclient(){ 163 return { 164 w:document.documentelement.clientwidth || document.body.clientwidth, 165 h:document.documentelement.clientheight || document.body.clientheight 166 }; 167 } 168 169 //通过parentnode检查元素是否存在于页面中 170 isel(el){ 171 if(typeof(el) !== 'object'){return false;} 172 //被删除之后的html元素object的 parentnode等于null 173 if(!el.parentnode){return false;} 174 return true; 175 } 176 177 //元素绑定事件 178 addevent(target, ev, callback){ 179 target.addeventlistener(ev, function(e){if(callback){callback(e);}}, false); 180 } 181 182 } 183 184 185 186 /** 3d库 187 three.js --- version number 114 188 create scene 189 */ 190 class three{ 191 192 constructor(){ 193 if (webgl.iswebglavailable() === false){ 194 alert("你不支持webgl"); 195 return; 196 } 197 this.view = new view(); 198 three.cache.enabled = true;//加载器启用缓存 199 this.clock = new three.clock();// 200 this.group = new three.group();//创建一个 分组 201 this.textures = this.createtexture();//存放 纹理 的对象 202 } 203 204 //创建 场景 205 createscene(){ 206 var scene = new three.scene(); 207 //scene.fog = new three.fog(0xcce0ff, 800, 1000);//线性雾 208 scene.background = new three.cubetextureloader() 209 .setpath('img/cube/skyboxsun25deg/') 210 .load( [ 'px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg' ] ); 211 if(this.group){scene.add(this.group);} 212 return scene; 213 } 214 215 //创建 相机 216 createcamera(){ 217 var camera = new three.perspectivecamera(75, this.view.client.w/this.view.client.h, 1, 5000); 218 camera.position.set(0, 150, 300);//相机起始位置 219 return camera; 220 } 221 222 //创建 渲染器 223 createrenderer(){ 224 var renderer = new three.webglrenderer({ 225 antialias : true,//抗割齿 226 powerpreference:"high-performance"//选择高性能gpu渲染 227 }); 228 renderer.setsize(this.view.client.w, this.view.client.h);//设置渲染大小 229 renderer.setpixelratio(window.devicepixelratio);//渲染矫正 230 renderer.gammafactor = 2.2;//着色校正 231 renderer.physicallycorrectlights = true;//使其精确照明 232 renderer.shadowmap.enabled = true;//渲染阴影 233 //renderer.autoclear = true;//每帧自动清理缓存 234 if(!renderer.extensions.get('webgl_depth_texture')){console.log("深度纹理扩展获取失败:webgl_depth_texture");} 235 document.body.appendchild(renderer.domelement); 236 renderer.domelement.style.zindex = "0"; 237 renderer.domelement.style.position = "absolute"; 238 renderer.domelement.style.top = "0px"; 239 renderer.domelement.style.left = "0px"; 240 return renderer; 241 } 242 243 //创建 灯光 244 createlight(scene){ 245 scene.add(new three.ambientlight(0x696969));//环境光(无处不在的光,太阳光) 246 var l_d = 1000, light = new three.directionallight(0xf0f8ff, 1);//平行光(产生阴影的光) 247 light.position.set(-3000, 3000, -3000); 248 light.position.multiplyscalar(1); 249 //阴影 250 light.castshadow = true; 251 light.shadow.mapsize.width = 1024; 252 light.shadow.mapsize.height = 1024; 253 light.shadow.camera.left = -l_d; 254 light.shadow.camera.right = l_d; 255 light.shadow.camera.top = l_d; 256 light.shadow.camera.bottom = -l_d; 257 light.shadow.camera.near = 1; 258 light.shadow.camera.far = 6000; 259 scene.add(light); 260 return light; 261 } 262 263 //创建 控制器 264 createcontrol(camera, renderer){ 265 var create = ()=>{ 266 let control = new three.orbitcontrols(camera, renderer.domelement); 267 control.target = new three.vector3(0, 100, 0);//相机焦点 268 //control.minpolarangle = math.pi * 0.3;//向上最大角度 269 //control.maxpolarangle = math.pi * 0.4;//向下最大角度 270 control.mindistance = 1;//最小距离 271 control.maxdistance = 1000;//最大距离 272 control.autorotatespeed = 10;//自动旋转速度 273 control.panspeed = 100;//鼠标旋转速度 274 control.enablezoom = true;//是否启用缩放 275 control.enablekeys = true;//是否启用键盘 276 control.keypanspeed = 100;//按键速度 277 control.keys.left = 65;//key a左 278 control.keys.up = 87;//key w前 279 control.keys.right = 68;//key d右 280 control.keys.bottom = 83;//key s后 281 this.control = control; 282 } 283 loadfile("./js/lib/orbitcontrols.js", create);//加载 控制器 插件 284 } 285 286 //创建 动画循环 287 animate(scene, camera, renderer){ 288 requestanimationframe(()=>{this.animate(scene, camera, renderer);}); 289 if(this.control !== undefined && this.clock !== undefined){ 290 this.control.update(this.clock.getdelta());//更新控制器 291 } 292 renderer.render(scene, camera); 293 } 294 295 //创建纹理 296 createtexture(){ 297 var load = new three.textureloader(); 298 299 var texture_ground = load.load("img/texture/ground.jpg"); 300 texture_ground.wraps = texture_ground.wrapt = three.repeatwrapping; 301 texture_ground.repeat.set(20, 20);//x y 平铺次数 302 texture_ground.anisotropy = 2;//纹理的清晰度(值为2的幕:1, 2, 4, 8, ... 512, 1024, 2048, ...) 303 304 var texture_wall = load.load("img/texture/wall.jpg"); 305 texture_wall.wraps = texture_wall.wrapt = three.repeatwrapping; 306 texture_wall.repeat.set(2.2, 2.2); 307 texture_wall.anisotropy = 1024;//贴图画质 308 texture_wall.minfilter = three.nearestfilter;//深度纹理贴图: 309 texture_wall.magfilter = three.nearestfilter;//深度纹理贴图: 310 311 var texture_gate = load.load("img/texture/gate.jpg"); 312 texture_gate.wraps = texture_gate.wrapt = three.repeatwrapping; 313 texture_gate.repeat.set(0.03, 0.03); 314 texture_gate.anisotropy = 1024; 315 316 return {ground:texture_ground, wall:texture_wall, gate:texture_gate}; 317 } 318 319 //创建地面 land 320 createground(group){ 321 var gro = group || this.group, w = 10000, h = 10000; 322 //gro.add(new three.gridhelper(w, 100));//添加网格辅助线 323 var mesh = new three.mesh(//创建 物体 324 new three.planebuffergeometry(w, h), // geometry 几何体 325 new three.meshlambertmaterial({map:this.textures.ground}) // material 材质 326 ); 327 mesh.receiveshadow = true;//接受阴影 328 mesh.rotation.x = -(math.pi*0.5);//旋转90度 329 mesh.matrixautoupdate = false;//不实时更新矩阵,提升性能 330 mesh.updatematrix();//更新一次矩阵, 让其旋转90度 331 gro.add(mesh); 332 return mesh; 333 } 334 335 //创建围墙 336 createwall(object){ 337 var o = object || {}; 338 var gro = o.group || this.group; 339 var w = 256, h = 256, d = 40; 340 var mesh = new three.mesh( 341 new three.boxbuffergeometry(w, h, d), 342 new three.meshlambertmaterial({map:this.textures.wall}) 343 ); 344 mesh.castshadow = true;//投射阴影 345 mesh.receiveshadow = true;//接收阴影 346 mesh.customdepthmaterial = new three.meshdepthmaterial({depthpacking: three.rgbadepthpacking});//添加阴影 347 if(o.issety === true){mesh.position.y = h / 2 + 1;}//调整坐标 y 348 mesh.matrixautoupdate = false;//不实时更新矩阵,提升性能 349 mesh.updatematrix();//更新一次矩阵 350 gro.add(mesh); 351 return mesh; 352 } 353 354 //创建一块 小钢门... 355 creategate(group){ 356 var gro = group || this.group, w = 256, h = 256; 357 var shape = new three.shape(), w2 = w/2, h2 = h/2; 358 shape.moveto(-w2, h2); shape.lineto(w2, h2); shape.lineto(w2, -h2); 359 shape.lineto(-w2, -h2); shape.lineto(-w2, h2); 360 var geometry = new three.extrudebuffergeometry(shape, { 361 depth:0, 362 bevelthickness:2, 363 bevelsize:3, 364 bevelsegments:1 365 }); 366 var material = new three.meshlambertmaterial({map:this.textures.gate}); 367 var mesh = new three.mesh(geometry, [material, material]); 368 mesh.castshadow = true; 369 mesh.receiveshadow = true; 370 mesh.customdepthmaterial = new three.meshdepthmaterial({depthpacking: three.rgbadepthpacking}); 371 mesh.position.set(300, h/2+1, 0); 372 gro.add(mesh); 373 return mesh; 374 } 375 376 }
展示:
three.js 版本号为 114 (需要 php 服务器环境才能运行, 如果此demo不能运行 或 有什么疑问 请联系我: 3247940050@qq.com)
完整代码: https://pan.baidu.com/s/1qvt50gecnb2wtr8dommwmw
提取码: 53i3