QQ sticky Little Red dot is very fun has wood, so I also want to achieve some, see iOS implementation of less people, Android more, so this is using iOS to achieve HA ~
:
Debug diagram:
In fact, in terms of implementation, I first realized the effect of the second picture.
Implementation ideas
1. Understand the principle and how to draw a "sticky" shape (i.e. draw two circles plus two Bezier curves).
2. New UIView (Azmetaballcanvas), as a separate canvas for drawing "sticky" shape, the program implementation algorithm, and draw out.
3. Add a method to the canvas (Azmetaballcanvas) attach:(UIView *)
and add a gesture listener to redraw it so that any view can have a "sticky" effect on the canvas.
4. Depending on the distance of the connecting line and judging whether or not to disconnect, the user's finger should be judged by distance to explode animation or rebound animation.
Detailed procedures
First of all we must understand the small red dot drag the process shape is what, in fact, is similar to the meta-ball effect (metaball). Careful observation of the analysis found that two different sizes of the circle plus two Bezier curve composition.
About the algorithm part, I have broken down into another blog post, it is strongly suggested that the shape is how to draw the students first look at the "algorithm analysis" QQ "a key to return" the detailed method of calculation
1. Draw drag
Now that we know how to draw the coordinate points, we can go to the realization.
Start by creating a new "canvas" that inherits fromUIView
//AZMetaBallCanvas.h@interface AZMetaBallCanvas : UIView@property(nonatomic,strong) Circle *centerCircle;@property(nonatomic,strong) Circle *touchCircle;@end
Circle
To customize the entity class, it defines some of the basic properties of the circle, such as the center coordinate, radius, and so on.
Why create a new canvas?
Because the small red dot is able to drag the full screen, although QQ on it there is a line Cell
, but in fact you can pull it to other Cell
up, this need to small red dot enough to draw, simply create a new canvas dedicated to the small red dots to draw the action good.
AZMetaBallCanvas
Currently contains two properties, two circles, a central circle, a touch circle, according to demand, the center circle should be the same position, the touch circle will follow the finger to touch the position of the screen, and then need to draw a Bezier curve between the two circles to form a meta-sphere effect.
The next thing AZMetaBallCanvas
to start writing is the implementation
//AZMETABALLCANVAS.M#define RADIUS 40.0 @interface azmetaballcanvas() {Uibezierpath *_path;Cgpoint_touchpoint;}@end @implementation azmetaballcanvas -(Instancetype) Initwithcoder: (Nscoder *) Adecoder { Self= [SuperInitwithcoder:adecoder];NSLog(@"Initwithcorder");if( Self) { [ SelfInitData]; }return Self;} - (void) InitData {_touchcircle = [Circle initwithcenterpoint: Self. CenterRadius:radius]; _centercircle = [Circle initwithcenterpoint: Self. CenterRadius:radius]; _touchpoint = Self. Center;NSLog(@"Self.center (%f,%f)", Self. Center. x, Self. Center. Y);}@end
Initializes the position of the two circles, defaults to View
the center, and joins the init
initWithFrame
custom initialization method in the,, and so on initWithCoder
parent class constructors initData
.
overriding drawing methods
Like in Android onDraw()
, iOS drawRect
can be overridden to draw and then called [view setNeedsDisplay]
to notify redraw.
- (void)drawRect:(CGRect)rect { _path = [[UIBezierPath alloc] init]; [self drawCenterCircle]; [self drawTouchCircle:_touchPoint]; [self drawBezierCurveWithCircle1:_centerCircle Circle2:_touchCircle];}
As described in the algorithm analysis, when drawing, we only need to draw two circle ( drawCenterCircle
, drawTouchCircle
) and connect two circles Bezier curve ( drawBezierCurve
), the algorithm is actually copied "algorithm analysis" QQ "one key backward" of the detailed calculation method
iOS comes with Bezier curves UIBezierPath
, with its own circle method addArcWithCenter: radius: startAngle: endAngle: clockwise:
, so we just call it!
#pragmaMark Draw Circle---Circle- (void) drawcentercircle {[self Drawcircle:_path circle:_centercircle];} - (void) drawtouchcircle:(cgpoint)Center {_touchcircle. Center Point= Center; [Self DrawCircle:_Path Circle:_TouchCircle];}- (void)drawcircle:(uibezierpath *)path circle:(Circle *)Circle {[ _path addarcwithcenter:circle. Center PointRadius:circle.radius startAngle:0 EndAngle: theClockwise:true]; [_Path fill]; [_Path stroke]; [_Path removeallpoints];}
#pragma mark Draw curve---draw Bezier curves- (void) DrawBezierCurveWithCircle1: (Circle *) Circle1 Circle2: (Circle *) Circle2 {floatcircle1_x = Circle1.centerpoint.x;floatcircle1_y = Circle1.centerpoint.y;floatcircle2_x = Circle2.centerpoint.x;floatcircle2_y = Circle2.centerpoint.y;//The length of the Connecting Heart line floatD =sqrt(POWF (circle1_x-circle2_x,2) + POWF (circle1_y-circle2_y,2));///The angle of the x-axis of the connecting Heart line floatAngle1 =Atan((circle2_y-circle1_y)/(circle1_x-circle2_x));//The angle of the connecting line and the Gongsche floatAngle2 =ASIN((Circle1.radius-circle2.radius)/d);//Tangent to center point and x-axis angle floatAngle3 = M_pi_2-angle1-angle2;floatAngle4 = M_pi_2-angle1 + angle2;floatoffset1_x =Cos(angle3) * Circle1.radius;floatOffset1_y =Sin(angle3) * Circle1.radius;floatoffset2_x =Cos(angle3) * Circle2.radius;floatOffset2_y =Sin(angle3) * Circle2.radius;floatoffset3_x =Cos(angle4) * Circle1.radius;floatOffset3_y =Sin(angle4) * Circle1.radius;floatoffset4_x =Cos(angle4) * Circle2.radius;floatOffset4_y =Sin(angle4) * Circle2.radius;floatp1_x = circle1_x-offset1_x;floatp1_y = circle1_y-offset1_y;floatp2_x = circle2_x-offset2_x;floatp2_y = circle2_y-offset2_y;floatp3_x = circle1_x + offset3_x;floatp3_y = circle1_y + offset3_y;floatp4_x = circle2_x + offset4_x;floatp4_y = circle2_y + offset4_y; Cgpoint P1 = Cgpointmake (p1_x, p1_y); Cgpoint P2 = cgpointmake (p2_x, p2_y); Cgpoint p3 = Cgpointmake (p3_x, p3_y); Cgpoint P4 = Cgpointmake (p4_x, p4_y); Cgpoint P1_CENTER_P4 = Cgpointmake ((p1_x + p4_x)/2, (p1_y + p4_y)/2); Cgpoint p2_center_p3 = Cgpointmake ((p2_x + p3_x)/2, (p2_y + p3_y)/2); [Self drawbeziercurvestartat:p1 endat:p2 controlpoint:p2_center_p3]; [Self drawlinestartat:p2 ENDAT:P4]; [Self drawbeziercurvestartat:p4 endat:p3 CONTROLPOINT:P1_CENTER_P4]; [Self drawlinestartat:p3 endat:p1]; [_path MOVETOPOINT:P1]; [_path Closepath]; [_path stroke];}
2. Listening gesture Simple version
The simplest is actually a series of AZMetaBallCanvas
methods that are rewritten directly in touchXXX
, and then in which the call notification is redrawn setNeedsDisplay
UIView
.
#pragma mark touch event- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; _touchPoint = [touch locationInView:self]; [self setNeedsDisplay];}- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; _touchPoint = [touch locationInView:self]; [self setNeedsDisplay];}
Now actually almost the second picture of the effect has come out, the difference is to change the radius of the two circle method.
The way to change the radius is very simple.
#pragma 改变半径-(void)changeCenterCircleRadiusTo:(float)radius { _centerCircle.radius = radius; [self setNeedsDisplay];}-(void)changeTouchCircleRadiusTo:(float)radius { _touchCircle.radius = radius; [self setNeedsDisplay];}
Normal edition
According to the phenomenon found, we need to drag the little red dots to move it, rather than our hands pointing, where the Little red dot, so we need to give the little red dot to increase gesture monitoring, rather than the "canvas."
Instead, we add a method to the canvas - (void)attach:(UIView *)item;
and then add gestures to the incoming view Pan
.
- (void) Attach: (UIView*) Item {Uipangesturerecognizer *drag = [[Uipangesturerecognizer alloc] Initwithtarget: SelfAction@selector(drag:)]; Item. userinteractionenabled=YES; [Item Addgesturerecognizer:drag];} - (void) Drag: (Uipangesturerecognizer *) Recognizer {//Get Touch point_touchpoint = [Recognizer Locationinview: Self];//Get Touch View UIView*touchview = recognizer. View;Switch(recognizer. State) { Caseuigesturerecognizerstatebegan:{//touch Start: Draw a copy of the Touchview on the canvas //... This section see the source code Break; } Caseuigesturerecognizerstatechanged:{//move: Record touch position, change coordinate position of Touchview and Touchcircle[ SelfResettouchcenter:_touchpoint]; Break; } CaseUigesturerecognizerstateended: {//touch End: Judging by the length of the centerline is the execution of an exploded animation or a spring animation //... This section see the source code Break; }default: Break; } [ SelfSetneedsdisplay];// Redraw}
Because the code is more, all paste will be messy, so I use the language description instead of paste code, need to source the students can go to the bottom of the article to download.
SOURCE Address: Https://github.com/Xieyupeng520/AZMetaBall ((づ ̄3 ̄) づ╭?~ seeking the Stars)
"iOS effect set" to achieve QQ to eliminate the red dot (one-click backward) effect