x = r⋅cos(φ)
y = r⋅sin(φ)
tg(φ) = y / x
φ = arctg(tg(φ)) = arctg(y / x)
<div id="Container">
<div id="Indicator"></div>
</div>
#Container
{
position: relative;
background-image: url('./images/rheostat.png');
width: 64px;
height: 64px;
}
#Indicator
{
position: absolute;
background-image: url('./images/indicator.png');
width: 4px;
height: 4px;
visibility: hidden;
}
var Rheostat = new Class({
Implements: [Events, Options],
// .
options: {
radius: 27,
minValue: 0,
maxValue: 100
},
// .
deg2rad: Math.PI / 180,
rad2deg: 180 / Math.PI,
// , , .
captured: false ,
//
initialize: function (container, indicator, options){
this .setOptions(options);
this .indicator = $(indicator);
this .container = $(container);
// .
this .indicator.fade( 'show' );
// .
this .container.addEvent( 'mousedown' , this .captureMouse.bind( this ));
document .addEvents({
'mousemove' : this .updateAngle.bind( this ),
'mouseup' : this .releaseMouse.bind( this )
});
// ,
// .
var containerSize = this .container.getSize();
var indicatorSize = this .indicator.getSize();
this .offset = {
x: Math.floor(containerSize.x / 2) - Math.floor(indicatorSize.x / 2),
y: Math.floor(containerSize.y / 2) - Math.floor(indicatorSize.y / 2)
};
this .angle = 0;
this .updateIndicatorPosition();
},
// , .
captureMouse: function (){
this .captured = true ;
},
// .
releaseMouse: function (){
this .captured = false ;
},
// .
updateAngle: function (e){
if ( this .captured)
{
var containerPosition = this .container.getPosition();
// .
// mouseLeft 0.1 , .
var mouseLeft = e.client.x - this .offset.x - containerPosition.x + 0.1;
var mouseTop = this .offset.y - e.client.y + containerPosition.y;
// (.. Math.atan() ,
// ).
this .angle = Math.atan(mouseTop / mouseLeft) * this .rad2deg;
// .. -90 +90,
// 180.
// .
if (mouseLeft < 0)
this .angle += 180;
// , 0 360 .
if ( this .angle < 0)
this .angle += 360;
// .
var value = Math.floor(( this .options.maxValue - this .options.minValue) * this .angle / 360 + this .options.minValue);
this .fireEvent( 'valueChanged' , value)
this .updateIndicatorPosition();
}
},
updateIndicatorPosition: function (){
// Math.cos() Math.sin().
var radAngle = this .angle * this .deg2rad
var left = this .options.radius * Math.cos(radAngle) + this .offset.x;
// "-". y .
var top = - this .options.radius * Math.sin(radAngle) + this .offset.y;
// .
this .indicator.setStyle( 'left' , left);
this .indicator.setStyle( 'top' , top);
}
});
var rheostat = new Rheostat( 'Container' , 'Indicator' );
rheostat.addEvent( 'valueChanged' , function( value ){
// value .
});
var Rheostat = new Class({
Implements: [Events, Options],
// .
options: {
radius: 27,
// .
minValue: 0,
maxValue: 100,
// .
minAngle: 50,
maxAngle: 310,
// .
angleOffset: -90,
// ?
reversed: true
},
// .
deg2rad: Math.PI / 180,
rad2deg: 180 / Math.PI,
// , , .
captured: false ,
angle: 0,
mouseAngle: 0,
oldMouseAngle: 0,
// .
initialize: function (container, indicator, options){
this .setOptions(options);
this .indicator = $(indicator);
this .container = $(container);
// .
this .container.addEvents({
'mousedown' : this .captureMouse.bind( this ),
'mousewheel' : this .handleWheel.bind( this ),
});
document .addEvents({
'mousemove' : this .updateAngle.bind( this ),
'mouseup' : this .releaseMouse.bind( this )
});
// ,
// .
var containerSize = this .container.getSize();
var indicatorSize = this .indicator.getSize();
this .offset = {
x: Math.floor(containerSize.x / 2) - Math.floor(indicatorSize.x / 2),
y: Math.floor(containerSize.y / 2) - Math.floor(indicatorSize.y / 2)
};
// .
this .angle = this .options.minAngle + this .options.angleOffset;
this .updateIndicatorPosition();
// .
this .indicator.fade( 'hide' ).fade( 'in' );
},
// .
handleWheel: function (e){
// .
var wheelAngle = this .angle + e.wheel;
if ((wheelAngle >= this .options.minAngle) && (wheelAngle <= this .options.maxAngle)){
this .oldMouseAngle = this .mouseAngle = this .angle = wheelAngle;
this .updateIndicatorPosition();
}
},
// , .
captureMouse: function (e){
this .captured = true ;
// .
var mouseAngle = this .getMouseAngle(e);
if ((mouseAngle >= this .options.minAngle) && (mouseAngle <= this .options.maxAngle)){
this .oldMouseAngle = this .mouseAngle = this .angle = mouseAngle;
this .updateIndicatorPosition();
}
},
// .
releaseMouse: function (){
this .captured = false ;
},
// .
getMouseAngle: function (e){
var containerPosition = this .container.getPosition();
// .
// mouseLeft 0.1 , .
var mouseLeft = e.client.x - this .offset.x - containerPosition.x + 0.1;
var mouseTop = this .offset.y - e.client.y + containerPosition.y;
// (.. Math.atan() ,
// ).
var angle = Math.atan(mouseTop / mouseLeft) * this .rad2deg;
// .. -90 +90,
// 180.
// .
if (mouseLeft < 0)
angle += 180;
// , 0 360 .
if (angle < 0)
angle += 360;
return angle - this .options.angleOffset;
},
// .
updateAngle: function (e){
// ?
if ( this .captured){
var mouseAngle = this .getMouseAngle(e);
// .
var diffAngle = mouseAngle - this .oldMouseAngle;
// .
if (( this .angle + diffAngle >= this .options.minAngle) && ( this .angle + diffAngle <= this .options.maxAngle))
this .angle += diffAngle;
this .oldMouseAngle = this .mouseAngle = mouseAngle;
this .updateIndicatorPosition();
}
},
// .
updateValue: function (){
var value = Math.floor(
( this .options.maxValue - this .options.minValue + 1) *
( this .angle - this .options.minAngle) /
( this .options.maxAngle - this .options.minAngle)
);
// .
this .fireEvent( 'valueChanged' , ( this .options.reversed) ? this .options.maxValue - value : value);
},
// .
updateIndicatorPosition: function (){
// Math.cos() Math.sin().
var radAngle = ( this .angle + this .options.angleOffset) * this .deg2rad;
var left = this .options.radius * Math.cos(radAngle) + this .offset.x;
// "-". y .
var top = - this .options.radius * Math.sin(radAngle) + this .offset.y;
// .
this .indicator.setStyles({left: left, top: top});
// .
this .updateValue();
}
});
Source: https://habr.com/ru/post/41894/
All Articles