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!

   

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();
toolbar.addItem(createItem('edit'.'edit'.'edit', [ new ht.graph.EditInteractor(graphView)])); // The last parameter array can hold multiple interactors, as defined in HTforWeb Manual //addItem(item, index) Inserts a new element at the specified index position. If index is empty, the element is inserted to the end. toolbar.addItem(createItem('shape'.'shape'.'Irregular pattern',  [ new CreateShapeInteractor(graphView)]));
toolbar.addItem(createItem('circle'.'circle'.'circle', [ new CreateNodeInteractor(graphView)]));
toolbar.addItem(createItem('roundRect'.'roundRect'.'Rounded rectangle', [ new CreateNodeInteractor(graphView)]));
toolbar.addItem(createItem('rect'.'rect'.'the rectangle', [ new CreateNodeInteractor(graphView)]));
toolbar.addItem(createItem('edge'.'edge'.'wired', [ new CreateEdgeInteractor(graphView)]));
toolbar.getItemById('edit').selected = true; / / selected by default toolbar. The "edit" getSelectBackground =function(){// Reload the custom selected background colorreturn '#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:

functionCreateItem (id, iconName, toolTip, interactorsArr){var item = {id: id,// Unfocusable:true,// Whether the toolbar element can not get focus. By default, a rectangle border will be displayed when the mouse moves over ittrueTurn off this effect icon: iconName,// The toolbar element's icon toolTip: toolTip,// The toolbar element's text prompt groupId:'bar'// Group the toolbar elements, select the elements in the same group will automatically appear mutually exclusive effect}; 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 ? json.fitSize :false,
        comps: [
            {
                type: 'rect'Rect: [0, 0, width + 8, height + 8], {type: 'image'Func: (data, view) => {func: (data, view) => {func: (data, view) => {return '# 000'}}}]}; item.action =function(){// Function type, called when the toolbar element is clickedfor(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(); // Clear all selections every time the toolbar is clicked}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); // Block all default interaction eventsif(ht.default.isleftButton (e)) {// Left click 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 scaling behavior on touch screensif(! this.p1) {return; } this.p2 = this._graphView.lp(e); const rect = ht.Default.unionPoint(this.p1, this.p2); // Combine points P1 and p2 into a rectangleif (this.node) {
        this.node.setRect(rect);
    }
    else {
        if(! rect.width || ! rect.height) {return; } this._graphView.dm().beginTransaction(); This.createnode (rect, rect, rect, rect, rect, recT, recT);false); // 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 onceif(! 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: http://www.cnblogs.com/xhload3d/p/7887229.html

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 of the graphics to have a three-dimensional feelif(toolbar.getItemById('circle').selected){// If the bar's 'circle' is selected this.node.s({// Set the style"shape": "oval",// Ellipse is displayed as a picture when empty. You can set the polygon type refer to the getting Started manual"shape.background": "#D8D8D8"// Polygon type pixel background"shape.border.width": 1,// The width of the polygon type pixel border"shape.border.color": "# 979797"// Color of polygon type pixel border"shape3d": "sphere"// Display 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(toolbar.getItemById('roundRect').selected){
        this.node.s({
            "shape": "roundRect"// Rectangle with rounded corners"shape.background": "#D8D8D8"."shape.border.width": 1,
            "shape.border.color": "# 979797"."shape3d": "roundRect"
        });
    }else if(toolbar.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); // Set node coordinates}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.