QQ sticky Little Red dot very funny there are wood, so they want to achieve some, see the iOS implementation of less people, Android more, so this is the use of iOS to achieve ha ~
Effect Chart:
Debug diagram:
In fact, in terms of implementation, I was first to achieve the effect of the second picture.
Cloud Habitat Community small make up to everybody say to realize the idea
1. Understand the principle and how to draw a "sticky" shape (that is, draw two circles plus two Bezier curves).
2. New UIView (Azmetaballcanvas), as a separate canvas to draw "sticky" shape, using the program to implement the algorithm, and draw out.
3. Add attach to the canvas (Azmetaballcanvas): (UIView *) method, and add gesture monitoring, redrawing, so that any view can be on the canvas has a "sticky" effect.
4. According to the distance between the heart line and to determine whether to disconnect, the user's finger to leave also to judge according to the distance from the explosion animation or rebound animation.
Detailed process
First of all, we must understand the small red dot dragging process shape is what, in fact, is similar to the ball effect (metaball). Careful observation can be found that two different sizes of the circle plus two Bezier curve composition.
On the algorithm section, I have broken down into another blog, strongly suggest that the shape is not clear how to draw students first look at "" Algorithm analysis QQ "one key retreat" of the detailed method of calculation
1. Draw and drag
Now that we know how to find the coordinates, we can do it.
First create a new "canvas" that inherits from UIView
AZMetaBallCanvas.h
@interface azmetaballcanvas:uiview
@property (nonatomic,strong) Circle *centercircle;
@property (Nonatomic,strong) Circle *touchcircle;
@end
Circle is a custom entity class that defines the basic properties of a circle, such as center coordinates, 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 row of cell, but in fact you can pull it to another cell up, this need to give small red dot enough position to draw, just create a new canvas dedicated to draw small red dot action good.
Azmetaballcanvas currently contains two properties, two circles, a center circle, a touch circle, according to the demand, the center circle should be the same position, the touch Circle will follow the position of the finger touch screen change, the following needs to be in the two circle between the Bezier curve to form a ball effect.
Next, start writing the Azmetaballcanvas implementation.
AZMETABALLCANVAS.M
#define RADIUS 40.0
@interface Azmetaballcanvas () {
uibezierpath *_path;
Cgpoint _touchpoint;
}
@end
@implementation Azmetaballcanvas
-(Instancetype) Initwithcoder: (Nscoder *) Adecoder {
self = [super Initwithcoder:adecoder];
NSLog (@ "Initwithcorder");
if (self) {
[self initdata];
}
return self;
}
-(void) InitData {
_touchcircle = [Circle initWithcenterPoint:self.center Radius:radius];
_centercircle = [Circle initWithcenterPoint:self.center Radius:radius];
_touchpoint = Self.center;
NSLog (@ "Self.center (%f,%f)", self.center.x, self.center.y);
@end
Initializes a two-circle position, defaults to the center of the view, and joins the custom initialization method InitData in the parent constructors such as Init, initWithFrame, Initwithcoder, and so on.
Overriding the drawing method
Like the Android OnDraw (), DrawRect in iOS can be overridden to draw, and then call [view Setneedsdisplay] to notify redraw.
-(void) DrawRect: (cgrect) rect {
_path = [[Uibezierpath alloc] init];
[Self drawcentercircle];
[Self drawtouchcircle:_touchpoint];
[Self drawbeziercurvewithcircle1:_centercircle circle2:_touchcircle];
}
As is said in algorithmic analysis, when drawing, we only need to draw two circles (drawcentercircle, drawtouchcircle) and connect two circles of Bezier curve (Drawbeziercurve), the algorithm is actually copy "" Algorithm Analysis "QQ" "The detailed calculation method of one key retreat"
iOS Uibezierpath with Bezier curve, its own drawing circle method AddArcWithCenter:radius:startAngle:endAngle:clockwise:, so we just call the good!
#pragma mark Draw Circle---draw a circle-(void) drawcentercircle {[self Drawcircle:_path circle:_centercircle];}-(void) Draw
Touchcircle: (cgpoint) Center {_touchcircle.centerpoint = center;
[Self Drawcircle:_path circle:_touchcircle]; }-(void) Drawcircle: (Uibezierpath *) path Circle: (Circle *) Circle {[_path addArcWithCenter:circle.centerPoint radius:
Circle.radius startangle:0 endangle:360 Clockwise:true];
[_path fill];
[_path stroke];
[_path removeallpoints]; #pragma mark Draw curve---bezier curve-(void) DrawBezierCurveWithCircle1: (Circle *) circle1 Circle2: (Circle *) circle2 {Flo
at circle1_x = circle1.centerpoint.x;
float circle1_y = Circle1.centerpoint.y;
float circle2_x = circle2.centerpoint.x;
float circle2_y = Circle2.centerpoint.y;
The length of the line is float d = sqrt (POWF (circle1_x-circle2_x, 2) + POWF (circle1_y-circle2_y, 2));
The angle of the x axis of the heart line is float angle1 = Atan ((circle2_y-circle1_y)/(circle1_x-circle2_x));
The angle between the connecting line and the Gongsche float angle2 = ASIN ((Circle1.radius-circle2.radius)/d); Tangent to center and XThe angle of the shaft is float angle3 = m_pi_2-angle1-angle2;
Float angle4 = m_pi_2-angle1 + angle2;
float offset1_x = cos (angle3) * Circle1.radius;
float offset1_y = sin (angle3) * Circle1.radius;
float offset2_x = cos (angle3) * Circle2.radius;
float offset2_y = sin (angle3) * Circle2.radius;
float offset3_x = cos (angle4) * Circle1.radius;
float offset3_y = sin (angle4) * Circle1.radius;
float offset4_x = cos (angle4) * Circle2.radius;
float offset4_y = sin (angle4) * Circle2.radius;
float p1_x = circle1_x-offset1_x;
float p1_y = circle1_y-offset1_y;
float p2_x = circle2_x-offset2_x;
float p2_y = circle2_y-offset2_y;
Float p3_x = circle1_x + offset3_x;
Float p3_y = circle1_y + offset3_y;
Float p4_x = circle2_x + offset4_x;
Float p4_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 simply to rewrite a series of methods, such as touchxxx, directly in Azmetaballcanvas, and then invoke the Setneedsdisplay notification UIView redraw.
#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 in fact, the effect of the second picture 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 change radius
-(void) Changecentercircleradiusto: (float) Radius {
_centercircle.radius = radius;
[Self setneedsdisplay];
}
-(void) Changetouchcircleradiusto: (float) Radius {
_touchcircle.radius = radius;
[Self setneedsdisplay];
}
General Edition
According to the phenomenon found, we need to drag small red dots to move it, rather than our hands pointing to where the Little red dot is, so we need to give small red dots to monitor, rather than "canvas."
Instead, we add a method-(void) Attach: (UIView *) item to the canvas, and then add a pan gesture to the incoming view.
-(void) Attach: (UIView *) Item {
Uipangesturerecognizer *drag = [[Uipangesturerecognizer alloc] Initwithtarget:self Action: @selector (drag:)];
item.userinteractionenabled = YES;
[item Addgesturerecognizer:drag];
}
-(void) Drag: (Uipangesturerecognizer *) recognizer {
//Get touch point
_touchpoint = [recognizer locationinview:self];
Get a touch of view
uiview *touchview = Recognizer.view;
Switch (recognizer.state) {case
uigesturerecognizerstatebegan:{
//touch start: Draw a copy of Touchview on the canvas
/ ... This section refer to the source break
;
}
Case uigesturerecognizerstatechanged:{
//move: Record touch position, change coordinate position of Touchview and touchcircle
[self Resettouchcenter:_touchpoint];
break;
Case uigesturerecognizerstateended: {
//touch end: According to the length of the line to determine whether the explosion animation or Spring animation
//... This section refer to the source break
;
}
Default: Break
;
}
[Self setneedsdisplay]; Redraw
}
The above content is small series to introduce the iOS set QQ small red Point elimination method (one key retreat), hope to help everyone.