Android从0到1绘制冬奥吉祥物冰墩墩,实现一户一墩
北京冬奥虽然已经结束了,但吉祥物冰墩墩的热度依然不减,毫不疑问冰墩墩是今年冬奥名副其实的新晋顶流。现在网上还流传着一句很贴切的话,那就是一“墩”难求。既然身无余“墩”,那就画一个梦中情“墩”,学会绘制冰墩墩,手动实现一户一墩不是梦,赶快学起来吧。
先来看看最终效果图:
根据效果图,我们大概可以看出这得自定义绘制,此冰墩墩基本是由点、矩形、椭圆、圆等常规图形构成,重点还是使用了比较多的曲线绘制而成,常规图形可以使用Canvas绘制,曲线则要使用Path绘制贝塞尔曲线,我们先来回顾一下涉及的知识点。
一、涉及知识点
回顾一下画笔paint涉及的API:
回顾一下canvas绘制涉及的API:
回顾一下path涉及的API:
具体到每个API这里就不展开讲解了,如有需要可以查阅相关资料。
二、实现步骤
1、首先画一个椭圆,如下图所示:
这个还是比较简单的,绘制长半轴在y轴的椭圆,椭圆具体数值短半轴为屏幕宽度/4,长半轴为屏幕高度/6,具体代码如下:
private void init() {//初始化矩阵rectF = new RectF();//创建画笔paint = new Paint();//设置画笔颜色paint.setColor(Color.BLACK);//设置画笔宽度paint.setStrokeWidth(10);//设置画笔填充方法:描边paint.setStyle(Paint.Style.STROKE);//设置抗锯齿paint.setAntiAlias(true);paintBlack.setColor(Color.BLACK);paintBlack.setStrokeWidth(10);paintBlack.setStyle(Paint.Style.FILL);paintBlack.setAntiAlias(true);paintRed.setColor(Color.parseColor("#AA292D"));paintRed.setStrokeWidth(10);paintRed.setStyle(Paint.Style.FILL);paintRed.setAntiAlias(true);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {//设置中心点mCentenWith = w / 2;mCentenHight = h / 2;//设置椭圆长、短半轴mWith = mCentenWith / 2;mHight = mCentenHight / 3;super.onSizeChanged(w, h, oldw, oldh);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制头部//将坐标原点移动到屏幕中心canvas.translate(mCentenWith, mCentenHight);//设置矩阵坐标rectF.set(-mWith, -mHight, mWith, mHight);//画椭圆canvas.drawOval(rectF, paint);}
2、然后再画两个耳朵,并填满颜色,如图所示:
这里绘制用到的是二阶贝塞尔曲线,还涉及到椭圆上的点x、y坐标的确认,根据参数方程:
x=acost;y=bsint;焦点在y轴上,短半轴为a,长半轴为b,t为坐标角度
这里二阶贝塞尔曲线起点设置在椭圆角度为π/5处,而终点设置在椭圆角度为π/3处,具体代码如下所示:
private Path mPathEar1 = new Path();private Path mPathEar2 = new Path();@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制右耳朵// x=acost;y=bsint;焦点在y轴上,短半轴为a,长半轴为b//起点float ear_x1 = (float) (mWith * Math.cos(Math.PI / 5));float ear_y1 = (float) (mHight * Math.sin(Math.PI / 5));//终点float ear_x2 = (float) (mWith * Math.cos(Math.PI / 3));float ear_y2 = (float) (mHight * Math.sin(Math.PI / 3));//移动起点位置mPathEar1.moveTo(ear_x1, -ear_y1);//绘制曲线mPathEar1.quadTo(mWith, -mHight, ear_x2, -ear_y2);canvas.drawPath(mPathEar1, paintBlack);//绘制左耳朵//移动起点位置mPathEar2.moveTo(-ear_x1, -ear_y1);//绘制曲线mPathEar2.quadTo(-mWith, -mHight, -ear_x2, -ear_y2);canvas.drawPath(mPathEar2, paintBlack);}
3、接下来就是绘制冰墩墩的两只手臂啦,在右手臂再画一个小心心在手上颜色填满,如图所示:
这里就要用到三阶贝塞尔曲线啦,这里的难点主要是控制点的坐标不好掌握,我这里绘制的点都是通过测试获取的,所以调试时间还是用的比较多的,最好的请设计帮忙,通过ps给个大致估算,这里只是跟着感觉试出来的,这里的爱心是通过四段三阶贝塞尔曲线合成的爱心,具体代码如下:
private Path mPathHandRight = new Path();private Path mPathHandLeft = new Path(); private Path mPathHeart = new Path(); @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制右手//终点float hand_x = (float) (mWith * Math.cos(Math.PI / 8));float hand_y = (float) (mHight * Math.sin(Math.PI / 8));//移动起点mPathHandRight.moveTo(mWith, 0);//绘制曲线mPathHandRight.cubicTo(mWith * 7 / 6, -mHight / 2, 2 * mWith, -mHight / 4, hand_x, hand_y);canvas.drawPath(mPathHandRight, paintBlack);//绘制右手红心,这里的爱心是通过四段三阶贝塞尔曲线合成的爱心//重新设置坐标原点,将画布平移到右手位置canvas.translate(mWith * 9 / 7, -mHight / 8);//将画布旋转30度canvas.rotate(30);//移动起点mPathHeart.moveTo(0, -2);//绘制曲线mPathHeart.cubicTo(mWith / 16, -mHight / 16, mWith / 12, -mHight / 16, mWith / 12, 0);canvas.drawPath(mPathHeart, paintRed);//绘制曲线mPathHeart.cubicTo(mWith / 12, 0, mWith / 12, mHight / 16, 0, mHight / 12);canvas.drawPath(mPathHeart, paintRed);//移动起点mPathHeart.moveTo(0, -2);//绘制曲线mPathHeart.cubicTo(-mWith / 16, -mHight / 16, -mWith / 12, -mHight / 16, -mWith / 12, 0);canvas.drawPath(mPathHeart, paintRed);//绘制曲线mPathHeart.cubicTo(-mWith / 12, 0, -mWith / 12, mHight / 16, 0, mHight / 12);canvas.drawPath(mPathHeart, paintRed);//将画布还原回去canvas.rotate(-30);canvas.translate(-mWith * 9 / 7, mHight / 8);//绘制左手//移动起点mPathHandLeft.moveTo(-mWith, 0);//绘制曲线mPathHandLeft.cubicTo(-mWith * 5 / 3, mHight / 2, -mWith * 7 / 6, mHight, -hand_x, hand_y);canvas.drawPath(mPathHandLeft, paintBlack);}
4、接下来就是画冰墩墩的两只脚啦,颜色填满,如图所示:
这里也要用到三阶贝塞尔曲线,控制点也是通过椭圆的长短半轴试出来的,具体代码如下所示:
private Path mPathArmRight = new Path();private Path mPathArmLeft = new Path();@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制右脚//起点float arm_x = (float) (mWith * Math.cos(Math.PI / 3));float arm_y = (float) (mHight * Math.sin(Math.PI / 3));//终点float arm_final_x = (float) (mWith * Math.cos(Math.PI * 4 / 9));float arm_final_y = (float) (mHight * Math.sin(Math.PI * 4 / 9));//移动起点mPathArmRight.moveTo(arm_x, arm_y);//绘制曲线mPathArmRight.cubicTo(arm_x + mWith / 12, mHight * 3 / 2, arm_x / 4, mHight * 3 / 2, arm_final_x, arm_final_y);canvas.drawPath(mPathArmRight, paintBlack);//绘制左脚//移动起点mPathArmLeft.moveTo(-arm_x, arm_y);//绘制曲线mPathArmLeft.cubicTo(-(arm_x + mWith / 12), mHight * 3 / 2, -(arm_x / 4), mHight * 3 / 2, -arm_final_x, arm_final_y);canvas.drawPath(mPathArmLeft, paintBlack);}
5、接下来就是绘制头部轮廓啦,先在内部绘制类似馒头的五环形状,如图所示:
这里主要用到的也是三阶贝塞尔曲线,控制点数值也是模拟出来的,具体如下所示:
private Path mPathBody = new Path();private int[] colors = {Color.parseColor("#88C46A"), Color.parseColor("#E5C267"),Color.parseColor("#6872AE"), Color.parseColor("#AE2C6D"), Color.parseColor("#6CC4F3")};@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制脸部轮廓for (int i = 0; i < 5; i++) {float r = mWith * 7 / 8 - 10 * i;mPathBody.reset();//移动起点mPathBody.moveTo(-r, 0);//控制点float x1 = mWith * 3 / 4 - 10 * i;float y1 = mHight / 2 - 10 * i;mPathBody.cubicTo(-x1, y1, x1, y1, r, 2);//控制点float x2 = mWith - 10 * i;float y2 = mHight * 5 / 4 - 10 * i;mPathBody.cubicTo(x2, -y2, -x2, -y2, -r, 2);//设置颜色paint.setColor(colors[i]);canvas.drawPath(mPathBody, paint);}//还原设置paint.setColor(Color.BLACK);}
6、接下来就是画眼睛啦,画两个椭圆黑眼圈,如图所示:
这里还是比较简单的,就是绘制椭圆,然后通过旋转,具体代码如下所示:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制右眼睛轮廓//绘制矩阵rectF.set(mWith / 4, -mHight * 2 / 5, mWith * 3 / 4, mHight / 6);//旋转角度canvas.rotate(-40);//绘制椭圆canvas.drawOval(rectF, paintBlack);//还原角度canvas.rotate(40);//绘制左眼睛轮廓//绘制矩阵rectF.set(-mWith / 4, -mHight * 2 / 5, -mWith * 3 / 4, mHight / 6);//旋转角度canvas.rotate(40);//绘制椭圆canvas.drawOval(rectF, paintBlack);//还原角度canvas.rotate(-40);}
椭圆里再画两个大眼睛,如图所示:
这里绘制大眼睛还是比较简单的,就是绘制两个圆,代码如下所示:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制右眼睛paint.setColor(Color.WHITE);canvas.drawCircle(mWith / 4, -mHight * 2 / 5, mWith / 6, paint);//还原设置paint.setColor(Color.BLACK);//绘制左眼睛paint.setColor(Color.WHITE);canvas.drawCircle(-mWith / 4, -mHight * 2 / 5, mWith / 6, paint);//还原设置paint.setColor(Color.BLACK);}
最后上色,留出眼白,点上高光比较可爱,如图所示:
这里的眼白也是用圆绘制,具体代码如下所示:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制右眼珠paint.setStyle(Paint.Style.FILL);canvas.drawCircle(mWith / 3, -mHight * 3 / 7, mWith / 20, paint);//还原设置paint.setStyle(Paint.Style.STROKE);//绘制左眼珠paint.setStyle(Paint.Style.FILL);canvas.drawCircle(-mWith / 3, -mHight * 3 / 7, mWith / 20, paint);//还原设置paint.setStyle(Paint.Style.STROKE);}
7、最后绘制冰墩墩的鼻子和嘴巴,如图所示:
这里鼻子就是一个圆,嘴巴用到了二阶贝塞尔曲线和点,这也是比较简单的,具体代码如下所示:
private Path mPathMouse = new Path();@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制鼻子paint.setStyle(Paint.Style.FILL);//绘制圆canvas.drawCircle(0, -mHight / 7, mWith / 20, paint);//绘制嘴巴paint.setStyle(Paint.Style.STROKE);//移动起点mPathMouse.moveTo(mWith / 4, 0);mPathMouse.quadTo(0, mHight / 6, -mWith / 4, 0);canvas.drawPath(mPathMouse, paint);//绘制嘴巴两边的点canvas.drawCircle(mWith / 4, 0, 1, paint);canvas.drawCircle(-mWith / 4, 0, 1, paint);}
到这里自定义的部分就绘制完成啦。
这里就不搞动画绘制了,需要的童鞋自己弄哈,接下来就是使用了,这还是比较简单的,如下所示:
需要源码的童鞋【龙旋】公众号后台回复关键字:冰墩墩,注意这个读墩(dun),别回复错关键字哈。
到这里就搞定啦。
相关阅读
-
世界热推荐:今晚7:00直播丨下一个突破...
今晚19:00,Cocos视频号直播马上点击【预约】啦↓↓↓在运营了三年... -
NFT周刊|Magic Eden宣布支持Polygon网...
Block-986在NFT这样的市场,每周都会有相当多项目起起伏伏。在过去... -
环球今亮点!头条观察 | DeFi的兴衰与...
在比特币得到机构关注之后,许多财务专家预测世界将因为加密货币的... -
重新审视合作,体育Crypto的可靠关系才能双赢
Block-987即使在体育Crypto领域,人们的目光仍然集中在FTX上。随着... -
简讯:前端单元测试,更进一步
前端测试@2022如果从2014年Jest的第一个版本发布开始计算,前端开发... -
焦点热讯:刘强东这波操作秀
近日,刘强东发布京东全员信,信中提到:自2023年1月1日起,逐步为...