javascript 基于原生登录注册系统搭建一个3d网页 ( three.js r114 初探 一)
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