写点什么

贝塞尔曲线在 iOS 端的绘图实践

  • 2020-09-15
  • 本文字数:9102 字

    阅读完需:约 30 分钟

贝塞尔曲线在iOS端的绘图实践

1 前言

在这个大数据的时代,很多信息只有通过图形才能更好的展示给用户。例如:房屋的历史价格、基金股票的历史增长、数据占比分析图等。如何做图形?需要用到什么知识?本文将从 建模、显示 两方面来展开介绍。


2 建模

建模是一切图形的基础,其他内容的前提,要用代码展示一个图形,首先要有它的几何模型表达。目前在客户端二维图形建模上,Bézier curve(贝塞尔曲线)可以称为 经典 和 主流 并重的数学曲线。


对于贝塞尔曲线来说,最重要的是 起始点、终止点(也称锚点)、控制点。控制点决定了一条路径的弯曲轨迹,根据控制点的个数,贝塞尔曲线被分为:一阶贝塞尔曲线(0 个控制点)、二阶贝塞尔曲线(1 个控制点)、三阶贝塞尔曲线(2 个控制点)、N 阶贝塞尔曲线(n - 1 个控制点)。

2.1 贝塞尔曲线原理

以二阶贝塞尔曲线为例 解释说明:


起始点:P0 ; 控制点:P1 ; 终止点:P2



  1. 连接 P0P1 线 和 P1P2 线。

  2. 在 P0P1 线上找到点 A,在 P1P2 线上找到点 B,使得 P0A/AP1 = P1B/BP2

  3. 连接 AB,在 AB 上找到点 X,X 点满足:AX/XB = P0A/AP1 = P1B/BP2

  4. 找出所有满足公式:AX/XB = P0A/AP1 = P1B/BP2 的 X 点。(从 P0 到 P2 的红色曲线点为所有 X 点的连线)这条由所有 X 点组成的连线 即为 贝塞尔曲线。


二阶贝塞尔曲线 起始点:P0 ; 控制点:P1 ; 终止点:P2



三阶贝塞尔曲线 起始点:P0 ; 控制点:P1、P2; 终止点:P3



四阶贝塞尔曲线 起始点:P0 ; 控制点:P1、P2、P3 ; 终止点:P4


2.2 UIBezierPath 类

系统给我们提供了一个叫做 UIBezierPath 类,用它可以画简单的圆形,椭圆,矩形,圆角矩形,也可以通过添加点去生成任意的图形,还可以简单的创建一条二阶贝塞尔曲线和三阶贝塞尔曲线。我们来了解一下它的常用方法:

2.2.1 初始化方法

// 创建UIBezierPath对象+ (instancetype)bezierPath;// 创建在rect内的矩形+ (instancetype)bezierPathWithRect:(CGRect)rect;// 设定特定的角为圆角的矩形,corners:指定的角为圆角,其他角不变,cornerRadii:圆角的大小+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;// 创建圆弧+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;// 通过已有路径创建路径+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;// 创建三次贝塞尔曲线 endPoint:终点 controlPoint1:控制点1 controlPoint2:控制点2- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;- // 创建二次贝塞尔曲线 endPoint:终点 controlPoint:控制点- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
复制代码

2.2.2 使用方法

// 移动到某一点- (void)moveToPoint:(CGPoint)point;// 绘制一条线- (void)addLineToPoint:(CGPoint)point;// 闭合路径,即在终点和起点连一根线- (void)closePath;// 清空路径- (void)removeAllPoints;// 填充- (void)fill;// 描边,路径创建需要描边才能显示出来- (void)stroke;
复制代码

2.2.3 常用属性

// 将UIBezierPath类转换成CGPath,类似于UIColor的CGColor@property(nonatomic) CGPathRef CGPath;// path线的宽度@property(nonatomic) CGFloat lineWidth;// path端点样式@property(nonatomic) CGLineCap lineCapStyle;// 拐角样式@property(nonatomic) CGLineJoin lineJoinStyle;
复制代码

2.2.4 举个栗子🌰

先看效果👇:



代码如下:


- (void)drawRect:(CGRect)rect {
[[UIColor redColor] set];
// 右边第一个图 UIBezierPath* maskPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 100, 100) byRoundingCorners:UIRectCornerTopLeft cornerRadii:CGSizeMake(30, 30)]; maskPath.lineWidth = 20.f; maskPath.lineJoinStyle = kCGLineJoinBevel; [maskPath stroke]; // 中间第二个图 UIBezierPath* maskFillPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(200, 50, 100, 100) byRoundingCorners:UIRectCornerTopLeft cornerRadii:CGSizeMake(30, 30)]; maskFillPath.lineWidth = 20.f; maskFillPath.lineJoinStyle = kCGLineJoinBevel; [maskFillPath fill]; [maskFillPath stroke]; // 右边第三个图 UIBezierPath *maskLinePath = [UIBezierPath bezierPath]; maskLinePath.lineWidth = 20.f; maskLinePath.lineCapStyle = kCGLineCapRound; [maskLinePath moveToPoint:CGPointMake(250.0, 50)]; [maskLinePath addLineToPoint:CGPointMake(300.0, 100.0)]; [maskLinePath stroke];}
复制代码


上图中:


1)图一和图二 唯一的不同是[maskFillPath fill]方法,fill 方法要在封闭的曲线调用。


2)图一和图二 为设定特定的角为圆角的矩形,corners 为 UIRectCornerTopLeft 左上角,cornerRadii 圆角大小为 30,绿色的箭头 表示的设定的这个角。


corners 为下面五种类型typedef NS_OPTIONS(NSUInteger, UIRectCorner) {    UIRectCornerTopLeft     = 1 << 0,    // 左上角    UIRectCornerTopRight    = 1 << 1,    // 右上角    UIRectCornerBottomLeft  = 1 << 2,    // 左下角    UIRectCornerBottomRight = 1 << 3,    // 右下角    UIRectCornerAllCorners  = ~0UL        // 全部};
复制代码


3)图一和图二 黄色的箭头 设置的属性 拐角样式:lineJoinStyle kCGLineJoinBevel(缺角)


lineJoinStyle 为下面三种类型typedef CF_ENUM(int32_t, CGLineJoin) {    kCGLineJoinMiter,    // 尖角    kCGLineJoinRound,    // 圆角    kCGLineJoinBevel    // 缺角};
复制代码


4)图三 白色的箭头 设置的属性 path 端点样式:lineCapStyle kCGLineCapRound(圆形端点)


lineCapStyle 为下面三种类型typedef CF_ENUM(int32_t, CGLineCap) {    kCGLineCapButt,        // 无端点    kCGLineCapRound,    // 圆形端点    kCGLineCapSquare    // 方形端点};
复制代码


有兴趣的 可以试试别的方法属性~

2.3 波浪曲线实现

如何实现 N 阶 波浪式曲线?如何找到 N-1 个对应的控制点?


有两个方法,下图为同数据,方案一 和 方案二 分别所得曲线图。


方案一 为左边(三阶贝塞尔)图 其中 第二条的红点 为数据的位置


方案二 为右边(CatmullRom)图 其中 第二条的红点 为数据的位置



方案一:根据 创建三次贝塞尔曲线 方法 实现波浪曲线


控制点的选取方案不唯一,以下为我选择控制点的方案:


控制点 P1:CGPointMake((PrePonit.x+NowPoint.x)/2, PrePonit.y)


控制点 P2:CGPointMake((PrePonit.x+NowPoint.x)/2, NowPoint.y)


可以根据前一个点 PrePonit 和 现在的点 NowPoint 进行计算 控制点。


主要代码如下:


UIBezierPath *path = [UIBezierPath bezierPath];[path moveToPoint:[self pointAtIndex:0]];NSInteger count = self.points.count;CGPoint PrePonit;
for (NSInteger i = 0; i < count; i++) {
CGPoint NowPoint = [self pointAtIndex:i]; if(i == 0) { PrePonit = NowPoint; } else { // 利用三次曲线 形成波浪曲线 [path addCurveToPoint:point controlPoint1:CGPointMake((PrePonit.x+NowPoint.x)/2, PrePonit.y) controlPoint2:CGPointMake((PrePonit.x+NowPoint.x)/2, NowPoint.y)]; PrePonit = NowPoint; }}return path;
复制代码


方案二:使用 CatmullRom 插值样条。(有兴趣的可以百度一下~这里只简单介绍)


要点分析:


1)给定一组控制点而得到一条曲线,曲线经过给定所有数据点。


2)Catmull-Rom 公式:P(t) = 0.5 * (2*p1 + (p2 - p0) *t + (2*p0 - 5*p1 + 4*p2 - p3)* t * t + (3*p1 - p0 - 3*p2 + p3) * t * t * t);


注:t 为分割的最小粒尺寸,根据 P0 P1 P2 P3 顺序的四个点 求得 P1P2 曲线公式。


主要代码如下:


void getPointsFromBezier(void *info, const CGPathElement *element) {    NSMutableArray *bezierPoints = (__bridge NSMutableArray *)info;    CGPathElementType type = element->type;    CGPoint *points = element->points;
if (type != kCGPathElementCloseSubpath) { [bezierPoints addObject:VALUE(0)]; if ((type != kCGPathElementAddLineToPoint) && (type != kCGPathElementMoveToPoint)) [bezierPoints addObject:VALUE(1)]; } if (type == kCGPathElementAddCurveToPoint) [bezierPoints addObject:VALUE(2)];}
NSArray *pointsFromBezierPath(UIBezierPath *bpath) { NSMutableArray *points = [NSMutableArray array]; // 获取贝塞尔曲线上所有的点 CGPathApply(bpath.CGPath, (__bridge void *)points, getPointsFromBezier); return points;}
- (UIBezierPath*)smoothedPathWithGranularity:(NSInteger)granularity path:(UIBezierPath *)path { NSMutableArray *points = [pointsFromBezierPath(path) mutableCopy];
if (points.count < 4) return [path copy]; [points insertObject:[points objectAtIndex:0] atIndex:0]; [points addObject:[points lastObject]];
UIBezierPath *smoothedPath = [path copy]; [smoothedPath removeAllPoints]; [smoothedPath moveToPoint:POINT(0)];
for (NSUInteger index = 1; index < points.count - 2; index++) { CGPoint p0 = POINT(index - 1); CGPoint p1 = POINT(index); CGPoint p2 = POINT(index + 1); CGPoint p3 = POINT(index + 2);
for (int i = 1; i < granularity; i++) { // granularity 这里按照 20 粒度划分的 float t = (float) i * (1.0f / (float) granularity); float tt = t * t; float ttt = tt * t;
CGPoint pi; // 根据 CatmullRom 公式 根据 P0 P1 P2 P3 获取点的坐标 pi.x = 0.5 * (2*p1.x+(p2.x-p0.x)*t + (2*p0.x-5*p1.x+4*p2.x-p3.x)*tt + (3*p1.x-p0.x-3*p2.x+p3.x)*ttt); pi.y = 0.5 * (2*p1.y+(p2.y-p0.y)*t + (2*p0.y-5*p1.y+4*p2.y-p3.y)*tt + (3*p1.y-p0.y-3*p2.y+p3.y)*ttt); if (pi.x <= self.width) { [smoothedPath addLineToPoint:pi]; } }
if (p2.x <= self.width) { [smoothedPath addLineToPoint:p2]; } }
return smoothedPath;}
复制代码


对比总结:


方案一 控制点的选取比较难,曲线的弯度 也取决于控制点,操作简单,易理解。


方案二 曲线更顺滑,但实现更复杂,不易理解。


这里推荐两个好用的网站:


a 这个网站提供了丰富的曲线类型可供选择,非常直观。


http://easings.net


b 这个网站提供了可视化的修改两个控制点,来生成一条三阶贝塞尔曲线,并切右边还可以看到这条曲线产生的动画会做怎样的速度改变。


http://www.roblaplaca.com/examples/bezierBuilder/#

3 显示

当 layer 与贝塞尔曲线相遇,会发生什么样的神奇反应?

3.1 CALayer

苹果官网注释:“An object that manages image-based content and allows you to perform animations on that content.” 管理基于图像的内容并允许您对该内容执行动画的对象。


CALayer 主要就两方面作用:


1) 管理展示内容


2)内容可执行动画


CALayer 自身有很多情况下自带隐式动画,但是 UIView 的根 Layer 是没有隐式动画的。

3.1.1 常用属性

// 图层大小 支持隐式动画@property CGRect bounds;// 图层位置 支持隐式动画@property CGPoint position;// 在z轴上的位置 支持隐式动画@property CGFloat zPosition;// 沿z轴位置的锚点 支持隐式动画@property CGFloat anchorPointZ;// 锚点 默认在layer的中心点 取值范围(0~1) 支持隐式动画@property CGPoint anchorPoint;// 图层变换 支持隐式动画@property CATransform3D transform;// 图层大小和位置 不支持隐式动画@property CGRect frame;// 是否隐藏 支持隐式动画@property(getter=isHidden) BOOL hidden;// 图层背景是否显示 支持隐式动画@property(getter=isDoubleSided) BOOL doubleSided;// 父图层 支持隐式动画@property(nullable, readonly) CALayer *superlayer;// 子图层 支持隐式动画@property(nullable, copy) NSArray<__kindof CALayer *> *sublayers;// 子图层变换 支持隐式动画@property CATransform3D sublayerTransform;// 图层蒙版 支持隐式动画@property(nullable, strong) __kindof CALayer *mask;// 子图层是否裁切超出父图层的部分,默认为NO@property BOOL masksToBounds;// 图层显示内容 设置layer的contents可以为layer添加显示内容 支持隐式动画@property(nullable, strong) id contents;// 图层显示内容的大小和位置 支持隐式动画@property CGRect contentsRect;// 用于指定层的内容如何在其范围内定位或缩放@property(copy) CALayerContentsGravity contentsGravity;// 是否包含完全不透明内容的布尔值@property(getter=isOpaque) BOOL opaque;// 背景色 支持隐式动画@property(nullable) CGColorRef backgroundColor;// 圆角半径 支持隐式动画@property CGFloat cornerRadius;// 边框宽度 支持隐式动画@property CGFloat borderWidth;// 边框颜色 支持隐式动画@property(nullable) CGColorRef borderColor;// 透明度 支持隐式动画@property float opacity;// 阴影颜色 支持隐式动画@property(nullable) CGColorRef shadowColor;// 阴影透明度 默认为0 需要显示阴影 必须设置值 支持隐式动画@property float shadowOpacity;// 阴影偏移量 支持隐式动画@property CGSize shadowOffset;// 阴影半径 支持隐式动画@property CGFloat shadowRadius;// 阴影形状 支持隐式动画@property(nullable) CGPathRef shadowPath;
复制代码

3.1.2 子类


CALayer 的子类有很多,下面说几个比较常用的。

3.2 CAShapeLayer

苹果官网注释:“A layer that draws a cubic Bezier spline in its coordinate space.” 专门用于绘制贝塞尔曲线的 layer。

3.2.1 看一下它独特的属性:

// path属性是曲线的路径,也是它和贝塞尔曲线紧密连接一个入口,决定了图层上画的是什么形状。@property(nullable) CGPathRef path;// 填充颜色@property(nullable) CGColorRef fillColor;// 曲线 指定哪块区域为内部,内部会被填充颜色@property(copy) CAShapeLayerFillRule fillRule;// 线的颜色@property(nullable) CGColorRef strokeColor;// strokeStart 和 strokeEnd 两者的取值都是0~1,决定贝塞尔曲线的划线百分比@property CGFloat strokeStart;@property CGFloat strokeEnd;// 虚线开始的位置@property CGFloat lineDashPhase;// 虚线设置,数组中奇数位实线长度,偶数位带遍空白长度@property(nullable, copy) NSArray<NSNumber *> *lineDashPattern;// 线的宽度@property CGFloat lineWidth;// 最大斜接长度 只有lineJoin属性为kCALineJoinMiter时miterLimit才有效@property CGFloat miterLimit;// 线端点样式(样式与 贝塞尔曲线的CGLineCap 属性一致)@property(copy) CAShapeLayerLineCap lineCap;// 拐角样式(样式与 贝塞尔曲线的CGLineJoin 属性一致)@property(copy) CAShapeLayerLineJoin lineJoin;
复制代码

3.2.2 举个栗子🌰

使用上面的一些属性,再结合贝塞尔曲线,我们实现了如下一些效果:



其中图五的效果,代码实现如下:


UIBezierPath *maskPath = [UIBezierPath bezierPath];for (NSInteger i = 1; i < 9; i++) {    UIBezierPath *tempPath = [UIBezierPath bezierPathWithRect:CGRectMake(190 - 20 * i, 550 - 10 * i, 40 * i, 20 * i)];    [maskPath appendPath:tempPath];}[maskPath stroke];
// CAShapeLayerCAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];maskLayer.frame = self.view.bounds;maskLayer.path = maskPath.CGPath;maskLayer.lineWidth = 5;maskLayer.strokeColor = [UIColor purpleColor].CGColor;maskLayer.fillRule = kCAFillRuleEvenOdd;maskLayer.fillColor = [UIColor cyanColor].CGColor;maskLayer.strokeStart = 0.2;maskLayer.strokeEnd = 0.5;maskLayer.lineDashPattern = @[@(10), @(10), @(30), @(30)];maskLayer.lineDashPhase = 0;[self.view.layer addSublayer:maskLayer];
复制代码

3.3 CAGradientLayer

苹果官网注释:“A layer that draws a color gradient over its background color, filling the shape of the layer (including rounded corners)” 专门用于在背景色上绘制颜色渐变的图层,填充图层的形状。

3.3.1 看一下它独特的属性:

// colors属性是CAGradientLayer的特殊属性,完美实现几种颜色的过渡。@property(nullable, copy) NSArray *colors;// 定义每个梯度停止的位置。取值范围为0~1递增@property(nullable, copy) NSArray<NSNumber *> *locations;// 决定了变色范围的起始点@property CGPoint startPoint;// 决定了变色范围的结束点@property CGPoint endPoint;// startPoint 和 endPoint两者的连线决定变色的趋势
复制代码

3.3.2 举个栗子🌰

使用上面的一些属性我们实现了如下一些效果:



其中图五的效果,代码实现如下:


CAGradientLayer *gradientLayer = [CAGradientLayer layer];gradientLayer.frame = CGRectMake(20, 450, 150, 150);gradientLayer.locations = @[@(0.2), @(0.5), @(0.6), @(0.8)];gradientLayer.startPoint = CGPointMake(0, 0);gradientLayer.endPoint = CGPointMake(1, 1);gradientLayer.colors = @[(id)[UIColor purpleColor].CGColor, (id)[UIColor greenColor].CGColor, (id)[UIColor orangeColor].CGColor, (id)[UIColor blackColor].CGColor];[self.view.layer addSublayer:gradientLayer];
复制代码

3.4 再举个栗子🌰🌰

当 CAGradientLayer + CAShapeLayer + 贝塞尔曲线 会有什么效果?上代码~


- (void)setupUI {    // 贝塞尔曲线    UIBezierPath *maskPath = [UIBezierPath bezierPath];    [maskPath moveToPoint:CGPointMake(100, 220)];    [maskPath addLineToPoint:CGPointMake(200, 150)];    [maskPath addLineToPoint:CGPointMake(300, 220)];    [maskPath stroke];
UIBezierPath *maskBottomPath = [UIBezierPath bezierPath]; [maskBottomPath moveToPoint:CGPointMake(280, 250)]; [maskBottomPath addCurveToPoint:CGPointMake(120, 250) controlPoint1:CGPointMake(250, 320) controlPoint2:CGPointMake(150, 320)]; [maskBottomPath stroke];
[maskPath appendPath:maskBottomPath];
// CAShapeLayer CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init]; maskLayer.frame = self.view.bounds; maskLayer.path = maskPath.CGPath; maskLayer.lineWidth = 20; maskLayer.strokeColor = UIColorFromRGB(0xF0F5FF).CGColor; maskLayer.lineCap = kCALineCapRound; maskLayer.lineJoin = kCALineJoinRound; maskLayer.fillColor = [UIColor clearColor].CGColor; maskLayer.strokeStart = 0; maskLayer.strokeEnd = 0; [self.view.layer addSublayer:maskLayer];
// CAGradientLayer NSMutableArray *colorArray = [NSMutableArray new]; for (NSInteger i = 0; i < 6; i++) { [colorArray addObject:[self arc4randomColor]]; }
CAGradientLayer *gradientLayer = [CAGradientLayer layer]; gradientLayer.frame = self.view.bounds; gradientLayer.colors = colorArray; gradientLayer.startPoint = CGPointMake(0, 0.5); gradientLayer.endPoint = CGPointMake(1, 0.5); gradientLayer.mask = maskLayer; [self.view.layer addSublayer:gradientLayer];
// 创建全局并发队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 创建定时器 dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); // 设置定时器,每N秒触发 int64_t intervalInNanoseconds = (int64_t)(0.3 * NSEC_PER_SEC); dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), (uint64_t)intervalInNanoseconds, 0); // 设置定时器处理事件 dispatch_source_set_event_handler(timer, ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (maskLayer.strokeEnd < 0.6) { maskLayer.strokeEnd += 0.4; } else if (maskLayer.strokeEnd < 0.8){ maskLayer.strokeEnd += 0.2; } else if (maskLayer.strokeEnd < 1){ maskLayer.strokeEnd += 0.1; } else { maskLayer.strokeEnd = 1; if (maskLayer.strokeStart < 0.6) { maskLayer.strokeStart += 0.4; } else if (maskLayer.strokeStart < 0.8){ maskLayer.strokeStart += 0.2; } else if (maskLayer.strokeStart < 1){ maskLayer.strokeStart += 0.1; } else { [colorArray removeObjectAtIndex:0]; [colorArray addObject:[self arc4randomColor]]; gradientLayer.colors = colorArray;
maskLayer.strokeStart = 0; maskLayer.strokeEnd = 0; } } }); });
_timer = timer;
// 开启定时器 dispatch_resume(_timer);}
- (id)arc4randomColor { return (id)[UIColor colorWithRed:arc4random()%255/255.f green:arc4random()%255/255.f blue:arc4random()%255/255.f alpha:1].CGColor;}
复制代码


运行结果👇


<https://v.qq.com/x/page/l3146iykm06.html


其他 layer 怎么使用?贝塞尔曲线 + Layer 还可以组合出更多神奇的反应!感兴趣的可以去试试哦~本文仅为抛砖引玉~~


本文转载自公众号贝壳产品技术(ID:beikeTC)。


原文链接


贝塞尔曲线在iOS端的绘图实践


2020-09-15 10:083770

评论

发布
暂无评论
发现更多内容

AI 绘画平台难开发,难变现?试试 Stable Diffusion API Serverless 版解决方案

阿里巴巴云原生

阿里云 云原生 Stable Diffusion Stable

为什么要用云手机养tiktok账号

Ogcloud

云手机 海外云手机 tiktok云手机 云手机海外版

帮助某百年车企实施 ASPICE 的一点点感悟

QE_LAB

测试 汽车 质量保证 流程规范 ASPICE

软件测试岗位发展编年史

老张

职业规划 软件测试 质量保障

Wasm on Go

fliter

使用 AI 构建面向未来的 DevOps

亚马逊云科技 (Amazon Web Services)

人工智能 机器学习

Jetpack Compose -> MutableState 和 mutableStateOf()

老A说

JetPack an'droid android jetpack

软件测试学习笔记丨Charles_Mock实战

测试人

软件测试

提升企业级数据处理效率!3.0 系列版本的四个集群优化点详解

TDengine

tdengine 时序数据库

IPA上传工具的原理和使用方法

DAPP持币生息项目系统开发|步骤逻辑|源码搭建

l8l259l3365

MSE Nacos 配置变更审计平台使用指南

阿里巴巴云原生

阿里云 微服务 云原生 nacos

人工智能与低代码开发: 创新技术的未来

不在线第一只蜗牛

人工智能 低代码

用户故事地图实际应用

敏捷开发

产品设计 软件开发 用户体验 交互设计 产品管理

释放未来:2024 年区块链应用程序开发

区块链软件开发推广运营

dapp开发 区块链开发 链游开发 公链开发

智能商品计划系统在哪些方面可以帮助服装企业提高盈利能力?

第七在线

Databend 开源周报第 130 期

Databend

《代码大全(第2版)》PDF

程序员李木子

那个因AI而自杀的人类,他在恐惧什么?

脑极体

AI

《实战Java高并发程序设计(第2版)》PDF

程序员李木子

如何领导规模化敏捷变革?

敏捷开发

敏捷开发 敏捷开发管理 safe 企业转型 规模化敏捷

Nacos 官网重大升级,提供官方发行版下载包,3.0 里程碑版本新特性预告

阿里巴巴云原生

阿里云 微服务 云原生 nacos

物流平台如何与电商平台进行自动化流程管理

RestCloud

零代码 APPlink 自动化集成

文心一言 VS 讯飞星火 VS chatgpt (189)-- 算法导论14.1 6题

福大大架构师每日一题

福大大架构师每日一题

深入理解 C# 编程:枚举、文件处理、异常处理和数字相加

小万哥

C# 程序人生 编程语言 软件工程 后端开发

贝塞尔曲线在iOS端的绘图实践_大数据_孙齐_InfoQ精选文章