Example of a 3D data center based on HTML5 WebGL, html5webgl3d data center
Preface
The 3D data center rendered with WebGL is nothing new now. The main purpose of this article is to explain that the eye and center problems in the 3D data center are just used in the project, after thinking about it, I finally thought that this example is the most suitable for my requirements and will be used as a record.
The Demo of this 3D data center is not bad, it is more beautiful, and the basic interaction is also satisfied. Let's take a look at how to implement it.
Code Generation
Definition class
First, open the corresponding js, server, one by one from the js path called in index.html. js customizes an Editor. HT. The Server class is encapsulated by ht. default. def function (note that the created class name is Editor. the Editor before Server cannot be replaced by E ):
Ht. default. def ('editor. server ', Object, {// The first parameter is the class name. If it is a string, it is automatically registered to the classMap of HT. The second parameter is the parent class to inherit from this class; the third parameter is the method and variable declaration addToDataModel: function (dm) {// Add the node to the data container dm. add (this. _ node); // pre-defined function in ht, adding the node to the data container through the add method}, setHost: function () {// set the adsorption of this. _ node. setHost. apply (this. _ node, arguments) ;}, s3: function () {// set the node size this. _ node. s3.apply (this. _ node, arguments) ;}, setElevation: function () {// controls the Y axis position of the 3D coordinate system where the Node element is located. this. _ node. setElevation. apply (this. _ node, arguments );}});
Create Editor. Server class
This class can create an ht. Node and set the color of the Node and the front texture:
Var S = E. server = function (obj) {// Server component var color = obj. color, frontImg = obj. frontImg; var node = this. _ node = new ht. node (); // create a node. s ({// set the node Style s to 'all. color ': color, // set the color of the six sides of the node' front. imag': frontImg // set the front image of the node });};
In this way, I can create a new server component object directly at the location where the server component needs to be created, and can directly call the setHost and other functions declared above, which will soon be used.
Next, create the Editor. Cabinet class. The method is similar to the definition method of the Editor. Server class above:
Ht. default. def ('editor. cabinet ', Object, {addToDataModel: function (dm) {dm. add (this. _ door); dm. add (this. _ node); this. _ serverList. forEach (function (s) {s. addToDataModel (dm) ;}) ;}, p3: function () {this. _ node. p3.apply (this. _ node, arguments); // set the 3d coordinates of the node }});
Create Editor. Cabinet class
This class is a little more complex than the Editor. Server component class. This class creates a cabinet body, a cabinet door, and a Server component inside the cabinet:
Var C = E. cabinet = function (obj) {var color = obj. color, doorFrontImg = obj. doorFrontImg, doorBackImg = obj. doorBackImg, s3 = obj. s3; var node = this. _ node = new ht. node (); // cabinet node. s3 (s3); // set the node size to setSize3d node. a ('Cabinet ', this); // customize the cabinet attribute node. s ({// set the node style to setstyle' all. color ': color, // set the color of the six sides of the node' front. visible ': false // specifies whether the node is visible before the node}); if (Math. random () & gt; 0.5) {node. addStyleIcon (' Alarm ', {// Add the icon names: ['icon thermometer'] to the node, // an array containing multiple strings, each string corresponds to an image or vector (through ht. default. setImage Registration) face: 'top', // The default value is front, which is the orientation of the icon in 3D. The value can be left | right | top | bottom | front | back | center position: 17, // specify the position of icons autorotate: 'y', // The default value is false, and whether the icon is automatically oriented to the eye in 3D. t3: [0, 16, 0], // The default value is undefined. the offset of the icon in 3D is in the format of [x, y, z] width: 37. // specify the width of each icon, by default, the height of each icon is specified based on the width of 32 when the image is registered. The default value is 2 based on the height of the image when the image is registered. The value represents a multiple of the textures actually generated by the memory. It is not recommended to set too large. Otherwise, the performance will be affected by visible: {func: function () {return !! E. alarmVisible ;}}// indicates whether the group of images is displayed});} var door = this. _ door = new ht. doorWindow (); // cabinet door. setWidth (s3 [0]); // set the length of the X axis in the 3D topology to door. setHeight (1); // set the Z axis length of the elements in the 3D topology door. setTall (s3 [1]); // controls the door length of the Node element on the Y axis. setElevation (0); // set the y coordinate door of the element center in the 3D coordinate system. setY (s3 [2] * 0.5); // set the location of the node on the y axis door. setHost (node); // set the door to be adsorbed. s ({// set the node style setstyle' all. color ': color, // set the six-sided color of the node' front. image ': doorFrontImg, // set the front image of the node' front. transparent ': true, // sets whether the front of the node is transparent. image ': doorBackImg, // set the image on the back of the node to 'back. uv ': [,], // uv texture behind the custom node. If it is null, the default value is [,] 'DW. axis ': 'right' // sets the rotation axis of the expand or close operation of the DoorWindow element. Optional values: left | right | top | bottom | v | h}) and var serverList = this. _ serverList = []; var max = 6, list = E. randomList (max, Math. floor (Math. random () * (max-2) + 2); // global. var server, h = s3 [0]/4; list. forEach (function (r) {var server = new E. server ({// server component color: 'rgb (, 49) ', frontImg: 'server component details'}); Server. s3 (s3 [0]-2, h, s3 [2]-4); // set the node size server. setElevation (r-max * 0.5) * (h + 2); // sets the coordinates of the node center on the y axis server. setHost (node); // set the node's adsorption serverList. push (server); // Add server nodes to serverList });};
The only thing not mentioned in the code above is the Editor. randomList function, which is declared in the global. js file. The declaration is as follows:
var E = window.Editor = { leftWidth: 0, topHeight: 40, randomList: function(max, size) { var list = [], ran; while (list.length < size) { ran = Math.floor(Math.random() * max); if (list.indexOf(ran) >= 0) continue; list.push(ran); } return list; }};
All right, all the classes in the scenario are created, so we should create the scenario and pile up these elements!
Scenario Creation
If you are familiar with this, you should know that you only need to create a new 3D component for a 3D scenario using HT, and then add the scenario to the body using the addToDOM function:
Var g3d = E. main = new ht. graph3d. Graph3dView (); // 3d scenario
Main. the js file mainly involves some necessary elements in 3D scenarios, such as walls, floors, doors, air conditioners, and the generation and discharge locations of all cabinets, there is also a very important part of interaction.
I will not post code for creating walls, floors, doors, air conditioners and cabinets. If you are interested, please check the code on your own, double-click the Cabinet and any objects related to the Cabinet (cabinet door, server equipment). In 3D, the camera line of sight will be moved to a location in front of the double-click cabinet, in addition, this movement is very smooth, and the previous skill was not refined, resulting in this part for a long time, and finally referred to the implementation method of this Demo.
In order to be able to repeatedly set the eye and center, the content corresponding to these two parameters is encapsulated as the setEye and setCenter methods. The setCenter method is similar to the setEye method, which is not repeated here:
// Set the eye position var setEye = function (eye, finish) {if (! Eye) return; var e = g3d. getEye (). slice (0), // obtain the current eye value dx = eye [0]-e [0], dy = eye [1]-e [1], dz = eye [2]-e [2]; // enable over-ht for 500 milliseconds. default. startAnim ({duration: 500, easing: easing, // animation easing function finishFunc: finish | function () {}, // The function action called after the animation ends: function (v, t) {// sets the animation v to represent the value after the easing (t) function operation, and t to represent the progress of the current animation [0 ~ 1]. Generally, g3d is performed based on the v parameter. setEye ([// set the eye values in 3D scenarios to an array, which corresponds to the values of x, y, and z axes respectively. e [0] + dx * v, e [1] + dy * v, e [2] + dz * v]) ;}}) ;};
I did not repeatedly declare the setCenter function. It does not mean that this function is not important. On the contrary, this function plays a decisive role in the process of moving the line of sight, the above setEye function is equivalent to the purpose I want to go to the front of my target position (at least I defined it for this purpose ), the definition of sCenter is to move my line of sight to the target position (for example, I can stand in my current position to see the object on the right back, or I can go to the right back, standing in front of the object). This is very important. Please have a good taste.
Double-click events are simple. You only need to listen to events encapsulated by HT, judge the event type, and make corresponding actions:
G3d. mi (function (e) {// addInteractorListener event listener function if (e. kind! = 'Doubleclickdata') // double-click the node to return the event type. var data = e. data, p3; if (data. a ('Cabinet ') // body p3 = data. p3 (); else {host = data. getHost (); // obtain the adsorption object if (host & host. a ('Cabinet ') {// If the adsorption object is cabinet p3 = host. p3 () ;}} if (! P3) return; setCenter (p3); // setEye ([p3 [0], 211, p3 [2] + 247]); // set the position where the eye is to be moved });
Top navigation bar
When I first saw this example, I was thinking that this person was amazing. I used HT for so long and HT for so long. widget. the Toolbar has not been able to make such a beautiful effect. I can see that it was originally made using form forms, which is amazing. I am so dull.
Var form = E. top = new ht. widget. formPane (); // form component form. setRowHeight (E. topHeight); // set the line height to form. setVGap (-E. topHeight); // set the horizontal spacing of the form component to a negative value of the row height, so that multiple rows can be in the same form row. setVPadding (0); // set the spacing between the top and top of the form and the component content. form. addRow ([null, {// Add a line of components to the form. The first parameter is the element array, the element can be component parameter information described in string, json format, html element, or null image: {icon :'. /symbols/inputBG. json ', stretch: 'centeruniform'}], [40,260]); // The second parameter is an array of width information for each element. a value greater than 1 indicates a fixed absolute value, if the value is less than or equal to 1, it indicates the relative value. It can also be a combination of 80 + 0.3 form. addRow ([null, null, {id: 'searchinput', textField :{}}, {element: 'Data center visualization management system', color: 'white', font: '18px arial, sans-serif'}, null, {button: {// label: 'view failed', icon :'. /symbols/viewChange. json', background: null, selectBackground: 'rgb (128,128,128) ', borderColor: 'rgba (0, 0, 0, 0)', onClicked: function () {E. focusTo () ;}}, null, {button: {// label: 'alert ', icon :'. /symbols/alarm. json', togglable: true, selected: false, background: null, selectBackground: 'rgb (128,128,128) ', borderColor: 'rgba (0, 0, 0, 0 )', onClicked: function (e) {E. setAlarmVisible (this. isSelected () ;}}, null], [40, 42,218,300, 0.1, 50, 10, 50, 10]);
All of the above can be implemented, but it is not actually added to the html tag, which means that there is nothing on the interface now! Do not forget to add the 3D scene to the body when loading the page, or add the form to the body. When setting the window size change event, the form also needs to be updated in real time:
Window. addEventListener ('load', function () {g3d. addToDOM (); // Add a 3D scene to the body document. body. appendChild (E. top. getView (); // Add the bottom-layer div of the form component to the window in the body. addEventListener ('resize', function () {// listener for window size change events E. top. iv (); // update the bottom div of the form });});
The addToDOM function is very important for understanding the HT mechanism. HT components are generally embedded in containers such as BorderPane, SplitView, and TabView, while the HT component at the outermost layer requires you to manually set getView () the returned bottom-layer div element is added to the DOM element of the page. Note that when the parent container size changes, if the parent container is a pre-defined HT container component such as BorderPane and SplitView, the HT container automatically calls the child component invalidate function recursively to notify the update. However, if the parent container is a native html element, the HT component cannot be informed of the need to update. Therefore, the HT component at the outermost layer generally needs to listen to window size change events of the window, call the invalidate function of the outermost component for updates.
For the convenience of loading and filling the window with the outermost component, all components of HT have the addToDOM function. The implementation logic is as follows, where iv is short for invalidate:
AddToDOM = function () {var self = this, view = self. getView (), style = view. style; document. body. appendChild (view); // Add the background div of the scenario to the style in the body. left = '0'; // HT sets the position of the bottom div of all components to absolute style by default. right = '0'; style. top = '0'; style. bottom = '0'; window. addEventListener ('resize', function () {self. iv () ;}, false); // listen to the window size change event to notify the component to update the changes}
In this way, all the code is over. You can right-click "check" and get the corresponding json file in the network.
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.