美文网首页iOS学习专题iOSiOS
画板(涂鸦)实现 - iOS

画板(涂鸦)实现 - iOS

作者: Wow_我了个去 | 来源:发表于2015-11-12 09:51 被阅读9965次

画板(涂鸦)实现


设置面板.png
背景.png

2017.3.7更新

  • 不使用drawRect
  • 内存消耗低 (把demo中XIB删除掉,内存大概在15M左右)
//橡皮擦
- (void)setEraseBrush:(HBPath *)path{
    
    UIGraphicsBeginImageContextWithOptions(self.frame.size, false, 0);
    
    [self.drawImage.image drawInRect:self.bounds];
    
    [[UIColor clearColor] set];
    
    path.bezierPath.lineWidth = _lineWidth;
    
    [path.bezierPath strokeWithBlendMode:kCGBlendModeClear alpha:1.0];
    
    [path.bezierPath stroke];
    
    self.drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
}


需求

  • 更换画布背景(获取 拍照 或者 相册 的图像)
  • 具有拍照 截屏保存功能
  • 不同的画笔颜色,线宽
  • 具有撤销 返回 清屏 擦除功能

思路

主要分三大块

  • 背景
  • 画布
  • 画笔的功能界面

1.首先获取用户触摸事件开始

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    CGPoint point = [self getTouchSet:touches];

    HBPath *path = [HBPath pathToPoint:point pathWidth:_lineWidth isEraser:self.ise];

    path.pathColor = _lineColor;
    
    path.imagePath = [NSString stringWithFormat:@"%@.png",[self getTimeString]];
    
    [self.paths addObject:path];

    [self.tempPoints addObject:[HBDrawPoint drawPoint:point]];
    
    if ([self.delegate respondsToSelector:@selector(drawBoard:drawingStatus:model:)]) {
        [self.delegate drawBoard:self drawingStatus:HBDrawingStatusBegin model:nil];
    }
}

HBPath封装的NSObject对象

paths:装有HBPath对象

#pragma mark - HBPath
@interface HBPath : NSObject

@property (nonatomic, strong) UIColor *pathColor;//画笔颜色
@property (nonatomic, assign) CGFloat lineWidth;//线宽
@property (nonatomic, assign) BOOL isEraser;//橡皮擦
@property (nonatomic, assign) HBDrawingShapeType shapType;//绘制样式
@property (nonatomic, copy) NSString *imagePath;//图片路径
@property (nonatomic, strong) UIBezierPath *bezierPath;


+ (instancetype)pathToPoint:(CGPoint)beginPoint pathWidth:(CGFloat)pathWidth isEraser:(BOOL)isEraser;//初始化对象
- (void)pathLineToPoint:(CGPoint)movePoint WithType:(HBDrawingShapeType)shapeType;//画

@end

2.移动

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

    CGPoint point = [self getTouchSet:touches];

    HBPath *path = [self.paths lastObject];
    
    [path pathLineToPoint:point WithType:self.shapType];
    
    if (self.ise) {
        [self setEraseBrush:path];
    }else{
        [self.drawView setBrush:path];
    }
    
    [self.tempPoints addObject:[HBDrawPoint drawPoint:point]];
    
    if ([self.delegate respondsToSelector:@selector(drawBoard:drawingStatus:model:)]) {
        [self.delegate drawBoard:self drawingStatus:HBDrawingStatusMove model:nil];
    }
}

3.结束

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self touchesMoved:touches withEvent:event];
    
    HBPath *path = [self.paths lastObject];
    
    UIImage *image = [self screenshot:self.drawImage];
    
    self.drawImage.image = image;
    
    [self.drawView setBrush:nil];
    
    NSData *imageData = UIImagePNGRepresentation(image);//UIImageJPEGRepresentation(image, 0.4);
    
    NSString *filePath = [ThumbnailPath stringByAppendingPathComponent:path.imagePath];

    BOOL isSave = [NSFileManager hb_saveData:imageData filePath:filePath];
    
    if (isSave) {
        
        NSLog(@"%@", [NSString stringWithFormat:@"保存成功: %@",filePath]);
    }
    HBDrawModel *model = [[HBDrawModel alloc] init];
    model.paintColor = [_lineColor toColorString];
    model.paintSize = @(_lineWidth);
    model.isEraser = [NSNumber numberWithBool:path.isEraser];
    model.pointList = self.tempPoints;
    model.shapType = [NSNumber numberWithInteger:self.shapType];
    
    if ([self.delegate respondsToSelector:@selector(drawBoard:drawingStatus:model:)]) {
        [self.delegate drawBoard:self drawingStatus:HBDrawingStatusEnd model:model];
    }

    //清空
    [self.tempPoints removeAllObjects];

}

其中HBDrawModel对象的作用是:操作结束后传递给外界操作的参数

拥有以下属性:

/**所有点的集合***/
@property (nonatomic, strong) NSArray * pointList;
/**画笔的颜色***/
@property (nonatomic, copy) NSString * paintColor;
/**背景图片***/
@property (nonatomic, copy) NSString * background;
/**动作 (返回 前进 画 改变背景 清屏)默认是 Action_playing ***/
@property (nonatomic, copy) NSString * action;
/**画笔大小***/
@property (nonatomic, strong) NSNumber * paintSize;
/**设备分辨率 宽***/
@property (nonatomic, strong) NSNumber * width;
/**设备分辨率 高***/
@property (nonatomic, strong) NSNumber * hight;
/**是不是橡皮擦***/
@property (nonatomic, strong) NSNumber * isEraser;

4.绘制

- (void)setBrush:(HBPath *)path
{
    CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer;
    
    shapeLayer.strokeColor = path.pathColor.CGColor;
    shapeLayer.fillColor = [UIColor clearColor].CGColor;
    shapeLayer.lineJoin = kCALineJoinRound;
    shapeLayer.lineCap = kCALineCapRound;
    shapeLayer.lineWidth = path.bezierPath.lineWidth;
    ((CAShapeLayer *)self.layer).path = path.bezierPath.CGPath;
    
    
}

最后强调一下关于橡皮擦的注意点!

和绘制线是一样的,区别在于绘制的时候加上下面这句代码。

if (self.isEraser)
[self.bezierPath strokeWithBlendMode:kCGBlendModeClear alpha:1.0];
更新->在最上面

真正的擦除你已经画的线,跟你画布的背景是不是白色,或者其他颜色没有关系!如果你的背景是图片,设置画笔的颜色与画布的颜色一致,就不会奏效了。

当然除了上面是使用贝塞尔路径绘制以外,你也可以使用上下文去实现,找准这个属性。妈妈再也不用担心橡皮擦啦~~~

问题

当两个设备中,一端正在画,另一端绘制对方画的线。如果对方先慢画,后快画。怎么在另一端也绘制出这种速度感??
如您知道的话,希望在评论中可以给我一些思路。

解决

在这里感谢 @柯拉Sir @梦的森林 提供的思路。最新的代码已经更新,需要的朋友自取哈。

如果这个文章帮到了你,一定给我Star哦!

GitHub 欢迎围观!!

相关文章

网友评论

  • 00822452baa5:当两个设备中,一端正在画,另一端绘制对方画的线。如果对方先慢画,后快画。怎么在另一端也绘制出这种速度感??
    这个问题解决的代码在哪呢~~没找着
    00822452baa5:@Wow_我了个去 ojbk
    Wow_我了个去:看这个哥们补充的
    Wow_我了个去:https://www.jianshu.com/p/bcd864c5dece
  • 175米那道线:橡皮擦是取背景填充吗?有没有直接将笔画透明的方法?谢谢
  • 小_菜_鸟:实现橡皮擦的功能 可不可以也使用ShapeLayer呢
    现在CPU达到90多
  • __shunshun__:HBPath的实现文件呢?没有啊
    Wow_我了个去:@__shunshun__ 有的,你跟进去看
  • 骑着蜗牛追流星:大神,可以这样吗,画出来的不是画笔,是一张图片,就用图片图铺满路径,这个要怎么做呢
  • S型身材的猪:画椭圆和矩形的效果不好哦,没有跟着手指移动的方向走。手指向上移,椭圆和矩形却是向下画
  • S型身材的猪:里面是采用了图片合成技术吗?如果采用了,关键代码是哪个部分呢?画完一条线就保存,没看到下一张图片与上一张保存的图片合成起来的代码啊
  • wko3:你好 开始画的时候为什么会内存暴增啊
  • 路小白同学:如果画笔颜色的透明度为0.5,有没有方法让绘制重复路径时颜色不叠加?请大神指点一下!!
  • 梦的森林:非常感谢作者能够提供思路和代码,我也参考您的demo做了一个涂鸦做了些改进
    1、使用CAShapelayer
    2、touchesEnded后,合成layer和图片。
    3、画曲线时,更圆滑。
    这样减少了cpu的占用,不论画多少笔,都一样,不掉帧。

    https://github.com/linsendear/LSDrawTest
    Wow_我了个去:@ef83770663b8 刚刚看了下,写的灰常好。现在这个画板算是圆满了,达到了一个理想的状态了。后面抽空我就把这部分加上 :joy: 感谢您的补充哈:heart:
  • YwWyW:如果要多人一起画怎么实现啊,有没有思路?
    Wow_我了个去:@YwWyW 只需要把坐标数据传过来,主动画就可以实现了
  • 4e4d816b72a3:正在研究,虽然没看懂,楼主这么耐心的解答,给楼主点个赞!!!网络上正缺少你这样的好人
    Wow_我了个去:干巴跌:fist:
  • Jesscia_Liu:楼主 你的问题 当两个设备中,一端正在画,另一端绘制对方画的线。如果对方先慢画,后快画。怎么在另一端也绘制出这种速度感?
    这个怎么解决的
  • ccb202cc1f31:怎么橡皮擦不能用??摖不掉
  • 写代码的小农民:图片能保存吗?
    Wow_我了个去:@zsongchao001 阔以
  • 星空雪雨:楼主,我们产品现在有个需求是画笔颜色可以选择荧光色和渐变色,不知道你有没有好的思路啊?
  • 新地球说着一口陌生腔调:封装的很好
    Wow_我了个去:@新地球说着一口陌生腔调 :kissing_heart:
  • 7965f47b74d9:用这种方法绘图的话,我发现画满全屏效率就明显下降了,曲线变直线了。。。楼主有什么优化思路吗? :pray:
    Wow_我了个去:@蹦跶 :pensive::pensive::pensive:嗯。。这样就稍微简单点儿,不过也可以使用现有的方式,自己去控制到。不过就是相对来说麻烦些。需要花点时间去调了
    7965f47b74d9:@Wow_我了个去 snapLayer我也卡在橡皮擦那个功能那里。。。感觉只能用撤销和恢复代替了
    Wow_我了个去:@蹦跶 可以使用snapLayer去绘制。但是橡皮擦那个功能就不好实现了。还没有找到思路
  • FR_Zhang:问一下,涂鸦也就是俗称的画板 画板里面的钢笔的效果如何实现 有什么思路吗
    Wow_我了个去:@FR_Zhang 要不然就只有自己去区分开始跟结束哦
    FR_Zhang:@Wow_我了个去 主要是钢笔的笔锋
    Wow_我了个去:@FR_Zhang 可以试试放钢笔线的图片上去。
  • b4c171d6109e:我的qq 2464294279
    Wow_我了个去:@anshao68 使用socket传数据
    2615b30fe1f4:大神你好,多台设备之间如何实时的传递数据,请大神指教一下?
    Wow_我了个去:@帅气的那个 demo里面有数据格式。只用把这个数据传给对方就行了。接收方也有对应这些格式的操作。只需要给数据就行
  • b4c171d6109e:大神,,我想问一下,,,使用这个实现双人互动 是怎样的,,,,就是一个人画,一个人接收
  • 8a3587631f11:准确的说是直接画横线,只能画一点,急求解啊楼主
    Wow_我了个去:@13770328402 应该不得画不到噢。你看哈你有没有触摸事件就晓得了
  • 8a3587631f11:想问问为什么画的时候有时候笔触会失灵,求解答
  • 代码搬运工oO:楼主你好,
    我下载了在GitHub上最新的代码,发现一个问题,当在画矩形和圆形时做上下拖动和左右拖动,位置会发生改变!!!
    梦的森林:- (CGRect)getRectWithStartPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint
    {
    CGFloat x = startPoint.x <= endPoint.x ? startPoint.x: endPoint.x;
    CGFloat y = startPoint.y <= endPoint.y ? startPoint.y : endPoint.y;
    CGFloat width = fabs(startPoint.x - endPoint.x);
    CGFloat height = fabs(startPoint.y - endPoint.y);

    return CGRectMake(x , y , width, height);
    }

    修改下这个代码
  • ab31a0a9d2cf:楼主 你好
    这是基于路径的绘图 基于像素的绘图 怎么实现
    Wow_我了个去:@manglangfeiyuan 代码不同,思路可以套用。你自己可以试试,加油!
  • iamWill65:感谢分享,我实现了接收数据绘画速度的问题,楼主要代码的话QQ发你 376894895
    97cbe94d020f:@柯拉Sir 对这个对焦感兴趣 大神可以发我一份吗 :blush: qq:401765756
    97cbe94d020f:@柯拉Sir
    Wow_我了个去:@柯拉Sir 感激不尽! :grin: 谢谢分享哦!
  • 39ea96b65e42:要是人家想通过两指放大后来画这种需求你怎么解决。
    Wow_我了个去:@我叫蓝炎钦 这个问题确实存在。不过这个思路可以套用,不过换种实现方式罢了。后期我会再改改:stuck_out_tongue::stuck_out_tongue::stuck_out_tongue:。感谢您提的建议:kissing_heart:
    39ea96b65e42:重复调用这个方法, [self setNeedsDisplay];如果你画多了的话,内存过高的。

    Wow_我了个去:@我叫蓝炎钦 添加手势,放大就好。具体可以看需求的,实现是没问题的 :stuck_out_tongue_closed_eyes:
  • 然然啊:有没有demo啊,求一个demo
    Wow_我了个去:@然然啊 分了两层。有一层图片,有一层画板。
    然然啊:@Wow_我了个去 你好,我还想请问一下,背景图片是怎么加上去的啊?
    Wow_我了个去:@然然啊 文章后面有个链接啊!你点击就对了 :smiley: 谢谢支持。多提建议哈
  • 夏都:考虑使用曲线代替直线让画出来的线更平滑,另外在iOS9以后有touch点预测以及iPad Air2以上有更高的touch 刷新频率相关的api可以优化一下:stuck_out_tongue_winking_eye:
    欢欢1206:@666天空蓝 二次贝塞尔曲线,再根据velocityInView获取速度,可设置线条的粗细
    runnerisme:@夏都 “曲线代替直线”,请大侠赐教
  • Shirley静甄:这是什么软件?我怎么不知道?有没有发短信也可以发自己画的东西的
    Wow_我了个去:@Shirley静甄 现在这个Demo的话,包含接受数据,自己画的功能。截图功能也有,发送的话看你自己喜好。如果你要是说的上架的APP,关于画图的话。你可以自己找一下,我也不是很清楚。 :yum:
    Shirley静甄:@Wow_我了个去 就是有没有其他的比较好用的软件可以自己画图后发送给朋友的软件 谢谢
    Wow_我了个去:@Shirley静甄 什么意思?可以再明确点嘛?

本文标题:画板(涂鸦)实现 - iOS

本文链接:https://www.haomeiwen.com/subject/esbhhttx.html