PDA

View Full Version : Vertical & Area Slider



TranceGeniK
23 Feb 2012, 6:45 AM
Hi,

I have to make a 2d area slider, which controls 2 classic sliders; 1 horizontal for the x position in the area, and 1 vertical for the y.
I'm new to Sencha and Ext. I'v realized almost all what I wanted to do, but this is much more complicated and any help would be appreciated!
If it can help, someone as already made it in Ext, but i don't really understand all this code and the internal structure of this framework : http://www.sencha.com/learn/legacy/Extension:Slider or code below



/**
* Class Ext.ux.SlideZone
* @author Jared Gisin
* @version $Id: Ext.ux.SlideZone.js 93 2007-12-12 04:41:40Z jaredgisin $
* @license http://www.opensource.org/licenses/mit-license.php
*
* TODO: make sure slider limits work when snap = 1 or snap < slider width/height
* TODO: API to dynamically modify the snap
* TODO: zone with slider zone responds to click and keypress
* TODO: API to programatically set slider value with animation
*
*/


Ext.ux.SlideZone = function(id, config) {
if(id) {
Ext.apply(this, config);
this.init(id, config || {});
} else {
return false;
}
}


Ext.extend(Ext.ux.SlideZone, Ext.BoxComponent, {
size: 100,
sliderSnap: [0],
type: 'horizontal',
sliderWidth: 20,
sliderHeight: 20,
cls: null,
minValue: 0,
maxValue: 100,
allowSliderCrossing: true,
value: null,


init: function(id, config) {
this.size = this.size instanceof Array ? this.size : [this.size];
this.minValue = this.minValue instanceof Array ? this.minValue : [this.minValue];
this.maxValue = this.maxValue instanceof Array ? this.maxValue : [this.maxValue];
this.sliderSnap = this.sliderSnap instanceof Array ? this.sliderSnap : [this.sliderSnap];


this.el = Ext.DomHelper.append(Ext.get(id), { tag: 'div',
id: Ext.id(),
style: 'position: relative; '
}, true);
this.el.addClass('x-slide-zone-' + this.type);
this.el.addClass(this.cls);


switch(this.type) {
case 'horizontal':
this.sliderSize = [this.sliderWidth];
this.el.applyStyles({width:this.size[0] + 'px'});
this.el.lowLimit = [this.el.getX()];
this.el.highLimit = [this.el.getRight()];
break;


case 'vertical':
this.sliderSize = [this.sliderHeight]
this.el.applyStyles({height:this.size[0] + 'px'});
this.el.lowLimit = [this.el.getY()];
this.el.highLimit = [this.el.getBottom()];
break;


case 'area':
this.sliderSize = [this.sliderWidth, this.sliderHeight];
this.el.applyStyles({width:this.size[0] + 'px'});
this.el.applyStyles({height:this.size[1] + 'px'});
this.el.lowLimit = this.el.getXY();
this.el.highLimit = [this.el.getRight(), this.el.getBottom()];
break
}


//normalize width/height for slider centering calculations
var l = this.sliderSize.length;
for(var i = 0; i < l; i++) {
this.sliderSize[i] = this.sliderSize[i] % 2 ? this.sliderSize[i] + 1: this.sliderSize[i]
}




var s = this.sliders;
this.sliders = new Ext.util.MixedCollection();
if(s){
this.add.apply(this, s);
}


},


add: function(){
var a = arguments, l = a.length
for(var i = 0; i < l; i++){
var el = a[i];
if (el instanceof Ext.ux.Slider) {
this.initSlider(el);
this.sliders.add(el)
} else if (typeof el == 'object') {
//initalize new slider and add to the items list
var s = new Ext.ux.ThumbSlider(el);
this.initSlider(s);
this.sliders.add(s)
}
}
this.updateValues();
this.updateConstraints();
},


enableCrossing: function() {
this.allowSliderCrossing = true;
this.updateConstraints();
},


disableCrossing: function() {
this.allowSliderCrossing = false;
this.updateConstraints();
},


getSlider: function(selector) {
/* Given a slider index or name, returns a slider object
* @selector slider index|name
*/
switch (typeof selector) {
case 'number':
return this.sliders.items[selector];
break;


case 'string':
var l = this.sliders.length;
for(var i = 0; i < l; i++){
if(this.sliders.items[i].name == selector) return this.sliders.items[i];
}
break;
}
},




updateConstraints: function() {
if(!this.allowSliderCrossing && this.sliders.length > 1 && this.type != 'area') {
// multiple sliders exists and sliders can't cross,
// so we have to take into account the
// positions of all sliders
sortFN = function(a,b){
var v1 = a.value instanceof Array ? a.value[0] : a.value;
var v2 = b.value instanceof Array ? b.value[0] : b.value;
return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
}
this.sliders.sort('asc',sortFN);//put in order by their value


var l = this.sliders.length;
for (var i=0; i< l; i++) { //forloop required, as we have to get -1 and +1 slider positions
var leftTravel = 0, rightTravel = 0;
var sliderL = this.sliders.get(i-1);
var slider = this.sliders.get(i);
var sliderR = this.sliders.get(i+1);




if (sliderL && sliderR) {
if (sliderL instanceof Ext.ux.ThumbSlider) {
var leftTravel = slider.getTL()[0] - sliderL.getTL()[0] - this.sliderSnap[0];
}
if (sliderL instanceof Ext.ux.RangeSlider) {
var leftTravel = slider.getTL()[0] - sliderL.getBR()[0] - this.sliderSnap[0];
}
if (sliderR instanceof Ext.ux.ThumbSlider) {
var rightTravel = sliderR.getTL()[0] - slider.getTL()[0] - this.sliderSnap[0];
}
if (sliderR instanceof Ext.ux.RangeSlider) {
var rightTravel = sliderR.getTL()[0] - slider.getBR()[0] - this.sliderSnap[0] ;
}
} else if(sliderL) {
if (sliderL instanceof Ext.ux.ThumbSlider) {
var rightTravel = this.el.highLimit[0] - slider.getTL()[0] - ( 0.5 * this.sliderSize[0]) ;
var leftTravel = slider.getTL()[0] - sliderL.getTL()[0] - this.sliderSnap[0];
}
if (sliderL instanceof Ext.ux.RangeSlider) {
var rightTravel = this.el.highLimit[0] - slider.getBR()[0];
var leftTravel = slider.getTL()[0] - sliderL.getBR()[0] - this.sliderSnap[0];
}
} else if(sliderR) {
if (sliderR instanceof Ext.ux.ThumbSlider) {
var leftTravel = slider.getTL()[0] - this.el.lowLimit[0] + ( 0.5 * this.sliderSize[0]);
var rightTravel = sliderR.getTL()[0] - slider.getTL()[0] - this.sliderSnap[0] ;
}
if (sliderR instanceof Ext.ux.RangeSlider) {
var leftTravel = slider.getTL()[0] - this.el.lowLimit[0];
var rightTravel = sliderR.getTL()[0] - slider.getBR()[0] - this.sliderSnap[0] ;
}
}
if(slider instanceof Ext.ux.RangeSlider) {
slider.resizable.leftTravel = [leftTravel];
slider.resizable.rightTravel = [rightTravel];
}
slider.setConstraint([leftTravel], [rightTravel], this.sliderSnap);
}
} else {
var l = this.sliders.length;
for (var i=0; i< l; i++) {
var slider = this.sliders.get(i);
if (slider instanceof Ext.ux.ThumbSlider ) {
slider.setConstraint([slider.getTL()[0]-this.el.lowLimit[0] + ( 1/2 * this.sliderSize[0]),
slider.getTL()[1]-this.el.lowLimit[1] + ( 1/2 * this.sliderSize[1])],
[this.el.highLimit[0]- (slider.getTL()[0] + this.sliderSize[0]) + ( 1/2 * this.sliderSize[0]),
this.el.highLimit[1]- (slider.getTL()[1] + this.sliderSize[1]) + ( 1/2 * this.sliderSize[1]) ],
this.sliderSnap)
}
if (slider instanceof Ext.ux.RangeSlider) {
slider.setConstraint([slider.getTL()[0] - this.el.lowLimit[0],
slider.getTL()[1] - this.el.lowLimit[1]],
[this.el.highLimit[0] - slider.getBR()[0],
this.el.highLimit[1] - slider.getBR()[1] ],
this.sliderSnap)
}
}
}
},


updateValues: function() {
var that = this;
var vals = {};
this.sliders.each(function(i) {


if(this.type == 'area') {
if (i instanceof Ext.ux.ThumbSlider) {
i.value = [(i.getTL()[0] + 1/2 * that.sliderSize[0] - that.el.lowLimit[0]) / (that.size[0]) * (that.maxValue[0] - that.minValue[0]) + that.minValue[0],
(i.getTL()[1] + 1/2 * that.sliderSize[1] - that.el.lowLimit[1]) / (that.size[1]) * (that.maxValue[1] - that.minValue[1]) + that.minValue[1]];
i.percent = [parseFloat((i.getTL()[0] + 1/2 * that.sliderSize[0] - that.el.lowLimit[0]) / (that.size[0]) * 100),
parseFloat((i.getTL()[1] + 1/2 * that.sliderSize[1] - that.el.lowLimit[1]) / (that.size[1]) * 100)];
}
if (i instanceof Ext.ux.RangeSlider) {
i.value = [[
(i.getTL()[0] - that.el.lowLimit[0]) / (that.size[0]) * (that.maxValue[0] - that.minValue[0]) + that.minValue[0],
(i.getBR()[0] - that.el.lowLimit[0]) / (that.size[0]) * (that.maxValue[0] - that.minValue[0]) + that.minValue[0]




],[
(i.getTL()[1] - that.el.lowLimit[1]) / (that.size[1]) * (that.maxValue[1] - that.minValue[1]) + that.minValue[1],
(i.getBR()[1] - that.el.lowLimit[1]) / (that.size[1]) * (that.maxValue[1] - that.minValue[1]) + that.minValue[1]
]];
i.percent = [[
parseFloat((i.getTL()[0] - that.el.lowLimit[0]) / (that.size[0]) * 100).toFixed(2),
parseFloat((i.getBR()[0] - that.el.lowLimit[0]) / (that.size[0]) * 100).toFixed(2)
],[
parseFloat((i.getTL()[1] - that.el.lowLimit[1]) / (that.size[1]) * 100).toFixed(2),
parseFloat((i.getBR()[1] - that.el.lowLimit[1]) / (that.size[1]) * 100).toFixed(2)
]];
}
} else {
if (i instanceof Ext.ux.ThumbSlider) {
i.value = (i.getTL()[0] + 1/2 * that.sliderSize[0] - that.el.lowLimit[0]) / (that.size[0]) * (that.maxValue[0] - that.minValue[0]) + that.minValue[0];
i.percent = parseFloat((i.getTL()[0] + 1/2 * that.sliderSize[0] - that.el.lowLimit[0]) / (that.size[0]) * 100);
}
if (i instanceof Ext.ux.RangeSlider) {
i.value = [(i.getTL()[0] - that.el.lowLimit[0]) / (that.size[0]) * (that.maxValue[0] - that.minValue[0]) + that.minValue[0],
(i.getBR()[0] - that.el.lowLimit[0]) / (that.size[0]) * (that.maxValue[0] - that.minValue[0]) + that.minValue[0]];
i.percent = [ parseFloat((i.getTL()[0] - that.el.lowLimit[0]) / (that.size[0]) * 100),
parseFloat((i.getBR()[0] - that.el.lowLimit[0]) / (that.size[0]) * 100)];
}
}
vals[this.name] = this.value;
});


this.value = vals;
},


initSliderPosition: function(slider) {
/* Sets a sliders's initial position within the slide zone.
* If slideZone has a sliderSnap set, the slider
* is positioned at a multiple of the sliderSnap value that is
* closest where the slider's value would otherwise position it.
* After positioning, the slider's actual value will be modified
* in Ext.ux.SlideZone.updateValues()
* to jibe with the actual position in the slide zone given the
* sliderSnap constraint.
*/
var initialPosition = [0,0];
slider.repositionDelta = [0,0];
var l = this.type == 'area' ? 2 : 1;
for(var i=0; i < l; i++ ) {
/* Make sure initial value is in slider bar range,
if it's outside, set to closest limit.
The slider value is modified if it's out of range. */
if(slider.value instanceof Array) {
if (slider instanceof Ext.ux.ThumbSlider) {


slider.value[i] = Number(slider.value[i]).constrain(this.minValue[i], this.maxValue[i]);
initialPosition[i] = parseInt((slider.value[i] - this.minValue[i]) / (this.maxValue[i] - this.minValue[i]) * this.size[i] + this.el.lowLimit[i]);
}
if (slider instanceof Ext.ux.RangeSlider) {
switch (this.type) {
case 'horizontal':
case 'vertical':
slider.value[i] = Number(slider.value[i]).constrain(this.minValue[i], this.maxValue[i]);
initialPosition[i] = parseInt((slider.value[i] - this.minValue[i]) / (this.maxValue[i] - this.minValue[i]) * this.size[i] + this.el.lowLimit[i]);
break;


case 'area':
slider.value[i][0] = Number(slider.value[i][0]).constrain(this.minValue[i], this.maxValue[i]);
slider.value[i][1] = Number(slider.value[i][1]).constrain(this.minValue[i], this.maxValue[i]);


initialPosition[i] = parseInt((slider.value[i][0] - this.minValue[i]) / (this.maxValue[i] - this.minValue[i]) * this.size[i] + this.el.lowLimit[i]);


break;
}
}
} else {
slider.value = Number(slider.value).constrain(this.minValue[i], this.maxValue[i]);
initialPosition[i] = parseInt((slider.value - this.minValue[i]) / (this.maxValue[i] - this.minValue[i]) * this.size[i] + this.el.lowLimit[i]);
}


/* Calculate slider value versus closest snap-to location,
and calculate an offset value to apply when positioning it. */
if (this.sliderSnap[i] > 1) {
var positionToSnapDiff = [0,0];
positionToSnapDiff[i] = (initialPosition[i] - this.el.lowLimit[i]) % this.sliderSnap[i];
if (positionToSnapDiff[i]) {
if(positionToSnapDiff[i] < 1/2 * this.sliderSnap[i]) {
slider.repositionDelta[i] = -positionToSnapDiff[i];
} else {
slider.repositionDelta[i] = this.sliderSnap[i] - positionToSnapDiff[i];
}
}
}
}


if (slider instanceof Ext.ux.ThumbSlider) {
slider.setPosition([initialPosition[0] + slider.repositionDelta[0] - (0.5 * this.sliderSize[0]),
initialPosition[1] + slider.repositionDelta[1] - (0.5 * this.sliderSize[1])]);
}
if (slider instanceof Ext.ux.RangeSlider) {


slider.setPosition([initialPosition[0] + slider.repositionDelta[0],
initialPosition[1] + slider.repositionDelta[1]]);
}
},


initSlider: function(slider) {
slider.init(this);
this.initSliderPosition(slider);
}


});




Ext.ux.Slider = function(config) {


Ext.apply(this, config);


this.addEvents( {
"dragstart" : true,
"dragend" : true,
"drag" : true,
"mouseover":true,
"mouseout":true
});
}


Ext.extend(Ext.ux.Slider, Ext.BoxComponent, {
value: 0,
ddEl: null,
name: null,
cls: null,


init: function() {
//Do nothing
},


getTL: function() {
switch(this.type) {
case 'horizontal':
return [this.el.getX()];
break;


case 'vertical':
return [this.el.getY()];
break;


case 'area':
return this.el.getXY();
break;
}
},


getBR: function() {
switch(this.type) {
case 'horizontal':
return [this.el.getRight()];
break;


case 'vertical':
return [this.el.getBottom()];
break;


case 'area':
return [this.el.getRight(), this.el.getBottom()];
break;
}


},


setPosition: function(position) {
var l = position.length;
for (i=0; i<l; i++) {
position[i] = parseInt(position[i]);
}


switch(this.type) {
case 'horizontal':
this.el.setX(position[0]);
break;


case 'vertical':
this.el.setY(position[0]);
break;


case 'area':
this.el.setX(position[0]);
this.el.setY(position[1]);
break;
}
},


setConstraint: function(low, high, snap) {
var l = low.length;
for (i=0; i<l; i++) {
low[i] = parseInt(low[i] = low[i] < 0 ? 0 : low[i]);
high[i] = parseInt(high[i] = high[i] < 0 ? 0 : high[i]);
}




switch(this.type) {
case 'horizontal':
this.ddEl.setXConstraint(low[0], high[0], snap[0]);
this.ddEl.setYConstraint(0, 0);
break;


case 'vertical':
this.ddEl.clearConstraints();
this.ddEl.setXConstraint(0, 0);
this.ddEl.setYConstraint(low[0], high[0], snap[0]);
break;


case 'area':
this.ddEl.setXConstraint(low[0], high[0], snap[0]);
this.ddEl.setYConstraint(low[1], high[1], snap[1]);
break;
}
this.ddEl.resetConstraints(true);
}




});


Ext.ux.ThumbSlider = function(config) {
Ext.ux.ThumbSlider.superclass.constructor.call(this, config);
};




Ext.ux.RangeSlider = function(config) {
Ext.ux.RangeSlider.superclass.constructor.call(this, config);
};


Ext.extend(Ext.ux.ThumbSlider, Ext.ux.Slider, {
//init must be passed an instance of a SliderZon e
init: function(parentSlideZone) {
this.type = parentSlideZone.type;


this.pointer = this.type == 'horizontal' ? 'w-resize' : this.type == 'vertical' ? 'n-resize' : 'move';


this.el = Ext.DomHelper.append(parentSlideZone.el, {
tag: 'div',
id: Ext.id(),
style: 'position:absolute;'+
'overflow: hidden;'
}, true);
this.el.addClass('x-thumb-slider-' + this.type);
this.el.addClass(parentSlideZone.cls);


this.ddEl = new Ext.dd.DD(this.el);


var sliderInstance = this;
var slideZoneInstance = parentSlideZone;


this.el.on('mouseover', function() {
sliderInstance.fireEvent('mouseover', sliderInstance);
sliderInstance.el.setStyle('cursor', sliderInstance.pointer);
});
this.el.on('mouseout', function() {
sliderInstance.fireEvent('mouseout', sliderInstance);
sliderInstance.el.setStyle('cursor', 'default');
});
this.ddEl.onMouseDown = function(x, y) {
sliderInstance.fireEvent('dragstart', sliderInstance);
};
this.ddEl.onMouseUp = function(x, y) {
slideZoneInstance.updateConstraints();
slideZoneInstance.updateValues();
sliderInstance.fireEvent('dragend', sliderInstance)
};
this.ddEl.onDrag = function(e) {
slideZoneInstance.updateValues();
sliderInstance.fireEvent('drag', sliderInstance);
};


Ext.ux.ThumbSlider.superclass.init.call(parentSlideZone);
}
});


Ext.extend(Ext.ux.RangeSlider, Ext.ux.Slider, {
init: function(parentSlideZone) {
this.type = parentSlideZone.type;


var el = Ext.DomHelper.append(parentSlideZone.el, {
tag: 'div',
id: Ext.id(),
style: 'position:absolute;'+
'overflow: hidden;'
}, true);


//this.el.addClass(parentSlideZone.cls);
switch(this.type) {
case 'horizontal':
var w = Math.abs(this.value[0] - this.value[1]) / (parentSlideZone.maxValue - parentSlideZone.minValue) * parentSlideZone.size;
var h = parentSlideZone.sliderHeight;
var handles = 'e,w';
this.pointer = 'w-resize';
break;


case 'vertical':
var w = parentSlideZone.sliderWidth;
var h = Math.abs(this.value[0] - this.value[1]) / (parentSlideZone.maxValue - parentSlideZone.minValue) * parentSlideZone.size;
var handles = 'n,s';
this.pointer = 'n-resize';
break;


case 'area':
var w = Math.abs(this.value[0][0] - this.value[0][1]) / (parentSlideZone.maxValue[0] - parentSlideZone.minValue[0]) * parentSlideZone.size[0];
var h = Math.abs(this.value[1][0] - this.value[1][1]) / (parentSlideZone.maxValue[1] - parentSlideZone.minValue[1]) * parentSlideZone.size[1];
var handles = 'all';
this.pointer = 'move';
break;
}


this.resizable = new Ext.Resizable(el, {
wrap:false,
pinned:true,
width: w,
height: h,
minWidth: 20,
widthIncrement: parentSlideZone.sliderSnap[0],
heightIncrement: parentSlideZone.sliderSnap[1],
minHeight: 20,
dynamic: true,
handles: handles,
draggable: true,
transparent: false,
constrainTo: parentSlideZone.el,
leftTravel: null,
rightTravel: null
});
var sliderInstance = this;
var slideZoneInstance = parentSlideZone;


this.el = this.resizable.el;
this.el.addClass('x-range-slider-' + this.type);
this.el.addClass(this.cls);
this.ddEl = this.resizable.dd;




this.resizable.on('beforeresize', function() {
sliderInstance.fireEvent('dragstart', sliderInstance);
})


/* Parts of this.resizable.onMouseMove handler taken from:
* http://extjs.com/forum/showthread.php?p=23122&highlight=resizable+constrainto#post23122
*/
this.resizable.onMouseMove = function( e ) {
var box = this.constrainTo.getRegion(), tgt = e.getXY();
//redefine the constraining box if slider crossing resrictions
if(!parentSlideZone.allowSliderCrossing) {
if( parentSlideZone.type == 'vertical') {
box = {left: box.left, right: box.right,
top: this.startBox.y - this.leftTravel[0],
bottom: this.startBox.y + this.startBox.height + this.rightTravel[0] }
}
if( parentSlideZone.type == 'horizontal') {
box = {left: this.startBox.x - this.leftTravel[0],
right: this.startBox.x + this.startBox.width + this.rightTravel[0],
top: box.top, bottom: box.bottom }
}
}


e.xy = [
tgt[0] - box.left < 0 ? box.left - this.startBox.x + this.startPoint[0] : tgt[0] - box.right > 0 ? box.right - this.startBox.right + this.startPoint[0] : tgt[0],
tgt[1] - box.top < 0 ? box.top - this.startBox.y + this.startPoint[1] : tgt[1] - box.bottom > 0 ? box.bottom - this.startBox.bottom + this.startPoint[1] : tgt[1]
];


Ext.Resizable.prototype.onMouseMove.call(this, e);
slideZoneInstance.updateValues();
sliderInstance.fireEvent('drag', sliderInstance);
};


this.resizable.on('resize', function(width, height) {
slideZoneInstance.updateConstraints();
slideZoneInstance.updateValues();
sliderInstance.fireEvent('dragend', sliderInstance);
})


this.el.on('mouseover', function() {
sliderInstance.fireEvent('mouseover', sliderInstance);
sliderInstance.el.setStyle('cursor', sliderInstance.pointer);
});
this.el.on('mouseout', function() {
sliderInstance.fireEvent('mouseout', sliderInstance);
sliderInstance.el.setStyle('cursor', 'default');
});
this.ddEl.onMouseDown = function(x, y) {
sliderInstance.fireEvent('dragstart', sliderInstance);
};
this.ddEl.onMouseUp = function(x, y) {
slideZoneInstance.updateConstraints();
slideZoneInstance.updateValues();
sliderInstance.fireEvent('dragend', sliderInstance)
};
this.ddEl.onDrag = function(e) {
slideZoneInstance.updateValues();
sliderInstance.fireEvent('drag', sliderInstance);
};


Ext.ux.ThumbSlider.superclass.init.call(parentSlideZone);


}
});

mitchellsimoens
23 Feb 2012, 9:45 AM
I would forget about that. I would look at the current slider and see what you can do to make it vertical.

TranceGeniK
23 Feb 2012, 11:30 PM
I would forget about that. I would look at the current slider and see what you can do to make it vertical.
I can't forget, I have to do the area slider AND the vertical slider for my job... I have done it with jQuery, but it was really much more simple!

mitchellsimoens
24 Feb 2012, 5:09 AM
Ok, then you will probably have a pretty hard time converting something made for Ext JS to Sencha Touch since the code will be very different. As I suggested, look at the slider code for ST1 and see what you can do to make it vertical.

TranceGeniK
27 Feb 2012, 12:25 AM
Okay, thanks for your answers. I have forgotten that as you suggested, it's really much more complicated than I thought. So I came back to jQuery, but the performances are bad on iPad :(