写在前面
本章我们来讲解下如何添加货架货物和显示各类信息的侧边栏。效果如下图所示:
2019.11.26 更新:我最近建立了个人网站,大家可以访问下面的链接查看演示
3D仓库演示
2019.11.28 更新:代码和图片资源等已上传至GitHub
https://github.com/xiao149/ThreeJsDemo
其中货架的层数和每层的货位数量是可以自定义的,比如我这边就是三层每层两个货位。方便起见我在每个货位上都放置了货物,大家当然可以自行选择。左上角的侧边栏使用了ThreeJs官方的dat.gui,这里为了演示也只是随便列了几个信息,当点击货物时相关信息便会显示出来。
创建货架对象
创建货架、货位类
因为我本身主要从事JAVA后台的开发,对前台一知半解,所以这部分写得不好也请各位轻喷,或许是受JAVA对象类的影响,我想在JS中也创建对象,比如货架的对象,货位的对象。奈何我水平有限,不知如何在JS中创建对象,只能用一些比较“愚蠢”的方式实现:
//创建货架对象
function shelf(storageZoneId, shelfId, shelfName,
planeLength , planeWidth , planeHeight ,
holderLength , holderWidth , holderHeight ,
positionX , positionY , positionZ ,
layerNum , columnNum)
{
this.storageZoneId=storageZoneId;
this.shelfId=shelfId;
this.shelfName=shelfName;
this.planeLength=planeLength;
this.planeWidth=planeWidth;
this.planeHeight=planeHeight;
this.holderLength=holderLength;
this.holderWidth=holderWidth;
this.holderHeight=holderHeight;
this.positionX=positionX;
this.positionY=positionY;
this.positionZ=positionZ;
this.layerNum=layerNum;
this.columnNum=columnNum;
}
//根据货架编码获取货架对象
function getShelfById(shelfId) {
for(var i = 0; i < shelfSize; i++){
if(shelfList[i].shelfId == shelfId){
return shelfList[i];
}
}
}
//创建货位对象
function storageUnit(storageZoneId, shelfId, shelfName,
inLayerNum , inColumnNum ,
positionX , positionY , positionZ, storageUnitId)
{
this.storageZoneId=storageZoneId;
this.shelfId=shelfId;
this.shelfName=shelfName;
this.inLayerNum=inLayerNum;
this.inColumnNum=inColumnNum;
this.positionX=positionX;
this.positionY=positionY;
this.positionZ=positionZ;
this.storageUnitId=storageUnitId;
}
//根据货架ID、层数、列数获取货位对象
function getStorageUnitById(shelfId,inLayerNum,inColumnNum) {
for(var i = 0; i < storageUnitSize; i++){
if(storageUnitList[i].shelfId == shelfId && storageUnitList[i].inLayerNum == inLayerNum && storageUnitList[i].inColumnNum == inColumnNum){
return storageUnitList[i];
}
}
}
//根据库位编码获取货位对象
function getStorageUnitByUnitId(storageUnitId) {
for(var i = 0; i < storageUnitSize; i++){
if(storageUnitList[i].storageUnitId == storageUnitId){
return storageUnitList[i];
}
}
}
上述的创建货架货位对象有点像创建JAVA类的初始化,对象的获取也是仿照了JAVA的GET方法。这样在需要创建货架货位的地方只需要调用对象的初始化方法,赋予相应的值。在需要获取对象的地方调用GET方法就好了。
根据配置添加货架
我们创建的货架没有使用外部导入的3D模型,全部使用ThreeJs创建各类长方体拼接而成,比如单层货架就是一个长方体作货架板面,再加上四个小长方体作支架。代码如下:
//region 货架货位
/** 放置单层货架 */
/** x,y,z 整个模型在场景中的位置 */
/** plane_x,plane_y,plane_z 货架板面的长高宽 */
/** holder_x,holder_y,holder_z 货架支架的长高宽 */
/** scene,name,num 要添加的场景,货架的名字,单层货架的库位数量 */
function addRack(x,y,z,plane_x,plane_y,plane_z,holder_x,holder_y,holder_z,scene,name,num) {
var plane = new THREE.BoxGeometry( plane_x, plane_y, plane_z/num );
var gz = [];
for(var i = 0; i < num; i++){
gz.push( z + plane_z/num/2 + (plane_z/num)*i );
var obj = new THREE.Mesh( plane, RackMat );
obj.position.set(x , y, gz[i]) ;
var msg = name+"$"+(2-i);
var storageUnitId = msg.split("$")[1] + "$" + msg.split("$")[3] + "$" + msg.split("$")[4];
//添加货位
var storageUnit_obj = new storageUnit(msg.split("$")[0],
msg.split("$")[1],
msg.split("$")[2],
msg.split("$")[3],
msg.split("$")[4],
x, y, gz[i], storageUnitId);
storageUnitList.push(storageUnit_obj);
storageUnitSize++;
var Unit = getStorageUnitById(msg.split("$")[1],msg.split("$")[3],msg.split("$")[4]);
obj.name = "货位"+"$"+Unit.storageUnitId;
scene.add(obj);
}
var holder = new THREE.BoxGeometry( holder_x, holder_y, holder_z );
var obj2 = new THREE.Mesh( holder, RackMat2 );
var obj3 = new THREE.Mesh( holder, RackMat2 );
var obj4 = new THREE.Mesh( holder, RackMat2 );
var obj5 = new THREE.Mesh( holder, RackMat2 );
obj2.position.set(x-plane_x/2+holder_x/2,y-holder_y/2-plane_y/2,z+holder_z/2);
obj3.position.set(x+plane_x/2-holder_x/2,y-holder_y/2-plane_y/2,z+holder_z/2);
obj4.position.set(x-plane_x/2+holder_x/2,y-holder_y/2-plane_y/2,z+plane_z-holder_z/2);
obj5.position.set(x+plane_x/2-holder_x/2,y-holder_y/2-plane_y/2,z+plane_z-holder_z/2);
scene.add(obj2);scene.add(obj3);scene.add(obj4);scene.add(obj5);
}
/** 放置一叠货架 */
/** stack_num 货架的叠数 */
function addStackOfRack(x,y,z,plane_x,plane_y,plane_z,holder_x,holder_y,holder_z,scene,name,num,stack_num) {
for(var i = 0; i < stack_num; i++){
addRack(x,y*(i+1),z,plane_x,plane_y,plane_z,holder_x,holder_y,holder_z,scene,name+"$"+(i+1),num);
}
}
/** 根据货架配置添加货架 */
function addShelf(scene) {
var shelf_list = new Array();
shelf_list.push({StorageZoneId:'Z1',shelfId:'A2',shelfName:'货架A2',x:0,y:27,z:0});
shelfSize = shelf_list.length;
for(var i = 0; i < shelfSize; i++){
var shelf_obj = new shelf(shelf_list[i].StorageZoneId,
shelf_list[i].shelfId,
shelf_list[i].shelfName,
PLANE_LENGTH,PLANE_WIDTH,PLANE_HEIGHT,
HOLDER_LENGTH,HOLDER_WIDTH,HOLDER_HEIGHT,
shelf_list[i].x,
shelf_list[i].y,
shelf_list[i].z,
LAYER_NUM,COLUMN_NUM);
shelfList.push(shelf_obj);
}
for(var i = 0;i < shelfSize; i++){
addStackOfRack(shelfList[i].positionX,shelfList[i].positionY,shelfList[i].positionZ,shelfList[i].planeLength,shelfList[i].planeHeight,shelfList[i].planeWidth,shelfList[i].holderLength,shelfList[i].holderHeight,shelfList[i].holderWidth,scene,shelfList[i].storageZoneId+"$"+shelfList[i].shelfId+"$"+shelfList[i].shelfName,shelfList[i].columnNum,shelfList[i].layerNum);
}
}
在HTML文件的init()
方法中添加addShelf(scene);
就可以实现添加货架的功能啦。
在addShelf
方法中我原本是读取数据库货架的配置的,但是既然拿出来展示了肯定就不能用数据库了,所以我只能写死一些数据。
var shelf_list = new Array();
shelf_list.push({StorageZoneId:'Z1',shelfId:'A2',shelfName:'货架A2',x:0,y:27,z:0});
这行是添加一个货架,参数的意义是:货架所在的库区、货架的编号、货架的中文名、货架的xyz位置。效果如下:
点击货位能以高亮显示,并显示出货位的信息:货位&货架编码&货位的所在层&所在列。
若是想添加多个货架也很简单,在addShelf
方法里加上:
var shelf_list = new Array();
shelf_list.push({StorageZoneId:'Z1',shelfId:'A1',shelfName:'货架A1',x:-100,y:27,z:0});
shelf_list.push({StorageZoneId:'Z1',shelfId:'A2',shelfName:'货架A2',x:0,y:27,z:0});
shelf_list.push({StorageZoneId:'Z1',shelfId:'A3',shelfName:'货架A3',x:100,y:27,z:0});
需要几个货架就在shelf_list
里添加几个。效果如下:
添加货物
在我的设计中,货物是跟货位绑定的,一个货位上可能会放很多箱的货物,为了显示简洁方便,我们统一成一个大箱子,然后双击这个大箱子弹出画面显示里面的详细信息(这章暂不讲解),Modules.js
代码如下:
//region 货物
/** 放置单个货物 */
function addCargo(x,y,z,box_x,box_y,box_z,scene,name) {
var geometry = new THREE.BoxGeometry( box_x, box_y, box_z );
var obj = new THREE.Mesh( geometry, CargoMat );
obj.position.set(x,y,z);
obj.name = name;
scene.add(obj);
}
/** 添加单个货位上的货物 */
function addOneUnitCargos(shelfId,inLayerNum,inColumnNum,scene) {
var storageUnit = getStorageUnitById(shelfId,inLayerNum,inColumnNum);
var shelf = getShelfById(storageUnit.shelfId);
var storageUnitid = storageUnit.storageUnitId;
var x = storageUnit.positionX;
var y = storageUnit.positionY + 8 + shelf.planeHeight/2;
var z = storageUnit.positionZ;
addCargo(x,y,z,16,16,16,scene,"货物"+"$"+storageUnitid)
}
//endregion
在HTML的init()
方法中添加:
//添加货物
for(var i = 1; i <= 3; i++){
for(var j = 1; j <= 2; j++){
for(var k = 1; k <= 3; k++) {
addOneUnitCargos("A" + k, i, j, scene);
}
}
}
效果如下,点击货物箱子也会显示货物的信息,跟货位类似也是:货物&货架编码&货物的所在层&所在列。
添加信息侧边栏
首先在HTML中引入这个JS
<script src="./ThreeJs/js/dat.gui.min.js"></script>
然后添加这个方法,大家可以自定义需要显示的内容。我这里添加了四个标签,每个都添加了.listen()
监听事件,这样就可以在点击到物体的时候改变侧边栏上的内容。
// 初始化GUI
function initGui() {
options = new function () {
this.batchNo ='';this.qty = 0;this.qtyUom ='';this.qty2 = 0;
};
var gui = new dat.GUI();
gui.domElement.style = 'position:absolute;top:50px;left:0px;height:600px';
gui.add(options, 'batchNo').name("物料批号:").listen();
gui.add(options, 'qty').name("数量:").listen();
gui.add(options, 'qtyUom').name("单位:").listen();
gui.add(options, 'qty2').name("件数:").listen();
}
在init()
中添加initGui()
就ok了
我们回到ThreeJs_Composer.js
这个自定义的JS,我们需要在鼠标点击事件里添加代码实现:
var Msg = intersects[0].object.name.split("$");
if(Msg[0] == "货物") {
_options.batchNo = "一个货物";
_options.qty = "100";
_options.qtyUom = "kg";
_options.qty2 = "10";
}
这里限定了只有点击到货物时,侧边栏_options
才会更新,一般数据也是从数据库取出来的,这里我仅仅为了演示写死了一些数据。完成后效果如下:
结束语
最近陆续有朋友来加我微信交流评论,说实话当初我写文章只是为了记录下自己的历程免得以后忘记,看到这么多朋友催更交流我感到很开心。就如大家所说,ThreeJs网上的资料实在太过杂乱,很难从啥都不会开始学习,我虽然只是个后台程序猿,但也愿意贡献自己的一份力量帮助大家共同学习。因为我是从事仓库系统的开发,3D仓库显示只是其中的一小部分功能,到这章为止已经是我现在能做到的全部内容了。未来可能会开发其他的内容,比如场景切换等等。最后还是要谢谢大家支持!
我跟广大学习ThreeJs的初学者一样,仍带着懵懂的心去探索这片新大陆,CSDN上的许多前辈都给了我很多关键的灵感和技术方法,如果大家有兴趣,也可以互相交流成长,欢迎大家指导咨询。PS:大家有兴趣可以点进去我的头像,陆陆续续也写了十来篇了。
链接:使用ThreeJs从零开始构建3D智能仓库——第一章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第二章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第三章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第四章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第五章: 点我跳转.