Circular Slider
Posted: January 8th, 2010 | Author: admin | Filed under: Uncategorized | 8 Comments »One of the challanges in the design of www.flurrious.com was getting those circular sliders in. Of course you can fake it and create an animation of a slider and control the timeline from 0-100, but that’s lame and not so easy to modify (like change the radius, sweep angle etc.). so, I set to create a circular slider.
I had the basic principles in place: create a circle, and create a beginSwipe andgle and an endSwipe angle. done:
the code looks something like this:
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
/**
* @author iasseo
*/
public class CircularSlider extends MovieClip
{
private var _circ : Sprite;
private var _startAngle : Number = 90;
private var _angleRange : Number = 30;
private var _angleShape : Sprite;
private var _radius : Number = 250;
private var topAngle : Number;
private var bottmAngle : Number;
public function CircularSlider()
{
calcAngles();
drawCircle();
initAngle();
addEventListener(Event.ENTER_FRAME, onEF);
}
private function calcAngles() : void
{
topAngle = _startAngle - _angleRange - 90;
topAngle = topAngle / 180 * Math.PI;
bottmAngle = _startAngle + _angleRange - 90;
bottmAngle = bottmAngle / 180 * Math.PI;
}
private function initAngle() : void
{
_angleShape = new Sprite();
_angleShape.x = stage.stageWidth/2;
_angleShape.y = stage.stageHeight/2;
addChild(_angleShape);
}
private function drawAngle() : void
{
_angleShape.graphics.lineStyle(2, 0xff0000);
_angleShape.graphics.lineTo(Math.cos(topAngle)*_radius, Math.sin(topAngle)*_radius);
_angleShape.graphics.lineStyle(2, 0x00ff00);
_angleShape.graphics.lineTo(Math.cos(bottmAngle)*_radius, Math.sin(bottmAngle)*_radius);
_angleShape.graphics.lineStyle(2, 0xff0000);
_angleShape.graphics.lineTo(0, 0);
}
private function drawCircle() : void
{
_circ = new Sprite();
_circ.graphics.lineStyle(1,0xffffff);
_circ.graphics.drawCircle(0, 0, _radius);
_circ.graphics.endFill();
_circ.x = stage.stageWidth/2;
_circ.y = stage.stageHeight/2;
addChild(_circ);
}
private function onEF(e:Event):void
{
_angleRange = Math.abs(stage.stageHeight/2 - mouseX);
_angleShape.graphics.clear();
calcAngles();
drawAngle();
}
}
}
Next, I needed to place the scroll handlebar at the top of the sweep (top of green line in picture), and have it be draggable to the bottom of the sweep. To do that, I calculated the X,Y position of the top and bottom (which I already have in the code), and upon click, I created a rectangle that represented the bounding box for the drag operation. Then, I calculated the percentage point of the handle inside that bounding box to get the 0-1 result, and using the Y position, extracted what the X position would be:
The only problem is that now, when I remove the guides (the circle and angles), I am left with only the slider… and I need the arc so the slider has some visual representation. There are 2 solutions for that. Simple solution: mask the circle… easy to do. here’s the result:
A solution that’s a bit more complex is to DRAW the arc.. that’s where I was happy to find this nice post by the algorythmist. So, using this code, you can then create a nice circular slider:
now all that’s left to do is output the percentage from the slider and you got yourself a nice circular slider!
here’s the final code:
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
/**
* @author iasseo
*/
public class CircularSlider extends MovieClip
{
private var _circ : Sprite;
private var _startAngle : Number = 90;
private var _angleRange : Number = 30;
private var _angleShape : Sprite;
private var _radius : Number = 250;
private var topAngle : Number;
private var bottmAngle : Number;
private var _handle : Sprite;
private var _box : Sprite;
private var _handleHolder : Sprite;
private var _mask : Sprite;
public function CircularSlider()
{
calcAngles();
drawCircle();
initAngle();
drawAngle();
drawSegments();
createHandle();
}
private function createHandle() : void
{
_handleHolder = new Sprite();
_handleHolder.x = stage.stageWidth/2;
_handleHolder.y = stage.stageHeight/2;
addChild(_handleHolder);
_handle = new Sprite();
_handle.graphics.beginFill(0xcccccc);
_handle.graphics.drawCircle(0, 0, 10);
_handle.graphics.endFill();
_handle.buttonMode = true;
_handle.x = Math.cos(topAngle)*_radius;
_handle.y = Math.sin(topAngle)*_radius;
_handleHolder.addChild(_handle);
_handle.addEventListener(MouseEvent.MOUSE_DOWN, onStartDrag);
}
private function onStartDrag(e : MouseEvent) : void
{
makeBox();
drawBox();
var rect : Rectangle = _box.getRect(_box);
_handle.startDrag(false, rect);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onStopDrag);
}
private function onMouseMove(e : MouseEvent) : void
{
e.updateAfterEvent();
var rect : Rectangle = _box.getRect(_box);
var dist : Number = rect.height;
var per : Number = (_handle.y + dist/2)/dist;
var a : Number = _startAngle - _angleRange;
var b : Number = _startAngle + _angleRange;
var r : Number = b-a;
var targetAngle :Number = a + r*per;
targetAngle = targetAngle / 180 * Math.PI;
_handle.x = Math.sin(targetAngle)*_radius + 2;
}
private function onStopDrag(e : MouseEvent) : void
{
removeBox();
_handle.stopDrag();
_handle.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, onStopDrag);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
}
private function removeBox() : void
{
_box.graphics.clear();
removeChild(_box);
}
private function drawBox() : void
{
_box.graphics.lineStyle(1,0xffffff, 0);
_box.graphics.moveTo(Math.cos(topAngle)*_radius, Math.sin(topAngle)*_radius);
_box.graphics.lineTo(_radius, Math.sin(topAngle)*_radius);
_box.graphics.lineTo(_radius, Math.sin(bottmAngle)*_radius);
_box.graphics.lineTo(Math.cos(topAngle)*_radius, Math.sin(bottmAngle)*_radius);
_box.graphics.lineTo(Math.cos(bottmAngle) * _radius, Math.sin(topAngle)*_radius);
}
private function makeBox() : void
{
_box = new Sprite();
_box.x = stage.stageWidth/2;
_box.y = stage.stageHeight/2;
_box.mouseEnabled = false;
_box.mouseChildren = false;
addChild(_box);
}
private function calcAngles() : void
{
topAngle = _startAngle - _angleRange - 90;
topAngle = topAngle / 180 * Math.PI;
bottmAngle = _startAngle + _angleRange - 90;
bottmAngle = bottmAngle / 180 * Math.PI;
}
private function drawSegments() : void
{
_angleShape.graphics.lineStyle(2, 0x0000ff);
// FROM THE ALGORYTHMIST -- http://algorithmist.wordpress.com/2009/12/01/drawing-circular-segments-in-actionscript/
// now that start and end angle are fixed, draw a set of quads no more than PI/4 in arc at once
var angleDelta:Number = bottmAngle - topAngle;
var numSeg:Number = Math.ceil(angleDelta*4.0/Math.PI);
var arc:Number = angleDelta/numSeg;
// p is the vector from the origin of the wedge to (p0X,p0Y)
// q is the vector from the origin of the wedge to (p2X,p2Y)
// the vector p+q bisects the angle between p and q. The middle interpolation point is
// 'radius' units along that bisector.
var pX:Number = _radius*Math.cos(topAngle);
var pY:Number = _radius*Math.sin(topAngle);
var p0X:Number = pX;
var p0Y:Number = pY;
var qX:Number = 0;
var qY:Number = 0;
var angle:Number = topAngle;
// initial point at startAngle
var startX:Number = _radius*Math.cos(topAngle);
var startY:Number = _radius*Math.sin(topAngle);
_angleShape.graphics.moveTo( startX, startY );
var radInv:Number = 1.0/_radius;
// approximate each arc with a quad. Bezier
for( var i:uint=0; i<numSeg; ++i )
{
angle += arc;
qX = _radius*Math.cos(angle);
qY = _radius*Math.sin(angle);
var p2X:Number = qX;
var p2Y:Number = qY;
// unit vector in direction of bisector - alternative approach is two more trig. calcs to compute the coordinates.
// let's have some fun and do it a different way.
var dX:Number = (pX+qX)*radInv;
var dY:Number = (pY+qY)*radInv;
var d:Number = Math.sqrt(dX*dX + dY*dY);
dX /= d;
dY /= d;
// middle interpolation point is a distance of 'radius' units along direction of bisecting unit vector
var p1X:Number = _radius*dX;
var p1Y:Number = _radius*dY;
// compute control point so that quad. Bezier passes through (p0X,p0Y), (p1X,p1Y), and (p2X,p2Y) at t=0.5
var cX:Number = 2.0*p1X - 0.5*(p0X + p2X);
var cY:Number = 2.0*p1Y - 0.5*(p0Y + p2Y);
// You can compute the control point directly with nothing but sin & cos, but if memory serves it takes
// four more trig comps. for a total of six per loop iteration.
_angleShape.graphics.curveTo(cX, cY, p2X, p2Y);
// end point is start point for next iteration
p0X = p2X;
p0Y = p2Y;
pX = qX;
pY = qY;
}
}
private function initAngle() : void
{
_angleShape = new Sprite();
_angleShape.x = stage.stageWidth/2;
_angleShape.y = stage.stageHeight/2;
addChild(_angleShape);
}
private function drawAngle() : void
{
_angleShape.graphics.lineStyle(2, 0xff0000);
_angleShape.graphics.lineTo(Math.cos(topAngle)*_radius, Math.sin(topAngle)*_radius);
_angleShape.graphics.lineStyle(2, 0x00ff00);
_angleShape.graphics.lineTo(Math.cos(bottmAngle)*_radius, Math.sin(bottmAngle)*_radius);
_angleShape.graphics.lineStyle(2, 0xff0000);
_angleShape.graphics.lineTo(0, 0);
}
private function drawCircle() : void
{
_circ = new Sprite();
_circ.graphics.lineStyle(1,0xffffff);
_circ.graphics.drawCircle(0, 0, _radius);
_circ.graphics.endFill();
_circ.x = stage.stageWidth/2;
_circ.y = stage.stageHeight/2;
// TO MASK OUT THE CIRCLE, YOU CAN USE THIS:
/*_mask = new Sprite();
_mask.x = stage.stageWidth/2;
_mask.y = stage.stageHeight/2;
_mask.mouseEnabled = false;
_mask.mouseChildren = false;
addChild(_mask);
_mask.graphics.beginFill(0xff0000, 1);
_mask.graphics.drawRect(Math.cos(topAngle)*_radius, Math.sin(topAngle)*_radius, _radius-Math.cos(topAngle)*_radius + 1, _radius/2-Math.sin(topAngle)*_radius);
*
*/
_circ.mask = _mask;
addChild(_circ);
}
}
}




Маленький еще.
Мне интересно здесь
“Достаточно интересная и познавательная тема”
Сайт отличный, буду рекомендовать знакомым!
Каждый из нас рано или поздно сталкивался с проблемой выбора: будь то обычная покупка или подарок близкому человеку.Посетите наш электронный ресурс, где содержатся самые разнообразные статьи и материалы, в которых профессионалы расскажут вам о качествах тех или иных товаров.
а в каком это городе,какой стране??очень креативненько!!!!!)))))
Вах-вах-вах
Кто не умеет пользоваться счастьем, когда оно приходит, не должен жаловаться, когда оно проходит. – М. Сервантес