preface
This example was originally intended to simulate server-client communication, and I simplified the entire requirement into this example. 3D simulation generally needs the assistance of hawk-eye, so it will be more clear to find products and summarize the whole space. In this example, I also added it. This article can be regarded as a summary of this project.
Example link: www.hightopo.com/demo/3DEdge…
GIF of this article:
Code implementation
Getting to the point, the whole example was implemented in just over 200 lines of code, which is what I like about using HT, being able to develop quickly. The rise of Web3D technology is roughly divided into two factions: plug-in faction and HTML5 faction. HT is HTML5 based, there is no need to install any plugins.
Scenario building
First of all, the scene is built again. The interface adds three parts on the body: 3D component, form component and topology component (2D component). For ease of loading and filling Windows for the outermost components, all components of HT have addToDOM functions that implement the following logic, where iv is short for invalidate:
addToDOM = function(){
var self = this,
view = self.getView(), // Get the underlying div of the component
style = view.style;
document.body.appendChild(view); // Add the underlying div to the body
style.left = '0'; // The default position of the HT component is absolute, so the position must be set
style.right = '0';
style.top = '0';
style.bottom = '0';
window.addEventListener('resize'.function () { self.iv(); }, false); // Trigger the event when the window size changes, call the iv function, refresh the page
}
Copy the code
While HT components are typically embedded in containers such as BorderPane, SplitView, and TabView, the outermost HT components require the user to manually add the underlying div element returned by getView() to the DOM element of the page. When the parent container size changes, if the parent container is a predefined container component such as BorderPane and SplitView, the HT container automatically recursively calls the child component invalidate function to notify the update. However, if the parent container is a native HTML element, the HT component cannot know that it needs to be updated. Therefore, the outermost HT component usually needs to listen for the window size change event and call the invalidate function of the outermost component to update.
Since this function fixes the position in style, we cannot use this function for all components. We add topology components and property components to the interface in the same way as this function. 3D components can directly use addToDOM function:
dm = new ht.DataModel();
g3d = new ht.graph3d.Graph3dView(dm); / / 3 d component
g3d.addToDOM(); // Add the component to the body
g3d.setDashDisabled(false); // Start the dashed line flow
g3d.setMovableFunc(function(){ // Override the move function
return false; // Return false, all primitives are immovable
});
g3d.setEye([- 813..718.1530]); / / set the eye
g3d.setCenter([140.- 25.217]); // Set center (target)
gv = new ht.graph.GraphView(dm); / / 2 d component
gv.getView().className = 'graphview'; // The HT component root layer is a div, obtained by the getView() function
document.body.appendChild(gv.getView()); // Add the topology component to the body
gv.fitContent(true); // Zoom and pan the entire topology to show all the primiples
var form = new ht.widget.FormPane(); // Form panel components
var view = form.getView(); // Get the form's underlying div
document.body.appendChild(view); // Add the form component's underlying div to the body
form.setWidth(200); // Set the width of the form panel
form.setHeight(140);
view.style.top = '5px'; // Sets the position of the bottom div of the form panel
view.style.right = '5px';
view.style.background = 'rgba (255, 255, 255, 0.2)';
Copy the code
I won’t go into detail about the styles of the topology and properties components, just setting a background color and the left right top bottom position. HT components are usually positioned in absolute mode with position set to Absolute.
Add eagle eye
You might be wondering, how did this hawk-eye come about? In HT, as long as 2D and 3D share the same dataModel, they can have all the elements in the dataModel in the same location, just do something like this:
dm = new ht.DataModel();
g3d = new ht.graph3d.Graph3dView(dm);
gv = new ht.graph.GraphView(dm);
Copy the code
Isn’t it very simple… Can save a lot of time development…
In this example, all elements except wires are hT. Node types, so we encapsulate the creation method of this Node for reuse:
function createNode(p3, s3, name, shape){ // Create a node
var node = new ht.Node(); // Create a Node of type HT. Node
dm.add(node); // Add the node to the data container dataModel
node.s({ // Set the style of the node. S is short for setStyle
'shape3d': shape, // Specify the shape of the node. Here is the JSON file passed in to the 3D model
'label.position': 23.// Text display position
'label.transparent': true.// Whether the text is transparent in 3D can eliminate the jagged edges around the font
'label.color': '#eee'.// Text color
'label.t3': [0.0.- 151.].// Text offset in 3D
'label.r3': [0.Math.PI, 0].// Rotate text in 3D
'label.scale': 2 // Text zoom
});
node.r3(0.Math.PI, 0); // Node rotation
node.p3(p3); // Set the node position in 3D
node.s3(s3); // Set the size of the node in 3D
node.setName(name); // Set the display name of the node
return node; // returns the node
}
Copy the code
And the creation of wires:
function createEdge(exchange, service){ // Create a connection
var edge = new ht.Edge(exchange, service);
dm.add(edge);
edge.s({
'edge.width': 4.// Line width
'edge.color': 'red'.// Line colors
'edge.dash': true.// Whether to display dashed lines
'edge.dash.color': 'yellow'.// Dashed line color
'edge.dash.pattern': [32.32].// Line dashed style defaults to [16, 16]
});
edge.a({ // User-defined attributes are short for setAttr
'flow.enable': true.// Whether to enable flow
'flow.direction': 1./ / direction
'flow.step': 4 / / step by step
});
return edge;
}
Copy the code
All the lines shown in our interface are dotted line flow. HT turns off the dotted line flow function by default. Turn on the dotted line flow function through the following sentence:
g3d.setDashDisabled(false); // Start the dashed line flow
Copy the code
At the same time, we also need to set the animation to control the time interval and make the dotted line offset to form a “flow” state. Please refer to the schedule scheduling manual for the animation:
flowTask = {
interval: 40.action: function(data){
if(data.a('flow.enable')){
data.s('edge.dash.offset', data.s('edge.dash.offset')+(data.a('flow.step')*data.a('flow.direction'))); }}}; dm.addScheduleTask(flowTask);// Add the flowTask animation
Copy the code
The following are all the server and client node declarations that appear on the screen, which are created based on the createNode and createEdge functions:
function initModel(){
floor = createNode([0.5.0], [1000.10.500]); // Floor graphics
floor.s({
'all.color': 'rgb(47, 79, 79)'.'3d.selectable': false}); exchange = createNode([0.300.- 400.], [200.20.150].'H3C Core Switch '.'Models/machine room/Cabinet/cabinet 6. Json'.'Symbols/Machine room/Cabinet Component 1. Json').s('label.t3'[0.0.- 151.]); / / switches
// Five servers with different functions
service1 = createNode([- 400..140.0], [100.260.100].'standby'.'Models/machine Room/Cabinet 2. Json'.'Symbols/computer/resistance cabinet. Json');
service2 = createNode([- 200..140.0], [100.260.100].'the website'.'Models/machine Room/Cabinet 2. Json'.'Symbols/computer/resistance cabinet. Json');
service3 = createNode([0.140.0], [100.260.100].'OA'.'Models/machine Room/Cabinet 2. Json'.'Symbols/computer/resistance cabinet. Json');
service4 = createNode([200.140.0], [100.260.100].'advertising'.'Models/machine Room/Cabinet 2. Json'.'Symbols/computer/resistance cabinet. Json');
service5 = createNode([400.140.0], [100.260.100].'accept'.'Models/machine Room/Cabinet 2. Json'.'Symbols/computer/resistance cabinet. Json');
var arr = [service1, service2, service3, service4, service5];
for(var i = 0; i < arr.length; i++){ // The node is created with a rotation and font rotation and position, which are specific to the device
var service = arr[i];
service.r3(0.0.0);
service.s({
'label.r3': [0.0.0].'label.t3': [0.30.50]}); }// Create a connection between the switch and the server
createEdge(exchange, service1);
createEdge(exchange, service2);
createEdge(exchange, service3);
createEdge(exchange, service4);
createEdge(exchange, service5);
// Switch number 2
exchange2 = createNode([- 100..60.400], [200.20.150].'Procurve Switch 2010-23 '.'Models/machine room/Cabinet/cabinet 6. Json'.'Symbols/Machine room/Cabinet Component 1. Json').s('label.t3'[0.0.- 151.]);
// The rest of the node creation part is too repetitive, I will not post code
}
Copy the code
Finally, in the form, manipulate the flow, flow direction, flow step, line color, and dotted color of the edge. We’re going to add these properties to the form that we need to manipulate, five properties in total, This includes the properties flow.direction, flow.step and the style properties edge.color and edge.dash Property to control the flow of wires. We realize access to Data node through name attribute and accessType attribute:
function createForm(){
var form = new ht.widget.FormPane(); // Form panel components
var view = form.getView(); // Get the form's underlying div
document.body.appendChild(view); // Add the form component's underlying div to the body
form.setWidth(200); // Set the width of the form panel
form.setHeight(140);
view.style.top = '5px'; // Sets the position of the bottom div of the form panel
view.style.right = '5px';
view.style.background = 'rgba (255, 255, 255, 0.2)';
form.setLabelColor('#fff'); // Set the text color in the form panel
form.addRow([ // Use this function to add a row of components to the form panel
'Enable Flow'.// Display text value used to access the property name, if empty display name property value
{
checkBox: { // CheckBox, HT automatically builds an ht.widget.CheckBox object based on the property value and saves it on the element property
value: true.onValueChanged: function(){ // Call the function after the value changes
flowTask.enabled = this.getValue(); }}}, [0.1.0.1]);
form.addRow([
{
name: 'flow.direction'.accessType: 'attr'.element: 'Flow Direction'}, {comboBox: {
value: 1.labels: ['Forward flow'.'Reverse flow'].values: [- 1.1].onValueChanged: function(){
for(var i = 0; i < dm.size(); i++){
var data = dm.getDatas().get(i);
data.a('flow.direction'.this.getValue()); }}}}], [0.1.0.1]);
form.addRow([
{
name: 'flow.step'.element: 'Flow Step'.accessType: 'attr'}, {slider: { // Slider, HT will automatically build an HT.Widget. Slider based on the property value and save it on the Element property
min: 0./ / the minimum
max: 10./ / Max
step: 0.1./ / step by step
value: 1.onValueChanged: function(){
for(var i = 0; i < dm.size(); i++){
var data = dm.getDatas().get(i);
data.a('flow.step'.this.getValue()); }}}}], [0.1.0.1]);
form.addRow([
{
name: 'edge.color'.accessType: 'style'.element: 'Edge Color'}, {colorPicker: { // Color selection box
instant: true.// Gets and sets whether the model values will change in real time as the editor of the table and property pages
value: 'rgb(255, 0, 0)'.onValueChanged: function(){
for(var i = 0; i < dm.size(); i++){
var data = dm.getDatas().get(i);
data.s('edge.color'.this.getValue()); }}}}], [0.1.0.1]);
form.addRow([
{
name: 'edge.dash.color'.// Dashed style properties
element: 'Dash Color'.accessType: 'style'
},
{
colorPicker: {
instant: true.value: 'rgb(255, 255, 0)'.onValueChanged: function(){
for(var i = 0; i < dm.size(); i++){
var data = dm.getDatas().get(i);
data.s('edge.dash.color'.this.getValue()); }}}}], [0.1.0.1]);
}
Copy the code
Is not very simple ~ quick hands-on practice it!