📜 ⬆️ ⬇️

How I made a power control widget for my space flight browser simulator

Today I made a small snippet of the code for myself and decided to share its contents and the history of its creation with the community.

I’ve been surfing the Internet for quite a while in order to find at least something similar to what I need, but the Internet hasn’t yet apparently faced such a task or no one has decided to create such a widget. Probably no one had to.

To begin with, I voiced what I wanted so much:
I need a slider - an analogue of the volume control, combined with the progress bar. A kind of power management component of something, combined with the simultaneous indication of this power. Sometimes the power can exceed the set limit of 100% - it is necessary to display this level and correctly calculate the percentage. Sometimes the power can go below zero (I don’t know if it can, but I provided for this, just in case) and this level should also be displayed. Moreover, the device that we adjust can be inert and not accelerate at the speed with which we set the value. If you press the afterburner button on the plane, then the engines will enter the afterburner mode after a while. That is, you must separately set the value of the progress bar and also separately receive-set the current value of the slider slider.
')
Maybe I'm a fig finder, but in the end I got into a fuss - I decided to make my own:
Here is a link to the result, and under the cut description of the process

Getting started


First, create the widget frame:
var PowerControlWidget = function(settings){ this.container = settings.container || undefined ; this.canvas = document.createElement('CANVAS'); this.canvas.height = this.height; this.canvas.width = this.width; this.container.appendChild(this.canvas); this.ctx = this.canvas.getContext('2d'); // ---    this.set_value(0); this.redraw() } 


Immediately make a reservation - I would like to be as independent as possible from jquery.js and jqueryui.js - so I did not design this widget as a jQuery plugin.

Event handling



For drag-n-drops, everything is trite: on mousedown, save the state, on mouseup, reset it.

 var self = this; this.canvas.addEventListener("mousedown", function(event){ self.mouse_down = true; self.value = event.offsetX; if(event.offsetX < self.padding_left_right){ self.value = self.padding_left_right; } if(event.offsetX > self._line_width - self.padding_left_right){ self.value = self._line_width - self.padding_left_right; } self._percent_value = self._get_percent(self.value); self.redraw(); self.onchange(self._percent_value, self.progress_value); }) this.canvas.addEventListener("mouseup", function(event){ self.mouse_down = false; self._percent_value = self._get_percent(self.value); self.redraw(); self.onchange(self._percent_value, self.progress_value); }) this.canvas.addEventListener("mousemove", function(event){ if (self.mouse_down){ self.value = event.offsetX; if(event.offsetX < self.padding_left_right){ self.value = self.padding_left_right; } if(event.offsetX > self._line_width - self.padding_left_right){ self.value = self._line_width - self.padding_left_right; } self._percent_value = self._get_percent(self.value); self.redraw(); self.onslide(self._percent_value, self.progress_value); } }) 


There are quite a few auxiliary variables that set the position only in the right place of this canvas. In the widget, in addition to its main part, it is also necessary to add the output of accurate information - the current value of the progress bar and slider. I decided to do it inside the canvas, even though we stumble upon quite understandable problems with the positioning of the text. Text output - a little later.

Start drawing



For the slider control itself, we allocate some area from this canvas. We will have it limited:


like this:

 this._draw_border = function(){ var b = this.padding_top_bottom; //     . var a = this.padding_left_right; var w = this._line_width - ( 2 * a ); var h = this.height - (2*b); this.ctx.beginPath(); this.ctx.moveTo(a,b); this.ctx.bezierCurveTo(a+(w/2), b, w-(w/2)+a, b, a+w, b ); this.ctx.bezierCurveTo(a+w+a, b, a+a+w, b+h, a+w, b+h ); this.ctx.bezierCurveTo( w/2+a, b+h, w/2+a,b+h, a, b+h); this.ctx.bezierCurveTo( 0, b+h, 0,b, a,b); this.ctx.closePath(); this.ctx.strokeStyle = this.border_color; this.ctx.stroke(); //     }; 

Let me remind you that the bezier curve contains three points in the input parameters. The fourth point is the current one; we have to go into it with the help of moveTo.
The general meaning of drawing such a curve:

image

We get a beautiful frame with rounded ends.

Now we start the magic:
In order to draw a zone of negative percentage and afterburner zone, we will use clips. The clip is simple, first create a path within which drawing takes place, and then repeat the border drawing with only one difference - we will not circle this path, but fill it with the desired color from the inside. It sounds trite and simple, it looks also not difficult.

First, determine where to draw it, everything to zero - we draw in a separate color.

  var zero = this._get_x(0); //         

Create a drawing area

  this.ctx.beginPath(); this.ctx.rect(0,0, zero, this.height); this.ctx.clip(); 


And fill the same bezier desired color.

  this.ctx.beginPath(); this.ctx.bezierCurveTo(a+(w/2), b, w-(w/2)+a, b, a+w, b ); this.ctx.bezierCurveTo(a+w+a, b, a+a+w, b+h, a+w, b+h ); this.ctx.bezierCurveTo( w/2+a, b+h, w/2+a,b+h, a, b+h); this.ctx.bezierCurveTo( 0, b+h, 0,b, a,b); this.ctx.fillStyle = this.below_z_color; this.ctx.fill(); this.ctx.closePath(); this.ctx.restore(); 


By analogy, we act with the region above 100%.

A little bit about calculations



We need to set the value on the widget and take it, converting the coordinates of the mouse cursor into a percentage and vice versa.
I wrote two very simple functions for this:
  this._get_percent = function(x){ var a = this.padding_left_right; //     var w = this._line_width - (2*a); //   return ((x - a) * this._range/ w)+this.starting_percent ; //      ,        ,        . }; this._get_x = function(p){ var a = this.padding_left_right; var w = this._line_width - (2*a); return a+ (p - this.starting_percent) * w / this._range; //  }; 


Well, a little bit about drawing text



Want to make both visual and accurate? Please, but then do not do without text data.
We will draw the text to the right of the indicator bar. Separately, we will draw the state of the slider, separately the progress bar. I think you can experiment with the location, but for now let's do it.

To begin with, we will display the interest, and not how it is implemented inside - that is, multiply by one hundred.
  var val = this._percent_value * 100 var int = Math.floor(val); var frac = Math.floor((val - int)*100); 

That is, we got separately the whole, separately fractional parts. We will draw them in different sizes, but for a start we will generally calculate which one is better for us to use the font. I decided to make a simple dependency that does not work too well in some conditions:
  var base_font_size = this.height - (this.padding_top_bottom*2) ; //     var add_font_size = Math.floor(base_font_size / 2); //     var base_marg = base_font_size *2; //   
Well, in the end - the code for drawing text

  this.ctx.save() this.ctx.translate(this._line_width+ this.padding_top_bottom, this.height-this.padding_top_bottom); this.ctx.fillStyle = "#000"; this.ctx.font = base_font_size + "pt Arial"; this.ctx.textAlign = "end"; //     this.ctx.fillText("" + (int), base_marg, 0 ) this.ctx.textAlign = "center"; this.ctx.font = (base_font_size -2) + "pt Arial"; this.ctx.fillText(",", base_marg+1,0 ) this.ctx.font = add_font_size + "pt Arial"; this.ctx.textAlign = "start"; //     this.ctx.fillText("" + (frac), base_marg+3, 0 ) this.ctx.restore(); 

Conclusion



Ready indicator can be used. Color settings for it can be set directly. Unfortunately, the decision to use canvas did not leave us ample opportunities to paint it with css, but the canvas has other advantages - in particular, it can be used to attach additional strokes and rulers to this indicator. Fortunately, the canvas can very accurately draw geometric shapes.

For those who want to pick it up or use leave the repository address github.com/stavenko/power-control-widget . Today this widget worked with only one browser - Google Chrome, and I’m honestly not sure if the events will be properly processed in other browsers. In particular, the events may not contain mouse coordinates in the offsetX variables. And it was very convenient - it is not necessary to calculate the coordinates - they are immediately given relative to the top-left of the container.

That's all for today.

Source: https://habr.com/ru/post/224173/


All Articles