No matter in any field, it would be great if non-programmers could drag and drop 2D and 3D design drawings. Today, we can write a 2D 3D editor by ourselves using HT without using 3dMaxs and other design software. I feel a great sense of achievement to realize this function. As long as you can think, can do, according to this editor can be extended into big Thing!
In this case the address: www.hightopo.com/demo/drag-c…
Here is the implementation:
We first output all the JSON files we need as vector images. The advantage of vector images is that the zoom of the pixels on the components will not be distorted, and there is no need to provide pictures of different sizes for Retina display. In the mobile era with diversified devicePixelRatio, perfect cross-platform can be achieved. Vector is probably the least cost solution.
HT uses the hT.default. setImage function to register images, which can be base64, JPG, PNG or JSON:
ht.Default.setImage('edit', 'images/default.json');
ht.Default.setImage('shape', 'images/edit.json');
ht.Default.setImage('edge', 'images/edge.json');
ht.Default.setImage('circle', 'images/circle.json');
ht.Default.setImage('roundRect', 'images/roundRect.json');
ht.Default.setImage('rect', 'images/rect.json');Copy the code
Here, I registered the top bar with 6 images, which are “Edit”, “Irregular shape”, “circle”, “Rounded Rectangle”, “Rectangle” and “Wire”, which function as the name suggests. Main operations: Click any icon of the toolbar and drag the mouse in the blank area under the toolbar to draw.
The next step is to create a “Toolbar”. HT. Widget. Toolbar fills the parameters of this function with the elements of the Toolbar. See the HT for Web Toolbar manual for details. GroupId is a group of elements of a type. If you select any of the elements in the group, all the other elements are not selected.
toolbar = new ht.widget.Toolbar(); AddItem (createItem('edit', 'edit', 'edit', [new ht.graph.editinteractor (graphView)])); // The last parameter array can hold more than one interactor, as defined belowHT for Web IntroductionCopy the code
//addItem(item, index) inserts a new element at the specified index position. AddItem (createItem(' Shape ', 'Shape ',' irregular shape', [New CreateShapeInteractor(graphView)])); AddItem (createItem('circle', 'circle', 'circle', [New CreateNodeInteractor(graphView)])); AddItem (createItem('roundRect', 'roundRect', 'rounded rectangle ', [New CreateNodeInteractor(graphView)])); AddItem (createItem('rect', 'rect', 'rectangle ', [New CreateNodeInteractor(graphView)])); AddItem (createItem('edge', 'edge', 'wire ', [new CreateEdgeInteractor(graphView)])); toolbar.getItemById('edit').selected = true; / / by default, select the "edit" toolbar. GetSelectBackground = function () {/ / overloaded custom select the background color return '# eee'; }Copy the code
The addItem function used above adds elements to the HT.Widget. Toolbar Toolbar that are returned from the createItem function. In this function we use a vector to create a combination of a rectangle and an image. We pass the previously registered vector image to the “picture” in this combination, and then filter the color of the selected and unselected state of the toolbar by controlling the “Render color” of the image:
function createItem(id, iconName, toolTip, interactorsArr){ var item = { id: Unfocusable: true; // Unfocusable: true; // Unfocusable: true: getItemById GroupId: 'bar' groupId: 'bar'// Group the toolbar elements in the same group and the elements in the same group will automatically appear mutually exclusive}; var json = ht.Default.getImage(iconName); var width = json ? json.width : 16; var height = json ? json.height : 16; item.icon = { width: width + 8, height: height + 8, fitSize: json ? FitSize: false, comps: [{type: 'rect',// Component type rect: [0, 0, width + 8, height + 8], {type: Func: (data, view) => {return '#000'}}}]}; func: (data, view) => {return '#000'}}}; Item.action = function(){// Call for(var I = 0; i < toolbar.getItems().length; i++){ toolbar.getItems()[i].icon.comps[1].color = '#000'; } item.icon.comps[1].color = '#1E90FF'; graphView.setInteractors(interactorsArr); // Combine multiple interactions graphView.sm().clearSelection(); } return item;} return item; }Copy the code
The entire interface is then divided into two parts, the top and bottom, using the HT.Widget.BorderPane, an HT-wrapped panel component. We use the ht.widget.SplitView component packaged by HT to split the bottom into upper and lower parts, and finally add the outer borderPane to the body:
SplitView = new hT.widget. splitView (graphView, g3d, 'v', 0.5); borderPane = new ht.widget.BorderPane(); borderPane.setTopView(toolbar); borderPane.setCenterView(splitView); borderPane.addToDOM();Copy the code
The whole scene was done, and then the functional part. We put making “irregular shapes” as a separate section in createshapeinteractor.js, and make “circles”, “rounded Rectangles” and “rectangles” as a separate section in createnodeInteractor.js. Divide the “wire” into a section and put it into createedgeInteractor.js. Next we will parse the three JS files one by one.
What these three js files have in common is that they inherit and create new classes from the hT.default.def (hT.default.def), the hT-wrapped inheritance function, which we mentioned in the previous code: CreateShapeInteractor, CreateNodeInteractor, and CreateEdgeInteractor classes are all similar. We’ll focus on the createnodeInteractor.js file.
The first thing to do is declare a CreateNodeInteractor class, which consists of these three parts:
var CreateNodeInteractor = function (graphView) { CreateNodeInteractor.superClass.constructor.call(this, graphView); }; Ht.default. def(CreateNodeInteractor, ht.graph.Interactor, {// Create a custom class, the first parameter is the class name, the second parameter is the inherited class, the third parameter is the method of this class.Copy the code
Then we add the functions we need to this class, the main functions are “mouse click event trigger” and “touch screen event trigger”, we draw the graph by listening to the event, the first is to determine whether the left mouse button or touch screen click:
Handle_touchstart: function (e) {/ / touch screen to start click the ht. The Default. The preventDefault (e); If (ht.default.isleftButton (e)) {if (ht.default.isleftButton (e)) {this._graphView.setfocus (e); // Set focus this.p1 = this._graphView.lp(e); // Get the current logical coordinates point this.startdragging (e); // Call startDragging function}}Copy the code
Then determine whether the mouse is up or the touch screen is over, and directly generate an HT. Node. HT names pure click events as handle_* and drag events as handleWindow*. The above code starts by clicking on the element that triggers the CreateNodeInteractor class in the toolbar and ends by putting it into the interface to generate the primitor. There is no drag process, there is a default size:
HT default call HT. Graph. DefaultInteractor events, there are a series of operations, we have to do now drag and drop with the conflict, so we will be the default event first stop in front, for the first point under the mouse point logical coordinates and logical coordinates the second point, Generate a rectangle based on the points of these two coordinates and start drawing nodes:
handleWindowTouchMove: function(e) { ht.Default.preventDefault(e); // Block the default behavior of events, often used to mask the default DoubleTap zooming behavior on touch screens. this.p1) { return; } this.p2 = this._graphView.lp(e); const rect = ht.Default.unionPoint(this.p1, this.p2); If (this.node) {this.node.setrect (rect); } else { if (! rect.width || ! rect.height) { return; } this._graphView.dm().beginTransaction(); This.createnode (rect, false); // Start a transaction from beginTransaction() to endTransaction(); // Call createNode}}Copy the code
Then at the end of the drag and drop operation to finish drawing, here I am directly set to draw the toolbar after the end of the “edit” element:
handleWindowTouchEnd: function(e) { ht.Default.preventDefault(e); this._graphView.dm().endTransaction(); // endTransaction from beginTransaction() to endTransaction(). All changes can be undone or redone at once. this.node && this.p1) { this.createNode({ x: this.p1.x - 25, y: this.p1.y - 25, width: 50, height: 50 }, true); } var continuousCreating = false; if (! continuousCreating) { for(var i = 0; i < toolbar.getItems().length; i++){ toolbar.getItems()[i].selected = false; toolbar.getItems()[i].icon.comps[1].color = '#000'; } toolbar.getItemById('edit').selected = true; toolbar.getItemById('edit').icon.comps[1].color = '#1E90FF'; borderPane.iv(); this._graphView.setEditable(true); this._graphView.sm().ss(this.node); } else { this.node = this.p1 = this.p2 = null; }}Copy the code
In HT, you can use shape or Shape3D to generate different primiples for basic primiples. This is how we do it. If you want to create complex graphics in the interface, for example, a cabinet model, you can refer to this article: www.cnblogs.com/xhload3d/p/…
createNode: function(rect, click) { // create instance if (ht.Default.isFunction(this.type)) { this.node = this.type(rect, click); } else { this.node = new ht.Node(); } this.node.setTall(50); // To prepare the 3D graphics, set the thickness, If (tool.getitembyId ('circle').selected){this.node.s({// Set the style "shape": "Oval ",// oval, will be displayed as an image when empty, polygon type can be set refer to "shape.background": "#D8D8D8",// Polygon type pixel background" shape.border. Width ": Color: "#979797",// color: "shape3d": "Sphere "// displays as a six-sided cube when empty, Other optional value of box | sphere | cylinder | cone | torus star | | the rect | roundRect | triangle | rightTriangle | parallelogram | trapezoid}); }else if(tool.getitembyId ('roundRect').selected){this.node.s({"shape": "roundRect",// "shape. Background ": "#D8D8D8", "shape.border.width": 1, "shape.border.color": "#979797", "shape3d": "roundRect" }); }else if(tool.getitembyId ('rect').selected){this.node.s({"shape": "rect",// rectangle "shape.background": "#D8D8D8", "shape.border.width": 1, "shape.border.color": "#979797", "shape3d": "rect" }); } // set bounds if (click) { this.node.setPosition(rect.x + rect.width / 2, rect.y + rect.height / 2); } else {this.node.setrect (rect); } // add to data model this._graphView.dm().add(this.node); // Add this node to the DataModel container}Copy the code
At this point, the declaration of creating hT. Node is complete and you can create any editor you want.