Saturday 12 May 2012

Area Selector and Chromosome Component

.position() .offset() .left . top .width() .height() .parent()

Those are the 'words' that I have written the most during the last few days. And the ones that I have spoken I had better avoid mentioning, in case it causes my blog to be censored.

Here is the story: after having finished the Chromosome component, I thought it would be cool to freely select and adjust areas in the chromosome, and not just select the bands as it was doing. I thought that a semi-transparent div at the top of the chromosome could do the trick, so it was just a matter of some styling and I have a div that shows what's selected in the chromosome. Then it's just javascript playing with the DOM to modify position, and size.

Simple! So let's start with a static HTML and its CSS. The Div is going to contain other divs that will work as the points to scale the div:

<div class='selector'>
  <div class='scaler top'></div>
  <div class='scaler bottom'></div>
  <div class='scaler left'></div>
  <div class='scaler right'></div>
</div>


So I defined a couple of styles in the CSS, defining the transparency, the position and size:

.selector {
  border: 1px solid #FF0000;
  position: absolute;
  margin-left: 0px;
  margin-top: -5px;
  height: 25px;
  width: 30px;
  z-index: 10;
  background-color: rgb(100%, 75%, 75%);
  opacity:0.4;
  filter:alpha(opacity=40); /* For IE8 and earlier */
}

.scaler {
  position: absolute;
  height: 5px;
  width: 5px;
  background-color: white;
  border: 1px solid red;
}



You can check the fully working code here. It looks like this:

12- Snapshot of the chromosome component with a overlapping div

So how hard could it be to make that div fully dynamic as an independent component? Easy peasy!
How wrong I was!! Well I'm not trying to say that it's incredibly complicated, but something that I estimated doing in one day took me the whole week. Hope that this is not going to be the rule in the rest of this project.

In reality the whole thing is not complicated, it's just a matter of transforming coordinates from the HTML document to the Chromosome space, and vice-versa. Plus having control of events to drag&drop the scalers to modify the size, location of the selector div. And saying it in that way makes me think again that it was something easy.

So, I tried to do the trick with mousedown() mouseup() events recalculating coordinates and redrawing things, but this didn't work because if you move your mouse too quickly you will move out of the scaler div, and it wont recognise the mouseup() event.

Then I realised that I was committing the classic mistake of reinventing the wheel. I mean that above is the description of a Draggable component in jquery UI, and for a moment I even thought that the whole thing could it be done by the Resizable component. But this one just modifies the size from the bottom-right corner, and what I want is something I can change from any side, conclusion back to my idea, but now using a jqueryui.

Here is the code for the draggable actions of the right scaler, the other scalers are pretty much the same, although the left and top required modifying the position of the div and not just increasing the width or height.


$("#"+self.opt.target+" .selector .right").draggable({
  axis: "x",
  start :function(event) {
    self.updateScalers();
    $(this).parent().css('border-right-width',"0px");
  },
  stop :function(event) {
    $("#"+self.opt.target+" .selector").width(self._removePx($(this).css('left'))+5);
    $(this).parent().css('border-right-width',"1px");
    self.updateScalers();
    self.raiseEvent('onRegionChanged', {
      region : self.getCoveredArea()
    });
  }
});


Obviously there is a lot more code than this, but this pretty much describes the task. The function updateScalers, repositions the divs that I used to create the effect. It also recalculates the limits of the enclosing div.

Another thing that I did for this component was to make sure that if the container changes its position, the constrains get automatically updated. In order to do that i found a plugin for jquery to watch a DOM element and react in that case. You can read some details here.

So this is how the Area Selector looks, I also created some methods and triggered some events in this component, so it can really interact with other elements.

13- Snapshot of the Area Selector component

You can play with it here, and soon it will be on the repository. I will update this post once it is online.
I just finished the interaction between this and the chromosome component and I will post about it at the beginning of the week.
For now, I'll be back to my super-lazy weekend, hope you guys are enjoying yours. :-)


EDIT: Here is the link of the AreaSelector in the registry (LINK)

No comments:

Post a Comment