Tuesday , December 10 2019
Home / Uncategorized / How to create an interactive Flex layout designer in JavaScript

How to create an interactive Flex layout designer in JavaScript



The best tutorials come from completing real projects. When the project is finished, I like to share what I have learned.

[[[[Check out Visual CSS Dictionary with all the CSS properties explained visually.]

This time, the project is mine CSS flex layout designer. I will explain how I created it from the beginning to the end and provide the example source code below.

Open Flex Layout Designer

This is the example of the flex class, we are going to codify in this tutorial.

This is what we are building in this tutorial. You can also generate multiple instances of it at will on the same page with different default parameters.

here is the Open Flex Layout Designer <click to try it live yet:

I think it's a fun project because the integrated CSS flex functionality eliminates most of the work for us. The hardest thing here is to write the "drag and drop" code, but anyway, it's not that difficult.

To create this flexible layout designer, we will briefly use jQuery for cross-browser click events and the jQuery.css method to dynamically set the CSS properties to our flex articles as they are resized.

STARTING FROM THE START. Why are you creating your application? Why is it necessary? (IS?)

It's okay if you get excited about ideas. But to write an effective software that people actually need, you never have to write a line of code without first asking if you need what you are going to create.

It always starts the same way.

Find an existing problem that needs to be solved. In my case, I was able to answer this question. This is because most of the flex tutorials I found online were too limited or contained explanations using static images.

I mean, let's go. This is flexible. The name itself alludes to the fact that things will flex. That is, resizing, resizing, changing. So why all the static image tutorials for flex?

This did not like me. I knew that to create a truly useful tool that had a high educational value, an interactive approach was delayed.

Your application idea should start with an abstract view. Usually, it's something you get excited about. But remember it your software must also solve an existing real problem.

For me, I wanted to create an instrument or a generator that does not exist yet. Something you wish existed for personal reasons.

In this case, the Examples of CSS flex layout I thought, I should have the ability to add and remove flexible items from the list dynamically.

First of all, I plan some global variables

Global variables are usually avoided by engineers and professional software developers. This is because the modular software design (you know, when you use someone else's code and define global variable names you've already used in your code) will fail if you use them in your library.

But for the purposes of this tutorial, we will use global variables to learn how to create something. We are not distributing this code as a library. We do not share it with other programmers. It will only be used on a site and will not be sent to any open source community.

Each variable is explained with a comment.

// Default width of a flexible object (when added or removed)
to allow DEFAULT_ITEM_WIDTH = & # 39; 30px & # 39 ;;
// Default height of a flexible object (when added or removed)   
to allow DEFAULT_ITEM_HEIGHT = & # 39; 30px & # 39 ;;

// Current position of the X and Y mouse.
window.mx = 0;
window.my = 0;
// When you click on the item,
// we monitor its vertical and horizontal width:

window.item_x = 0;
window.item_y = 0;
// A flag to indicate if an element is
// currently under resizing (or less)

window.dragging = false;
// Position of the XY mouse screen when you clicked on the last item
window.drag_x = 0;
window.drag_y = 0;
// The actual JavaScript element object of the dragged item
window.dragging_target = null;
// The ID of the item that was clicked and resized
window.dragging_id = -1;
// Width and height of the element, when clicking on
window.item_width = 0;
window.item_height = 0;
// The difference between the current position of the XY mouse
// and the position of the XY mouse while it is being dragged

window.drag_width = 0;
window.drag_height = 0;
// Attribute "data-id" of the selected item
window.item_id = 0;
// To make a copy of the instance of
// the flex object (to update its HTML when
// changing the number or size of the article)

window.flex = null;
// The 7 flexible objects that represent an instance of the flex class
window.flex1 = null;
window.flex2 = null;
window.flex3 = null;
window.flex4 = null;
window.flex5 = null;
window.flex6 = null;
window.flex7 = null;
// I do not know what it is 🙂 It's unused. But I hate to delete it.
window.funcs = [];

Talking about software architecture

Whenever you design an app, you will think about its structure. This is the architecture of the software. It is the structure of your application.

As a software engineer, your job is to create the most efficient structure for your code. Efficient in this case means that it is easy to change it in the long run without having to rewrite much code again.

This means that you have to resume code reuse as much as possible during the design of your application architecture.

Designing software means that you will work with component-based structures. One nested inside another. But this example of flexible application is so simple that we only have one class. The only class is our entire architecture.

The application of the real world, of course, will be much broader than this. Regarding this case, here it is not really much architecture.

The first thing you want to do is create a JavaScript class that represents the main purpose of your application. Call it elegantly. In this case, I simply chose the flex class.

Of course, it works. After all, objects from this class will be instantiated providing a flexible container.

class flex {
builder(_target,
id,
width,
height,
elements,
item_width,
item_height,
justify_content
) {
// I noticed that when justify-content is set to stretch,
// and the width of the object is less than 100% of the width of the container (o
// generally, something small, like 100 px) then the stroke
// the effect will not even take place. It will seem
// flex-start. So the purpose of this line of code is to set up
// all the widths of the object (by default) to 800px, (the full size of
// flex container.) This equals all the elements in the equation equally
// container.
Self    (justify_content == & # 39; stretch & # 39;)
item_width = & # 39; 800 & # 39 ;;
// Stores the name of the ID attribute of the HTML element
// (without initial "#" character)
This.tag = _target;
This.variable_name = _target;
// Get the real JavaScript object of the HTML container
This.target = document.getElementById (_target);
// Size of the flexible container
This.width = width;
This.height = height;
// Number of articles
This
.items = elements;
// Create arrays that maintain the width and height of objects e
// populate them with the default values

This.items_widths = new array (elements).to fill(item_width);
This.items_heights = new array (elements).to fill(item_height);
// Default width and height of the element
This.item_width = item_width;
This.item_height = item_height;
// Set the justify-content property
This.justify_content = justify_content;
// C & # 39; is an object that is dragged / resized at this time?
This.dragging = false;
Binding methods (if you do not, they will not work)
This.setjustifycontent = This.setjustifycontent;
This.source = This.source;
// Makes the whole HTML flex in the container (dynamically
// update it)

This.incolla ();
}
// ... the functions of the class method follow ...
// (We'll get there soon)

This is the constructor of your class. A constructor allows you to pass arguments to its parameter names. Constructors are usually used to initialize the object with a set of predefined values.

  • this.tag – When I create a new flexible object, I want to remember the id attribute of its HTML parent container element.
  • this.variable_name – The same thing above is probably redundant but I made a copy just in case (do not do it at home.)
  • this.target – The actual JavaScript object of the container's HTML element.
  • this.width – The width of the container element.
  • This.Height – The height of the container element.
  • this.items – The number of items in this flexible container.
  • this.items_widths – Series of indices 0 of width of each element in the list.
  • this.items_heights – Array of indices 0 of heights of each element in the list.
  • this.item_width – The default width of all items.
  • this.item_height – The height of the default article of all articles.
  • this.justify_content – value of justify-content for this flexible container.
  • this.dragging – An item is now dragged (false by default).
  • this.source – We need to associate the member function of the source class, otherwise our methods will not work outside the class.
  • This.setjustifycontent– We have to tie the setjustifycontent member function of the class, otherwise it will not work.

And finally we call the Paste member function.

  • this.paste ();– all default values ​​are set, make the Flex HTML container with the elements it contains for the first time when the object is instantiated.

The flex class builder allows us to do the following:

// Initialize the 7 flex objects and assign them to window.flex[n]
window.Flex1 = newflex ("flex1", "flex-a", 800, 40, 1, & # 39; 200px & # 39 ;, & # 39; 30px & # 39 ;, & # 39; flex-start & # 39;);
window.Flex2= new flex ("flex2", "flex-b", 800, 40, 2, & # 39; 200px & # 39 ;, & # 39; 30px & # 39 ;, & # 39; flex-end & # 39;);
window.FLEX3 = new flex ("flex3", "flex-c", 800, 40, 3, "50px", "30px", "center");
window.Flex4 = new flex ("flex4", "flex-d", 800, 40, 4, "50px", & # 39; 30px & # 39 ;, space-between & # 39;);
window.Flex5= newflex ("flex5", "flex-e", 800, 40, 5, & # 39; 50px & # 39 ;, & # 39; 30px & # 39 ;, space-around & # 39;);
window.flex6= new flex ("flex6", "flex-f", 800, 40, 6, & # 39; 50px & # 39 ;, & # 39; 30px & # 39 ;, & # 39; stretch & # 39;);
window.Flex7= new flex ("flex7", "flex-g", 800, 40, 7, & # 39; 50px, & # 39; 30px & # 39 ;, space-uniform & # 39;);

The constructor instantly creates each flexible object using the new JavaScript keyword. We provide default values, so our flexible object can crunch some data as a starting point during initialization.

During the life cycle of application, these values ​​may change. (For example, the ownership of the justification content will be changed via the onscreen shortcuts displayed just above the flexible container.)

Creation of class method functions

At this point we have provided an instantiation mechanism for our flex class. As our primary (and only) class, it will take many parameters to be able to set application defaults.

The application is still abstract right now. We have no function that gets something. And this is the key to designing your software using classes. They help to define the structure of your application before writing the code.

Once the constructor is complete, we are ready to start working on the member functions of our class (also known as methods, in some languages).

Method: flex.source () – creates the CSS and HTML source code from the class properties names

The source method is what builds the source code for the flexible layout associated with the flex object. It will take the article number, the width of each individual article and convert it into a text string that represents the HTML and CSS code to create the flex you designed using the flexible layout designer.

Oh, and of course it will also take into consideration the subject of flex justify-content property. This is what determines the unique default look of each flex block.

This method provides the code that is copied to the clipboard when the user clicks Copy to clipboard .

source () {
// Start with an empty string
to allow    I = ``;
// Go through each item in the list e
// create a container of contents for this:
for    (to allow    i = 0; I < This.elements; i ++)
I + = `<div style = & # 39; width:` +
This.items_widths[i] +
`; height: `+
This.items_heghts[i] + `& # 39;>` + (i + 1) +
`
R n & # 39 ;;
// Create the css style tag, put .flex class in it, create
// flex the container and insert the list (we just created above)
        to allow    src = `%MINIFYHTML97c331fc52ea354be4f08161d116882424%R  n
r n` + I + `
`;
// Return the string we have just constructed
return src;
}

We will simply call the source method whenever you need to copy the code to the integrated gluing clipboard of the browser (or operating system).

Method: flex.setjustifycontent () – sets the justification of the content value and dynamically updates the flexible HTML code

call setjustifycontent the member function will simply set a value in CSS justify-content properties on that flexible container. This is what happens when you click on one of these links above the flex container.

setjustifycontent(justify_content) {
// Set the property value
This.justify_content = justify_content;
// "Paste" HTML into the flex container (update flex)
This.incolla ();
}

This is a rather simple function, is not it? Next, let's write the add function to add items to the list. Note, all these functions go into the object in the same scope in which the constructor is located. But later in this tutorial we will write some global support functions.

insert() {
// Store widths and heights of all items
to allow copyW = This.items_widths.slice ();
to allow copyH = This.items_heights.slice ();
// Reset all the widths and heights of the objects
// to their default values

This.items_widths[this.items] =
This.justify_content == & # 39; stretch & # 39 ;?
& # 39; & # 39 ;: 800px
DEFAULT_ITEM_WIDTH;
This.items_heights[[[[This.elements]= DEFAULT_ITEM_HEIGHT;
This.items ++;
// Set all the dimensions of the elements to the values ​​we have previously stored
for    (to allow    i = 0; i <copyW.length; i ++)
This.items_widths[i] = copyW[i];
for    (to allow    i = 0; i <copyH.length; i ++)
this.items_heights[i] = copyH[i];
// Generate HTML and update view
This.incolla ();
}

The comments here are self-explanatory.

to remove() { This.elements--; This.incolla (); }

Removing an object is as simple as decreasing the object counter. We're not actually eliminating the object's width / height arrays. This is important. We do not want to delete the item's size when the item is deleted. Because if the user decides to re-add it again, we want to preserve the previous width and height of that element. Create only a better user experience.

Finally, the paste method (somewhat embarrassingly named) is responsible for "pasting" the actual HTML code for the flex container and its elements based on the current class parameters. This is the core behind the update of the application view.

paste() {
// Start with an empty string
to allow    list = ``;
// Explore all existing elements e
// create the container of the content of the element
for    (to allow    i = 0; I < This.elements; i ++)
list + = `<div class = "object"
id = "` + this.target + `_item_` + (i + 1) +
`" Data-id = "` + (i + 1) +
`" style = & # 39;height of the line : `+ this.items_heights[i] +
`; height : `+ this.items_heights[i] +
`; width: `+ this.items_widths[i] +
`& # 39;>` + (i + 1) +
`
`;
// Create the main container and enter the list
// we have created in the cycle described above
to allowsource = `
`+ create_justify_content_modifier(This.label, This.justify_content, This.variable_name) + `
<div class = & # 39; dynamic flex & # 39; style = & # 39; width: `+ This.width + `; height: `+ This.height + `; justify-content: `+ This.justify_content + `& # 39;>` + list + `
`;
// Finally, paste the code into the HTML content
The container element, effectively (and instantaneously) updated
// the browser view
This.target.innerHTML = source;
// The way events functions work does not allow us to
// refers to "this" object, in the following "mousedown"
// callback function () {... here ...}, so what we will do is
// store a copy in the "that" variable and use it instead (that
// works.)
to allowthat = This;
// You have clicked on an object inside this container flex.target!
$ (& # 39; Item & # 39 ;, This.target) .on ("MouseDown", function(is) {
// Assign the object "that" to the global variable
// window.flex The use of "this" here will not refer to flex
// class "this" but the event is "this", which in this
The case would refer to the object on which it was clicked,
// but that's not what we need. Instead, we keep it
// flex class globally for future operations and access
// from our other support functions

window.flex = that;
// The object was clicked, then apply the "edit" appearance to
// it (dotted border)

$ (This) .addClass ("change");
// Update the globals with useful information
window.dragging = true;
window.drag_x = e.pageX;
window.drag_y = e.pageY;
window.dragging_target = This;
window.item_id = parseInt ($ (This) .attr (& # 39; data-id & # 39;)) - 1;
// Simply get the initial width and height of an object
// just clicked we will use this to measure his new one
// size based on the distance traveled by the mouse
// its original position after the item has been clicked
// (window.drag_x and window.drag_y)

window.item_width = parseInt ($ (This) .css ("width"));
window.item_height = parseInt ($ (This) .css ("height"));
// Save the width of the clicked item
// width and height globally
Self (This.target) window.item_x
= parseInt ($ (This.target)[0].style.width);
Self (This.target) window.item_y
= parseInt($ (This.target)[0].style.height);
});
}
}

You see, every time we "paste" the new HTML based on the current values ​​of the class properties, we need to relink the onclick event to this newly inserted HTML. Otherwise the old event listener will be deleted and nothing will happen.

function copy to the clipboard(target) {
// Get the flexible JavaScript object (window.flex1, for example
// if target = "flex1") and call the source method of
The flexibility class we coded above

to allowsrc = window[target].source();
// Copy and paste can only be performed by a & quot; text area, grab
// its value and select it

document.getElementById (& # 39; src & # 39;). value = src;
. Document.getElementById (& # 39; src & # 39;) select ();
// Run the native "copy" command on the text currently
// selected in the off-screen textarea

to allowcopied = document.execCommand (& # 39; copy & # 39;);
// The edge of the Flash container is "Copy to clipboard"
// clicked ... to let the user know about the copy operation
// it was a success

$ ("#" + target).animate({borderColor: & # 39; # 00F & # 39;}, 200,
function() {
$ ("#" + target).animate({borderColor: & # 39; # EEE & # 39;}, 200);
});
}

Note that you need an off-screen text area to copy the code from. This is why it is first inserted into the textarea and selected. Only then the native OS command "copy to clipboard" is called to copy the selected text.

function setjustifycontent(object, target_id, command, value) {
$ (". link", $ (object) .parent ()). removeClass (& # 39; sel & # 39;);
$ (Subject) .addClass (& # 39; sel & # 39;);
window[target_id].setjustifycontent (value);
}

This is also self-explanatory. We simply call the setjustify content on the target flex object. But this is a global function to accomplish this (not the member function of the object).

function create_justify_content_modifier(target_id, justify_content, object_variable_name) {
return `

justify-content
<span class = "link` + (justify_content == & # 39; flex-start & # 39 ;? & # 39; sel & # 39 ;: & # 39; & # 39;) +` " at the click = "setjustifycontent (This, & # 39; + target_id + `& # 39 ;, & # 39;` + object_variable_name + `& # 39 ;, & # 39; flex-start & # 39;)"> flex-start
<span class = "link` + (justify_content == & # 39; flex-end & # 39 ;? & # 39; sel & # 39 ;: & # 39; & # 39;) +` " at the click = "setjustifycontent (This, & # 39; + target_id + `& # 39 ;, & # 39;` + object_variable_name + `& # 39 ;, & # 39; flex-end & # 39;)"> flex-end
<span class = "link` + (justify_content == & # 39; center & # 39;? & # 39; sel & # 39 ;: & # 39; & # 39;) +` " at the click = "setjustifycontent (This, & # 39; + target_id + `& # 39 ;, & # 39;` + object_variable_name + `& # 39 ;, & # 39; center & # 39;)"> center
<span class = "link` + (justify_content == & # 39; space-between & # 39;? & # 39; sel & # 39 ;: & # 39; & # 39;) +` " at the click = "This, setjustifycontent (This, & # 39; + target_id + `& # 39 ;, & # 39;` + object_variable_name + `& # 39 ;, & # 39; space-between & # 39;)"> space-tra
<span class = "link` + (justify_content == & # 39; space-around & # 39;? & # 39; sel & # 39 ;: & # 39; & # 39;) +` " at the click = "setjustifycontent (This, & # 39; + target_id + `& # 39 ;, & # 39;` + object_variable_name + `& # 39 ;, & # 39; space-around & # 39;)"> space-around
<span class = "link` + (justify_content == & # 39; stretch & # 39;? & # 39; sel & # 39 ;: & # 39; & # 39;) +` " at the click = "setjustifycontent (This, & # 39; + target_id + `& # 39 ;, & # 39;` + object_variable_name + `& # 39 ;, & # 39; stretch & # 39;)"> stretch
<span class = "link` + (justify_content == & # 39; space-evenly & # 39 ;? & # 39; sel & # 39 ;: & # 39; & # 39;) +` " at the click = "setjustifycontent (This, & # 39; + target_id + `& # 39 ;, & # 39;` + object_variable_name + `& # 39 ;, & # 39; space-evenly & # 39;)"> space-uniform

`;
}

We're almost done. But we have to add some other things.

The next 3 code blocks are connected from the same scope to the document ready function.

// All DOM objects have finished loading
$ (document) .ready (function () {
// Initialize the 7 flexors
window.flex1 = newflex ("flex1", "flex-a",
800, 40, 1, '200px & # 39; 30px & # 39 ;, & # 39; flex-start & # 39;);
window.flex2 = newflex ("flex2", "flex-b",
800, 40, 2, '200px', & # 39; 30px & # 39 ;, & # 39; flex-end & # 39;);
window.flex3 = newflex ("flex3", "flex-c",
800, 40, 3, '50px & # 39; 30px & # 39;, & # 39; center & # 39;);
window.flex4 = newflex ("flex4", "flex-d",
800, 40, 4, '50px', & # 39; 30px & # 39; space-between & # 39;);
window.flex5 = newflex ("flex5", "flex-e",
800, 40, 5, '50px & # 39; 30px & # 39 ;, space-around & # 39;);
window.flex6 = newflex ("flex6", "flex-f",
800, 40, 6, '50px & # 39; 30px & # 39 ;, & # 39; stretch & # 39;);
window.flex7 = newflex ("flex7", "flex-g",
800, 40, 7, '50px', '30px', & # 39; space-uniform & # 39;);

In our context of the JavaScript program, we first initialize all flexible objects, one after the other.

$ ("Body"). On ("mouseUp", function() {
// Finished element resizing, disable dragging
window.dragging = false;
// Remove the dotted border around the element
$ ("Item") removeClass ("modify") .;
// Update width / height matrix with new object dimensions
Self (window.flex) {
Self (window.flex.items_widths &&
window.flex.items_widths[window.item_id])
window.flex.items_widths[window.item_id]
= window.drag_width + & # 39; px & # 39 ;;
Self    (window.flex.items_heights &&
window.flex.items_heights[window.item_id])
window.flex.items_heights[window.item_id] =
window.drag_height + & # 39; px & # 39 ;;
}
});

This is the "up" event of the global mouse. Basically, regardless of what happens, every time the mouse button is pressed (someone has lithium?), We want to reset the drag state to false. So our other code (see below) does not go into action and keeps changing the size of the element.

// Process mouse movement events as you drag the item, update
// its dimensions and the height of the line

$ ("Body"). MouseMove (function(is) {
window.mx = e.pageX;
window.my = e.pageY;
Self    (window.dragging) {
// Calculate the difference in drag distance
to allow diffX =
window.drag_width =
parseInt ((window.item_width + (window.mx - window.drag_x) * 1.75));
to allow    diffY =
window.drag_height =
parseInt ((window.item_height + (window.my - window.drag_y) * 1.75));
// Absolute values ​​ensure the size of the object
// never become negative

$ (Window.dragging_target) css ("width",
Math.abs (diffX) + & # 39; px & # 39;);
$ (Window.dragging_target) css ("height",
Math.abs (diffY) + & # 39; px & # 39;);
$ (Window.dragging_target) css ("lineHeight",
Math.abs (diffY) + & # 39; px & # 39;);
// Change the cursor to the left-right or up-down arrow,
// based on the direction in which the resistance is the largest
if (diffX> diffY) {
/ * Optional, perhaps a future function ... * /
}
}
})
});

And finally … the event window.onload ensures that all the HTML elements and multimedia contents (images, etc.) have completed downloading from the server. Here, I used jQuery animation to animate the first flexible object in the first flexible container, 500 milliseconds after the DOM and the images were loaded into the browser, to let the visitor know that it is an interactive flex designer, and they should click on objects to resize them.

window.onload = () => {
setTimeout (function () {
$ ("# flex1 .item"). addClass ("change");
$ ("# flex1 .item"). animate ({width: "+ = 75px"}, 700);
$ ("# animcursor"). animate ({left: "+ = 75px"}, 700, function () {
$ ("# flex1 .item"). removeClass ("modify");
$ ("# animcursor"). animate ({opacity: 0}, 400, function () {
$ ("# Animcursor") remove () .;
both B = true;
both C = 0;
both T = setInterval (function () {
if (B) {$ (& # 39; # flex1 .item & # 39;). addClass ("change"); } else {$ (& # 39; # flex1 .item & # 39;). removeClass ("modify"); }
B =! B;
if (C ++> 6) {clearInterval (T); T = null; }
}, 60);
});
});
}, 500);
}

Congratulations!

You've just created a flexible layout designer in vanilla JavaScript.

Limited offer

The diagrams in this tutorial were directly influenced by the manuscript!

Visual CSS Dictionary 28% DISCOUNT for average readers.

28% DISCOUNT

Get your copy!

Visual CSS Dictionary

Contains all CSS properties


Source link

Leave a Reply

Your email address will not be published.