主頁 > 前端設計 > 3D網頁小實驗——將txt配置文本轉化為3D陳列室

3D網頁小實驗——將txt配置文本轉化為3D陳列室

2020-12-04 08:05:49 前端設計

設計目標:借鑒前輩編程者的經驗將簡單的配置文本轉化為3D場景,并根據組態檔在場景中加入圖片和可播放的視頻,最終形成可瀏覽的3D陳列室,

一、使用效果

1、txt組態檔:

(博客園的富文本編輯器會改變txt文本的排版,所以用圖片方式呈現文本)

第一行表示陳列室的每一層前后最多有5個房間,左右最多有8個房間,接下來是第一層的地圖:“0”表示普通房間,“+、-、|”表示連接房間的通道,“#”表示地面有洞的房間可用來連接下一層,“^”表示頂部有洞的房間用來連接上一層,“//source”后面是本層的資源,用豎線分隔的引數依次表示前后位置、左右位置、資源“貼在”哪面墻上、資源型別、資源url,比如“//source:2|4|z|mp4|big_buck_bunny.mp4”即表示在第二行第四列的房間的z面(前面)貼上url為big_buck_bunny.mp4的mp4視頻,再下面則是-1層的地圖,

2、顯示效果

房間的整體效果如下:

渲染出了組態檔中設定的房間布局,可以通過修改代碼替換默認的草地和線框紋理,場景默認使用Babylon.js的自由相機進行控制,按“v”鍵可以啟用fps式控制,滑鼠移動直接控制視角,wasd控制前后左右,c和空格控制升降,同時啟用相機與墻壁的碰撞檢測阻止穿墻,再次按v鍵則可關閉fps控制,

進入第二層中間的房間可以看到房間兩側的視頻,點擊即可播放:

進入第二層右側的房間,可以看到相鄰的小房間融合為一個大房間:

可以通過https://ljzc002.github.io/Txt2room/HTML/PAGE/room.html查看在線實體,代碼沒有進行編譯可以直接在線除錯,在https://github.com/ljzc002/ljzc002.github.io/tree/master/Txt2room查看專案代碼,

二、代碼實作

1、建立房間零件的源網格(master mesh)

為了提高渲染效率,這里并沒有為每個房間建立獨立的mesh物件,而是將房間拆解為基礎的組成零件,對零件建立源網格,然后用WebGL的instance技術批量生成源網格的實體,

以下是生成預制件源網格的方法:

 1 var size_per_u=3;//1紋理坐標長度對應場景的3單位長度
 2 var size_per_v=3;
 3 var positions=[];
 4 var uvs=[];
 5 var normals=[];
 6 var indices=[];
 7 function initMeshClass()
 8 {//plan的基礎狀態是一個位于原點,面向z軸負方向的平面
 9     add_plan2({x:-4.5,y:4.5,z:0},{x:1.5,y:4.5,z:0},{x:1.5,y:1.5,z:0},{x:-4.5,y:1.5,z:0},0);
10     add_plan2({x:1.5,y:4.5,z:0},{x:4.5,y:4.5,z:0},{x:4.5,y:-1.5,z:0},{x:1.5,y:-1.5,z:0},4,6/size_per_u);
11     add_plan2({x:-1.5,y:-1.5,z:0},{x:4.5,y:-1.5,z:0},{x:4.5,y:-4.5,z:0},{x:-1.5,y:-4.5,z:0},8,3/size_per_u,6/size_per_v);
12     add_plan2({x:-4.5,y:1.5,z:0},{x:-1.5,y:1.5,z:0},{x:-1.5,y:-4.5,z:0},{x:-4.5,y:-4.5,z:0},12,0,3/size_per_v);
13     var mesh=vertexData2Mesh(positions, indices, normals, uvs,"class_hole",mat_grass);
14     mesh.setEnabled(false);//令源網格不顯示
15     // 很奇怪如果不對長通道設定mesh.setEnabled(false);則實體無法正常顯示,但其他類的實體則沒有這種問題,
16     //mesh.setEnabled(true);//默認就是這個
17     obj_meshclass["hole"]=mesh;//帶有洞的墻壁
18 
19     positions=[];//新建式清空,理論上不影響參考的資料
20     uvs=[];
21     normals=[];
22     indices=[];
23     add_plan2({x:-4.5,y:4.5,z:0},{x:4.5,y:4.5,z:0},{x:4.5,y:-4.5,z:0},{x:-4.5,y:-4.5,z:0},0);
24     var mesh=vertexData2Mesh(positions, indices, normals, uvs,"class_wall",mat_grass);
25     mesh.setEnabled(false);
26     obj_meshclass["wall"]=mesh;//墻壁
27 
28     positions=[];
29     uvs=[];
30     normals=[];
31     indices=[];
32     add_plan2({x:-1.5,y:1.5,z:0},{x:1.5,y:1.5,z:0},{x:1.5,y:-1.5,z:0},{x:-1.5,y:-1.5,z:0},0);
33     var mesh=vertexData2Mesh(positions, indices, normals, uvs,"class_smallwall",mat_grass);
34     mesh.setEnabled(false);
35     obj_meshclass["smallwall"]=mesh;//小塊墻壁
36 
37     positions=[];
38     uvs=[];
39     normals=[];
40     indices=[];
41     add_plan2({x:-1.5,y:1.5,z:-4.5},{x:-1.5,y:1.5,z:4.5},{x:1.5,y:1.5,z:4.5},{x:1.5,y:1.5,z:-4.5},0);
42     add_plan2({x:1.5,y:1.5,z:-4.5},{x:1.5,y:1.5,z:4.5},{x:1.5,y:-1.5,z:4.5},{x:1.5,y:-1.5,z:-4.5},4);
43     add_plan2({x:1.5,y:-1.5,z:-4.5},{x:1.5,y:-1.5,z:4.5},{x:-1.5,y:-1.5,z:4.5},{x:-1.5,y:-1.5,z:-4.5},8);
44     add_plan2({x:-1.5,y:-1.5,z:-4.5},{x:-1.5,y:-1.5,z:4.5},{x:-1.5,y:1.5,z:4.5},{x:-1.5,y:1.5,z:-4.5},12);
45     var mesh=vertexData2Mesh(positions, indices, normals, uvs,"class_channel",mat_frame);
46     //mesh.setEnabled(false);
47     // 很奇怪如果不對長通道設定mesh.setEnabled(false);則實體無法正常顯示,但其他類的實體則沒有這種問題,
48     mesh.setEnabled(false);
49     obj_meshclass["channel"]=mesh;//長通道
50 
51     positions=[];
52     uvs=[];
53     normals=[];
54     indices=[];
55     add_plan2({x:-1.5,y:1.5,z:1.5},{x:-1.5,y:1.5,z:4.5},{x:1.5,y:1.5,z:4.5},{x:1.5,y:1.5,z:1.5},0);
56     add_plan2({x:1.5,y:1.5,z:1.5},{x:1.5,y:1.5,z:4.5},{x:1.5,y:-1.5,z:4.5},{x:1.5,y:-1.5,z:1.5},4);
57     add_plan2({x:1.5,y:-1.5,z:1.5},{x:1.5,y:-1.5,z:4.5},{x:-1.5,y:-1.5,z:4.5},{x:-1.5,y:-1.5,z:1.5},8);
58     add_plan2({x:-1.5,y:-1.5,z:1.5},{x:-1.5,y:-1.5,z:4.5},{x:-1.5,y:1.5,z:4.5},{x:-1.5,y:1.5,z:1.5},12);
59     var mesh=vertexData2Mesh(positions, indices, normals, uvs,"class_shortchannel",mat_grass);
60     mesh.setEnabled(false);
61     obj_meshclass["shortchannel"]=mesh;//短通道
62 }

其中add_plan2方法用來根據四個給定的頂點生成平整的四邊形頂點資料:

 1 //平面四個頂點的坐標(從左上角開始順時針排列),第一個頂點的插入索引,uv紋理的坐標的偏移量
 2 function add_plan2(v1,v2,v3,v4,index,offsetu,offsetv)
 3 {
 4     positions.push(v1.x);
 5     positions.push(v1.y);
 6     positions.push(v1.z);
 7     positions.push(v2.x);
 8     positions.push(v2.y);
 9     positions.push(v2.z);
10     positions.push(v3.x);
11     positions.push(v3.y);
12     positions.push(v3.z);
13     positions.push(v4.x);
14     positions.push(v4.y);
15     positions.push(v4.z);
16     //使用和Babylon.js條帶網格相同的頂點順序
17     indices.push(index+3);
18     indices.push(index+2);
19     indices.push(index);
20     indices.push(index+1);
21     indices.push(index);
22     indices.push(index+2);
23     //根據頂點位置計算平整紋理坐標
24     //1234對應abcd
25     var vab=v3subtract(v2,v1);
26     var lab=v3length(vab);
27     var vac=v3subtract(v3,v1);
28     var lac=v3length(vac);
29     var vad=v3subtract(v4,v1);
30     var lad=v3length(vad);
31 
32     var BAC=Math.acos((vab.x*vac.x+vab.y*vac.y+vab.z*vac.z)/(lab*lac));
33     var BAD=Math.acos((vab.x*vad.x+vab.y*vad.y+vab.z*vad.z)/(lab*lad));
34     if(!offsetu)
35     {
36         offsetu=0;
37     }
38     if(!offsetv)
39     {
40         offsetv=0;
41     }
42     uvs.push(offsetu);
43     uvs.push(offsetv);
44     uvs.push(offsetu+lab/size_per_u);
45     uvs.push(offsetv);
46     uvs.push(offsetu+(lac*Math.cos(BAC)/size_per_u));
47     uvs.push(offsetv+(lac*Math.sin(BAC)/size_per_v));
48     uvs.push(offsetu+(lad*Math.cos(BAD)/size_per_u));
49     uvs.push(offsetv+(lad*Math.sin(BAD)/size_per_v));
50 }
51 function v3subtract(v1,v2)//向量相減
52 {
53     return {x:(v1.x-v2.x),y:(v1.y-v2.y),z:(v1.z-v2.z)}
54 }
55 function v3length(v)//計算向量長度
56 {
57     return Math.pow(v.x*v.x+v.y*v.y+v.z*v.z,0.5)
58 }

計算平整紋理坐標使用了向量點乘的性質:vab.x*vac.x+vab.y*vac.y+vab.z*vac.z=vab.vac=lab*lac*cosCAB

這使得我們可以根據三角形三個頂點的坐標計算出其中兩個向量的夾角,進而在這兩個向量確定的平面中計算出三個頂點的紋理坐標,

可以在https://forum.babylonjs.com/t/which-way-should-i-choose-to-make-a-custom-mesh-from-ribbon/10793查看一些關于為什么要進行紋理平整的討論,

vertexData2Mesh方法用來將生成的頂點資料轉化為網格,

 1 function vertexData2Mesh(positions, indices, normals, uvs,name,material)
 2 {
 3     var vertexData= https://www.cnblogs.com/ljzc002/p/new BABYLON.VertexData();//頂點資料物件
 4     BABYLON.VertexData.ComputeNormals(positions, indices, normals);//計演算法線
 5     BABYLON.VertexData._ComputeSides(0, positions, indices, normals, uvs);
 6     vertexData.indices = indices.concat();//索引
 7     vertexData.positions = positions.concat();
 8     vertexData.normals = normals.concat();//position改變法線也要改變!!!!
 9     vertexData.uvs = uvs.concat();
10     var mesh=new BABYLON.Mesh(name,scene);
11     vertexData.applyToMesh(mesh, true);
12     mesh.vertexData=https://www.cnblogs.com/ljzc002/p/vertexData;
13     mesh.material=material;
14     mesh.renderingGroupId=2;
15     return mesh;
16 }

最后mesh.setEnabled(false)用來隱藏源網格,

2、讀取配置文本,提取房間資訊

 1 var str=newland.importString("06.txt");
 2         //console.log(str);
 3         var arr=str.split("\r\n")//限于window作業系統下??
 4         var len=arr.length;
 5         for(var i=0;i<len;i++)//對于每一行
 6         {
 7             var line=arr[i];
 8             if(line.substring(0,2)=="//")
 9             {
10                 var arr2=line.substring(2).split("@");
11                 var len2=arr2.length;
12                 for(var j=0;j<len2;j++)
13                 {
14                     var obj=arr2[j];
15                     var arr3=obj.split(":");
16                     var ptype=arr3[0];
17                     var pvalue=https://www.cnblogs.com/ljzc002/p/arr3[1];
18                     if(ptype=="seg_z")
19                     {
20                         seg_z=parseInt(pvalue);
21                     }
22                     else if(ptype=="seg_x")
23                     {
24                         seg_x=parseInt(pvalue);
25                     }
26                     else if(ptype=="floor")//進入了一層
27                     {
28                         i=handleFloor(pvalue,arr,i+1);
29                     }
30                 }
31             }
32         }

其中importString是一個讀取服務端文本檔案的方法,其代碼如下:

1 newland.importString=function(url)
2 {
3     var xhr=new XMLHttpRequest;
4     xhr.open("GET",url,false);//第三個引數表示是同步加載
5     xhr.send(null);
6     var data=https://www.cnblogs.com/ljzc002/p/xhr.responseText;
7     return data;
8 }

讀入后一行行遍歷文本,發現“//floor”則開始處理這一層的房間資料:

 1 function handleFloor(int_floor,arr,index)
 2 {
 3     var floor=obj_building[int_floor];//在obj_building中保存所有房間資訊
 4     if(!floor)
 5     {
 6         obj_building[int_floor]={};
 7         floor=obj_building[int_floor];
 8     }
 9     var len=arr.length;
10     var count=0;
11     //繼續讀txt文本
12     for(var i=index;i<len;i++)
13     {
14         var line=arr[i];
15         count++;
16         if(count<=seg_z)
17         {
18 
19             if(!floor[count+""])
20             {
21                 floor[count+""]={}
22             }
23 
24             for(var j=0;j<seg_x;j++)
25             {
26                 if(line[j])
27                 {
28 
29                     floor[count+""][j+1+""]={type:line[j],arr_source:[]};//這個“陣列”都是從一開始的
30                     //addRoom(count-1,j);//行、列,規劃完畢后統一添加渲染
31                 }
32             }
33         }
34         else
35         {
36             if(line.substring(0,7)=="//floor")//查找到另一層
37             {
38                 return (index+count-2);
39             }
40             else if(line.substring(0,8)=="//source")//為這個房間設定資源
41             {
42                 //var arr2=line.split(":")[1].split("|");
43                 var arr2=line.substring(line.search(":")+1).split("|");
44                 if(floor[arr2[0]]&&floor[arr2[0]][arr2[1]])
45                 {
46                     var arr_source=floor[arr2[0]][arr2[1]].arr_source;//這個房間的資源串列
47                     var obj={};
48                     obj.sourceSide=arr2[2];
49                     obj.sourceType=arr2[3];
50                     obj.sourceUrl=arr2[4];
51                     arr_source.push(obj);
52                 }
53 
54             }
55         }
56 
57     }
58     return (len);//查找到檔案末尾
59 }

經過以上處理,組態檔中的房間資訊都被提取到obj_building中,

3、根據房間資訊排列源網格的實體,并放置資源,

代碼如下:(有一定冗余)

  1 function handleBuilding()
  2 {
  3     var len=0;
  4     for(var key in obj_building)
  5     {
  6         len++;//總層數
  7     }
  8     for(var key in obj_building)//對于每一層
  9     {
 10         var int_key=parseInt(key);
 11         var floor=obj_building[key];
 12         //尋找這一層的上下兩層,這里假設obj_building是沒有順序的
 13         var int_key_shang=int_key,int_key_xia=int_key;
 14         var floor_shang=null,floor_xia=null;
 15         //for(var i=int_key;i<)
 16         for(var key2 in obj_building)
 17         {
 18             var int_key2=parseInt(key2);
 19             if((int_key2>int_key)&&(int_key_shang==int_key||int_key_shang>int_key2))
 20             {
 21                 int_key_shang=int_key2;
 22                 floor_shang=obj_building[key2];
 23             }
 24             if((int_key2<int_key)&&(int_key_xia==int_key||int_key_xia<int_key2))
 25             {
 26                 int_key_shang=int_key2;
 27                 floor_xia=obj_building[key2];
 28             }
 29         }
 30         for(var i=1;i<=seg_z;i++)//對于本層的每一行房間
 31         {
 32             var row=floor[i+""];
 33             if(row)//如果有這一行
 34             {
 35                 for(var j=0;j<=seg_x;j++)//對于本行的每一個房間
 36                 {
 37                     var room=row[j+""];
 38                     //根據房間的型別不同決定是否要查看其周圍的房間
 39                     if(room)
 40                     {//@@@@普通房間,要考慮前后左右的四個房間狀態,要考慮資源放置
 41                         if(room.type=="0"||room.type=="#"||room.type=="^")
 42                         {
 43                             //room.arr_source=[];
 44                             //考慮前面
 45                             if(floor[i-1+""]&&floor[i-1+""][j+""])
 46                             {
 47                                 var room2=floor[i-1+""][j+""];
 48                                 if(!room2)//如果沒有東西,就是普通墻壁
 49                                 {
 50                                     //網格型別,實體名字,位置,姿態
 51                                     drawMesh("wall","wall_z_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(0.5-i)*sizez)
 52                                         ,new BABYLON.Vector3(0,0,0))
 53                                 }
 54 
 55                                 else if(room2.type=="|"||room2.type=="+")
 56                                 {
 57                                     drawMesh("hole","hole_z_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(0.5-i)*sizez)
 58                                         ,new BABYLON.Vector3(0,0,0))
 59                                 }
 60                                 else if(room2.type=="0"||room2.type=="#"||room2.type=="^")//旁邊也是一個房間則合并房間,不繪制墻壁
 61                                 {
 62 
 63                                 }
 64                                 else//默認繪制墻壁
 65                                 {
 66                                     drawMesh("wall","wall_z_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(0.5-i)*sizez)
 67                                         ,new BABYLON.Vector3(0,0,0))
 68                                 }
 69                             }
 70                             else//默認繪制墻壁
 71                             {
 72                                 drawMesh("wall","wall_z_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(0.5-i)*sizez)
 73                                     ,new BABYLON.Vector3(0,0,0))
 74                             }
 75                             //后面
 76                             if(floor[i+1+""]&&floor[i+1+""][j+""])
 77                             {
 78                                 var room2=floor[i+1+""][j+""];
 79                                 if(!room2)//如果沒有東西,就是普通墻壁
 80                                 {
 81                                     //網格型別,實體名字,位置,姿態
 82                                     drawMesh("wall","wall_z-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(-0.5-i)*sizez)
 83                                         ,new BABYLON.Vector3(0,0,0))
 84                                 }
 85 
 86                                 else if(room2.type=="|"||room2.type=="+")
 87                                 {
 88                                     drawMesh("hole","hole_z-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(-0.5-i)*sizez)
 89                                         ,new BABYLON.Vector3(0,0,0))
 90                                 }
 91                                 else if(room2.type=="0"||room2.type=="#"||room2.type=="^")//旁邊也是一個房間則合并房間,不繪制墻壁
 92                                 {
 93 
 94                                 }
 95                                 else//默認繪制墻壁
 96                                 {
 97                                     drawMesh("wall","wall_z-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(-0.5-i)*sizez)
 98                                         ,new BABYLON.Vector3(0,0,0))
 99                                 }
100                             }
101                             else//默認繪制墻壁
102                             {
103                                 drawMesh("wall","wall_z-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(-0.5-i)*sizez)
104                                     ,new BABYLON.Vector3(0,0,0))
105                             }
106                             //左邊
107                             if(floor[i+""][j-1+""])
108                             {
109                                 var room2=floor[i+""][j-1+""];
110                                 if(!room2)//如果沒有東西,就是普通墻壁
111                                 {
112                                     //網格型別,實體名字,位置,姿態
113                                     drawMesh("wall","wall_x-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j-0.5)*sizex,int_key*sizey,(-i)*sizez)
114                                         ,new BABYLON.Vector3(0,Math.PI/2,0))
115                                 }
116 
117                                 else if(room2.type=="-"||room2.type=="+")
118                                 {
119                                     drawMesh("hole","hole_x-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j-0.5)*sizex,int_key*sizey,(-i)*sizez)
120                                         ,new BABYLON.Vector3(0,Math.PI/2,0))
121                                 }
122                                 else if(room2.type=="0"||room2.type=="#"||room2.type=="^")//旁邊也是一個房間則合并房間,不繪制墻壁
123                                 {
124 
125                                 }
126                                 else//默認繪制墻壁
127                                 {
128                                     drawMesh("wall","wall_x-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j-0.5)*sizex,int_key*sizey,(-i)*sizez)
129                                         ,new BABYLON.Vector3(0,Math.PI/2,0))
130                                 }
131                             }
132                             else//默認繪制墻壁
133                             {
134                                 drawMesh("wall","wall_x-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j-0.5)*sizex,int_key*sizey,(-i)*sizez)
135                                     ,new BABYLON.Vector3(0,Math.PI/2,0))
136                             }
137                             //右邊
138                             if(floor[i+""][j+1+""])
139                             {
140                                 var room2=floor[i+""][j+1+""];
141                                 if(!room2)//如果沒有東西,就是普通墻壁
142                                 {
143                                     //網格型別,實體名字,位置,姿態
144                                     drawMesh("wall","wall_x_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j+0.5)*sizex,int_key*sizey,(-i)*sizez)
145                                         ,new BABYLON.Vector3(0,Math.PI/2,0))
146                                 }
147 
148                                 else if(room2.type=="-"||room2.type=="+")
149                                 {
150                                     drawMesh("hole","hole_x_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j+0.5)*sizex,int_key*sizey,(-i)*sizez)
151                                         ,new BABYLON.Vector3(0,Math.PI/2,0))
152                                 }
153                                 else if(room2.type=="0"||room2.type=="#"||room2.type=="^")//旁邊也是一個房間則合并房間,不繪制墻壁
154                                 {
155 
156                                 }
157                                 else//默認繪制墻壁
158                                 {
159                                     drawMesh("wall","wall_x_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j+0.5)*sizex,int_key*sizey,(-i)*sizez)
160                                         ,new BABYLON.Vector3(0,Math.PI/2,0))
161                                 }
162                             }
163                             else//默認繪制墻壁
164                             {
165                                 drawMesh("wall","wall_x_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j+0.5)*sizex,int_key*sizey,(-i)*sizez)
166                                     ,new BABYLON.Vector3(0,Math.PI/2,0))
167                             }
168                             //上面
169                             if(room.type=="^")
170                             {
171                                 drawMesh("hole","hole_y_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j)*sizex,(int_key+0.5)*sizey,(-i)*sizez)
172                                     ,new BABYLON.Vector3(Math.PI/2,0,0))
173                                 //還要負責向上連接
174                                 if(floor_shang)
175                                 {
176                                     for(var k=int_key+1;k<int_key_shang;k++)
177                                     {
178                                         drawMesh("channel","channel_^_"+k+"_"+i+"_"+j,new BABYLON.Vector3((j)*sizex,(k)*sizey,(-i)*sizez)
179                                             ,new BABYLON.Vector3(Math.PI/2,0,0))
180                                     }
181                                 }
182                                 //暫時不設定彈射器,使用失重模式
183                             }
184                             else
185                             {
186                                 drawMesh("wall","wall_y_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j)*sizex,(int_key+0.5)*sizey,(-i)*sizez)
187                                     ,new BABYLON.Vector3(Math.PI/2,0,0))
188                             }
189                             //下面
190                             if(room.type=="#")
191                             {
192                                 drawMesh("hole","hole_y-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j)*sizex,(int_key-0.5)*sizey,(-i)*sizez)
193                                     ,new BABYLON.Vector3(Math.PI/2,0,0))
194                             }
195                             else
196                             {
197                                 drawMesh("wall","wall_y-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j)*sizex,(int_key-0.5)*sizey,(-i)*sizez)
198                                     ,new BABYLON.Vector3(Math.PI/2,0,0))
199                             }
200                             //翻轉方向會影響碰撞檢測嗎?
201                             //最后處理資源
202                         }
203                         //@@@@表示通道的三種符號,要考慮其前后左右的位置
204                         else if(room.type=="-"||room.type=="+"||room.type=="|")
205                         {
206                             if(room.type=="-")
207                             {//橫向長通道
208                                 drawMesh("channel","channel_-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j)*sizex,(int_key)*sizey,(-i)*sizez)
209                                     ,new BABYLON.Vector3(0,Math.PI/2,0))
210                             }
211                             else if(room.type=="|")
212                             {//縱向長通道
213                                 drawMesh("channel","channel_|_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j)*sizex,(int_key)*sizey,(-i)*sizez)
214                                     ,new BABYLON.Vector3(0,0,0))
215                             }
216                             else
217                             {//十字連接件
218                                 //考慮前面
219                                 if(floor[i-1+""]&&floor[i-1+""][j+""])
220                                 {
221                                     var room2=floor[i-1+""][j+""];
222                                     if(!room2)//如果沒有東西,就是普通墻壁
223                                     {
224                                         //網格型別,實體名字,位置,姿態
225                                         drawMesh("smallwall","smallwall_z_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(0.5/3-i)*sizez)
226                                             ,new BABYLON.Vector3(0,0,0))
227                                     }
228 
229                                     else if(room2.type=="|"||room2.type=="+"||room2.type=="0"||room2.type=="#"||room2.type=="^")
230                                     {//短通道自帶位移
231                                         drawMesh("shortchannel","shortchannel_z_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(-i)*sizez)
232                                             ,new BABYLON.Vector3(0,0,0))
233                                     }
234                                     else//默認繪制小型墻壁
235                                     {
236                                         drawMesh("smallwall","smallwall_z_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(0.5/3-i)*sizez)
237                                             ,new BABYLON.Vector3(0,0,0))
238                                     }
239                                 }
240                                 else//默認繪制小型墻壁
241                                 {
242                                     drawMesh("smallwall","smallwall_z_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(0.5/3-i)*sizez)
243                                         ,new BABYLON.Vector3(0,0,0))
244                                 }
245                                 //后面
246                                 if(floor[i+1+""]&&floor[i+1+""][j+""])
247                                 {
248                                     var room2=floor[i+1+""][j+""];
249                                     if(!room2)//如果沒有東西,就是普通墻壁
250                                     {
251                                         //網格型別,實體名字,位置,姿態
252                                         drawMesh("smallwall","smallwall_z-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(-0.5/3-i)*sizez)
253                                             ,new BABYLON.Vector3(0,0,0))
254                                     }
255 
256                                     else if(room2.type=="|"||room2.type=="+"||room2.type=="0"||room2.type=="#"||room2.type=="^")
257                                     {
258                                         drawMesh("shortchannel","shortchannel_z-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(-i)*sizez)
259                                             ,new BABYLON.Vector3(0,Math.PI,0))
260                                     }
261                                     else//默認繪制小型墻壁
262                                     {
263                                         drawMesh("smallwall","smallwall_z-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(-0.5/3-i)*sizez)
264                                             ,new BABYLON.Vector3(0,0,0))
265                                     }
266                                 }
267                                 else//默認繪制小型墻壁
268                                 {
269                                     drawMesh("smallwall","smallwall_z-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3(j*sizex,int_key*sizey,(-0.5/3-i)*sizez)
270                                         ,new BABYLON.Vector3(0,0,0))
271                                 }
272                                 //左邊
273                                 if(floor[i+""][j-1+""])
274                                 {
275                                     var room2=floor[i+""][j-1+""];
276                                     if(!room2)//如果沒有東西,就是小型墻壁
277                                     {
278                                         //網格型別,實體名字,位置,姿態
279                                         drawMesh("smallwall","smallwall_x-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j-0.5/3)*sizex,int_key*sizey,(-i)*sizez)
280                                             ,new BABYLON.Vector3(0,Math.PI/2,0))
281                                     }
282 
283                                     else if(room2.type=="-"||room2.type=="+"||room2.type=="0"||room2.type=="#"||room2.type=="^")
284                                     {
285                                         drawMesh("shortchannel","shortchannel_x-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j)*sizex,int_key*sizey,(-i)*sizez)
286                                             ,new BABYLON.Vector3(0,-Math.PI/2,0))
287                                     }
288                                     else//默認繪制小型墻壁
289                                     {
290                                         drawMesh("smallwall","smallwall_x-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j-0.5/3)*sizex,int_key*sizey,(-i)*sizez)
291                                             ,new BABYLON.Vector3(0,Math.PI/2,0))
292                                     }
293                                 }
294                                 else//默認繪制小型墻壁
295                                 {
296                                     drawMesh("smallwall","smallwall_x-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j-0.5/3)*sizex,int_key*sizey,(-i)*sizez)
297                                         ,new BABYLON.Vector3(0,Math.PI/2,0))
298                                 }
299                                 //右邊
300                                 if(floor[i+""][j+1+""])
301                                 {
302                                     var room2=floor[i+""][j+1+""];
303                                     if(!room2)//如果沒有東西,就是普通墻壁
304                                     {
305                                         //網格型別,實體名字,位置,姿態
306                                         drawMesh("smallwall","smallwall_x_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j+0.5/3)*sizex,int_key*sizey,(-i)*sizez)
307                                             ,new BABYLON.Vector3(0,Math.PI/2,0))
308                                     }
309 
310                                     else if(room2.type=="-"||room2.type=="+"||room2.type=="0"||room2.type=="#"||room2.type=="^")
311                                     {
312                                         drawMesh("shortchannel","shortchannel_x_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j)*sizex,int_key*sizey,(-i)*sizez)
313                                             ,new BABYLON.Vector3(0,Math.PI/2,0))
314                                     }
315                                     else//默認繪制墻壁
316                                     {
317                                         drawMesh("smallwall","smallwall_x_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j+0.5/3)*sizex,int_key*sizey,(-i)*sizez)
318                                             ,new BABYLON.Vector3(0,Math.PI/2,0))
319                                     }
320                                 }
321                                 else//默認繪制墻壁
322                                 {
323                                     drawMesh("smallwall","smallwall_x_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j+0.5/3)*sizex,int_key*sizey,(-i)*sizez)
324                                         ,new BABYLON.Vector3(0,Math.PI/2,0))
325                                 }
326                                 //上面
327                                 drawMesh("smallwall","smallwall_y_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j)*sizex,(int_key+0.5/3)*sizey,(-i)*sizez)
328                                     ,new BABYLON.Vector3(Math.PI/2,0,0))
329                                 //下面
330                                 drawMesh("smallwall","smallwall_y-_"+int_key+"_"+i+"_"+j,new BABYLON.Vector3((j)*sizex,(int_key-0.5/3)*sizey,(-i)*sizez)
331                                     ,new BABYLON.Vector3(Math.PI/2,0,0))
332 
333                             }
334                         }
335                         //如果這個房間有資源
336                         if(room.arr_source)
337                         {
338                             var arr_source=room.arr_source;
339                             var len=arr_source.length;
340                             for(var k=0;k<len;k++)
341                             {
342                                 var source=arr_source[k];
343                                 if(source.sourceType=="mp4"||source.sourceType=="jpg"||source.sourceType=="png")
344                                 {
345                                     var mesh_plan=new BABYLON.MeshBuilder.CreatePlane(source.sourceType+"_"+source.sourceSide+"_"+int_key+"_"+i+"_"+j,
346                                         {height:4.5,width:8},scene);//建立一個平面網格用來展示資源
347                                     var pos={x:0,y:0,z:0},rot=new BABYLON.Vector3(0,0,0);
348                                     if(source.sourceSide=="z")//根據資源所在的墻壁不同調整資源網格的位置和姿態
349                                     {
350                                         pos.z=0.4
351                                     }else if(source.sourceSide=="z-")
352                                     {
353                                         pos.z=-0.4;
354                                         rot.y=Math.PI;
355                                     }
356                                     else if(source.sourceSide=="x")
357                                     {
358                                         pos.x=0.4;
359                                         rot.y=-Math.PI/2;
360                                     }
361                                     else if(source.sourceSide=="x-")
362                                     {
363                                         pos.x=-0.4;
364                                         rot.y=Math.PI/2;
365                                     }
366                                     else if(source.sourceSide=="y")
367                                     {
368                                         pos.y=0.4;
369                                         rot.x=Math.PI/2;
370                                     }
371                                     else if(source.sourceSide=="z-")
372                                     {
373                                         pos.y=-0.4;
374                                         rot.x=-Math.PI/2;
375                                     }
376                                     mesh_plan.position=new BABYLON.Vector3((j+pos.x)*sizex,(int_key+pos.y)*sizey,(-i+pos.z)*sizez);
377                                     mesh_plan.rotation=rot;
378                                     mesh_plan.renderingGroupId=2;
379                                     if(source.sourceType=="jpg"||source.sourceType=="png")
380                                     {
381                                         var materialf = new BABYLON.StandardMaterial("mat_"+source.sourceSide+"_"+int_key+"_"+i+"_"+j, scene);
382 
383                                         materialf.diffuseTexture = new BABYLON.Texture(source.sourceUrl, scene);
384                                         materialf.diffuseTexture.hasAlpha = false;
385                                         materialf.backFaceCulling = true;
386                                         materialf.freeze();
387                                         mesh_plan.material =materialf;
388                                     }
389                                     else if(source.sourceType=="mp4")
390                                     {
391                                         var mat = new BABYLON.StandardMaterial("mat_"+source.sourceSide+"_"+int_key+"_"+i+"_"+j, scene);
392                                         //從Chrome 66開始為了避免標簽產生隨機噪音禁止沒有互動前使用js播放視頻,所以后面要監聽點擊啟動播放
393                                         var videoTexture = new BABYLON.VideoTexture("video_"+source.sourceSide+"_"+int_key+"_"+i+"_"+j, [source.sourceUrl], scene, true, false);
394                                         //videoTexture.video.autoplay=false;//這兩個設定
395                                         //videoTexture.video.muted=true;不起作用
396                                         mat.diffuseTexture = videoTexture;//Babylon.js視頻紋理
397                                         mat.emissiveColor=new BABYLON.Color3(1,1,1);
398                                         //監聽到互動需求
399                                         // videoTexture.onUserActionRequestedObservable.add(() => {
400                                         //     scene.onPointerDown = function (evt) {
401                                         //         if(evt.pickInfo.pickedMesh == mesh_plan)
402                                         //         {
403                                         //             if(videoTexture.video.paused)
404                                         //             {
405                                         //                 videoTexture.video.play();
406                                         //             }
407                                         //             else
408                                         //             {
409                                         //                 videoTexture.video.pause();
410                                         //             }
411                                         //         }
412                                         //
413                                         //     }
414                                         // });
415                                         //mat.emissiveTexture= videoTexture;
416                                         mesh_plan.material =mat;
417                                         obj_videos[mesh_plan.name]=videoTexture;
418                                         if(false)
419                                         {
420                                             scene.onPointerDown = function (evt) {//這個evt是dom的,不會有pickInfo!!
421                                                 if(evt.pickInfo&&(evt.pickInfo.pickedMesh == mesh_plan))
422                                                 {
423                                                     if(videoTexture.video.paused)
424                                                     {
425                                                         videoTexture.video.play();
426                                                     }
427                                                     else
428                                                     {
429                                                         videoTexture.video.pause();
430                                                     }
431                                                 }
432                                             }
433                                         }
434 
435                                     }
436                                 }
437                             }
438                         }
439 
440                     }
441                 }
442             }
443         }
444     }
445 }

drawMesh方法用來在指定位置生成指定源網格的實體:

1 function drawMesh(type,name,pos,rot)
2     {
3         var instance=obj_meshclass[type].createInstance(name);
4         instance.position=pos;
5         instance.rotation=rot;
6     }

在完成零件組裝后,再根據資源資訊向設定的位置添加資源,

4、運動控制與碰撞檢測

監聽操作者的滑鼠和鍵盤操作:

 1 var node_temp;
 2 function InitMouse()
 3 {
 4     canvas.addEventListener("blur",function(evt){//監聽失去焦點
 5         releaseKeyStateOut();
 6     })
 7     canvas.addEventListener("focus",function(evt){//改為監聽獲得焦點,因為除錯失去焦點時事件的先后順序不好說
 8         releaseKeyStateIn();
 9     })
10 
11     //scene.onPointerPick=onMouseClick;//如果不attachControl onPointerPick不會被觸發,并且onPointerPick必須pick到mesh上才會被觸發
12     canvas.addEventListener("click", function(evt) {//這個監聽也會在點擊GUI按鈕時觸發!!
13         onm ouseClick(evt);//
14     }, false);
15     canvas.addEventListener("dblclick", function(evt) {//是否要用到滑鼠雙擊??
16         onm ouseDblClick(evt);//
17     }, false);
18     scene.onPointerMove=onMouseMove;//Babylon.js的事件監聽屬性
19     scene.onPointerDown=onMouseDown;
20     scene.onPointerUp=onMouseUp;
21     scene.onKeyDown=onKeyDown;
22     scene.onKeyUp=onKeyUp;
23     node_temp=new BABYLON.TransformNode("node_temp",scene);//用來提取相機的姿態矩陣
24     node_temp.rotation=camera0.rotation;
25 }

滑鼠點擊控制視頻播放:

 1 function onm ouseDblClick(evt)
 2 {
 3     var pickInfo = scene.pick(scene.pointerX, scene.pointerY, null, false, camera0);
 4     if(pickInfo.hit)
 5     {
 6         var mesh = pickInfo.pickedMesh;
 7         if(mesh.name.split("_")[0]=="mp4")//重放視頻
 8         {
 9             if(obj_videos[mesh.name])
10             {
11                 var videoTexture=obj_videos[mesh.name];
12 
13                     videoTexture.video.currentTime =0;
14 
15             }
16         }
17     }
18 }
19 function onm ouseClick(evt)
20 {
21     var pickInfo = scene.pick(scene.pointerX, scene.pointerY, null, false, camera0);
22     if(pickInfo.hit)
23     {
24         var mesh = pickInfo.pickedMesh;
25         if(mesh.name.split("_")[0]=="mp4")//啟停視頻
26         {
27             if(obj_videos[mesh.name])
28             {
29                 var videoTexture=obj_videos[mesh.name];
30                 if(videoTexture.video.paused)
31                 {
32                     videoTexture.video.play();
33                 }
34                 else
35                 {
36                     videoTexture.video.pause();
37                 }
38             }
39         }
40     }
41 
42 }

在fps模式下通過滑鼠移動控制相機視角

 1 var lastPointerX,lastPointerY;
 2 var flag_view="free"//free表示默認的自由移動狀態,locked表示鎖定滑鼠的fps模式狀態
 3 var flag_locked;
 4 var obj_keystate=[];
 5 function onm ouseMove(evt)
 6 {
 7 
 8     if(flag_view=="locked")
 9     {
10         evt.preventDefault();
11         //繞y軸的旋轉角度是根據x坐標計算的
12         var rad_y=((scene.pointerX-lastPointerX)/window.innerWidth)*(Math.PI/1);//將滑鼠位置的變化轉化為相機視角的變化
13         var rad_x=((scene.pointerY-lastPointerY)/window.innerHeight)*(Math.PI/1);
14         camera0.rotation.y+=rad_y;
15         camera0.rotation.x+=rad_x;
16     }
17     lastPointerX=scene.pointerX;
18     lastPointerY=scene.pointerY;
19 }
20 function onm ouseDown(evt)
21 {
22     if(flag_view=="locked") {
23         evt.preventDefault();
24     }
25 }
26 function onm ouseUp(evt)
27 {
28     if(flag_view=="locked") {
29         evt.preventDefault();
30     }
31 }

記錄鍵盤按鍵狀態

 1 function onKeyDown(event)
 2 {
 3     if(flag_view=="locked") {
 4         event.preventDefault();
 5         var key = event.key;
 6         obj_keystate[key] = 1;//1表示按下
 7     }
 8 }
 9 function onKeyUp(event)
10 {
11     var key = event.key;
12     if(key=="v"||key=="Escape")//按v鍵開閉fps模式
13     {
14         event.preventDefault();
15         if(flag_view=="locked")
16         {
17             flag_view="free";
18             document.exitPointerLock();
19         }
20         else if(flag_view=="free")
21         {
22             flag_view="locked";
23             canvas.requestPointerLock();
24         }
25     }
26     if(flag_view=="locked") {
27         event.preventDefault();
28 
29         obj_keystate[key] = 0;
30     }
31 }

接下來在渲染回圈中根據控制輸入確定相機的位移:

var flag_speed=1;
                //var m_view=camera0.getViewMatrix();
                //var m_view=camera0.getProjectionMatrix();
                var m_view=node_temp.getWorldMatrix();
                //只檢測其運行方向?-》相對論問題!《-先假設直接外圍環境不移動
                if(obj_keystate["Shift"]==1)//Shift+w的event.key不是Shift和w,而是W!!!!
                {
                    flag_speed=5;//加速移動
                }
                var delta=engine.getDeltaTime();//兩渲染幀之間的時間間隔(毫秒)
                //console.log(delta);
                flag_speed=flag_speed*engine.getDeltaTime()/10;
                var v_temp=new BABYLON.Vector3(0,0,0);
                if(obj_keystate["w"]==1)
                {
                    v_temp.z+=0.1*flag_speed;

                }
                if(obj_keystate["s"]==1)
                {
                    v_temp.z-=0.1*flag_speed;
                }
                if(obj_keystate["d"]==1)
                {
                    v_temp.x+=0.05*flag_speed;
                }
                if(obj_keystate["a"]==1)
                {
                    v_temp.x-=0.05*flag_speed;
                }
                if(obj_keystate[" "]==1)
                {
                    v_temp.y+=0.05*flag_speed;
                }
                if(obj_keystate["c"]==1)
                {
                    v_temp.y-=0.05*flag_speed;
                }

                //camera0.position=camera0.position.add(BABYLON.Vector3.TransformCoordinates(v_temp,camera0.getWorldMatrix()).subtract(camera0.position));
                //engine.getDeltaTime()
          
                var pos_temp=camera0.position.add(BABYLON.Vector3.TransformCoordinates(v_temp,m_view));

根據按鍵狀態和兩幀之間的時間計算出相機在這一幀內的位移向量,需要注意的是這個位移向量以相機的區域坐標系為參考,為了在世界坐標系中使用它,建立了一個node_temp節點專門用來保存相機的姿態矩陣,對位移向量施加這個矩陣變化將它轉化為世界坐標系中的位移矩陣,

接下來使用射線進行簡單的碰撞檢測:

 1 var direction=pos_temp.subtract(pos_last);//pos_last是上一幀的相機位置,取新位置向量減舊位置向量的結果為物體的運動方向
 2                 //var direction=BABYLON.Vector3.TransformCoordinates(v_temp,m_view);//一次性計算的好處是只需繪制一條射線,缺點是容易射空
 3                 var ray = new BABYLON.Ray(camera0.position, direction, 1);//從camera0.position位置向direction方向,繪制長度為1的‘射線’
 4                 var arr=scene.multiPickWithRay(ray);
 5                 arr.sort(sort_compare)//按距離從近到遠排序
 6                 var len=arr.length;
 7 
 8                 var flag_hit=false;
 9                 for(var k=0;k<len;k++)//對于這條射線擊中的每個三角形
10                     {
11                         var hit=arr[k];
12                         var mesh=hit.pickedMesh;
13                         var distance=hit.distance;
14                         if(mesh||mesh.name)//暫不限制mesh種類
15                         {
16                             console.log(mesh.name);
17                             flag_hit=true;
18                             break;
19                         }
20                     }
21                 if(!flag_hit)//如果沒有被阻攔,則替換位置
22                 {
23                     camera0.position=pos_temp;
24                 }
25                 else
26                 {
27                     camera0.position=pos_last;//回溯的太遠了
28                 }

以上渲染回圈中的運動控制代碼主要在fps模式下生效,嘗試通過在檢測到碰撞時呼叫camera0.position=pos_last;來阻止自由相機穿墻,但效果并不好,

三、總結:

編程結果基本達到設計目標,但在代碼冗余、功能細節除錯方面尚有不足,接下來可以考慮向程式中添加模型資源作為‘雕塑’展示、添加更多型別的零件、添加重力效果、添加WebSocket互動等,

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/229734.html

標籤:HTML5

上一篇:怎么使用 Observable 獲得 JSON 資料

下一篇:uni-app開發經驗分享八: 實作微信APP支付的全程序詳解

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more