Android 酷炫的3d立体圆柱动画效果实现
Posted 化作孤岛的瓜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 酷炫的3d立体圆柱动画效果实现相关的知识,希望对你有一定的参考价值。
最近在drrible上看到一个超酷炫的效果,立体圆柱缓慢上升:https://dribbble.com/shots/7077455-Spending-analytics
然后准备实现一波,做之前在网上找了很久,并没有相似的效果,所以自己做了一个,已经上传到我的代码库里:
https://github.com/jiangzhengnan/NguiLib
欢迎小伙伴们的start或者requests
下面简单说一下实现过程:
1.首先要讲传入的数据数组进行排序,因为是2d平面模拟3d,所以弧形的绘制要由内到外才不会出现错位的情况:
//计算出绘制顺序
private void computationOrder()
ArrayList<Entry> tempOrderList = new ArrayList<>();
leftAngle = 0;
rightAngle = 0;
for (int i = 0; i < max; i++)
Collections.sort(mEntries, new MyCompare());
Entry tempEntry = mEntries.get(0);
float halfAngle = tempEntry.percent / 2;
/*
1.算法,先绘制最大的一块,居中
2.剩下的 优先记录左边和右边的坐标值
3.优先添加不会溢出的
*/
if (i == 0)
leftAngle = 270f - halfAngle;
rightAngle = 270f + halfAngle;
if (rightAngle >= 360)
rightAngle -= 360f;
tempEntry.tag = CENTER;
tempEntry.startAngle = leftAngle;
mEntries.remove(0);
else
//1.找到当前小的边
if (getDistanceToCenter(leftAngle, LEFT) > getDistanceToCenter(rightAngle, RIGHT))
//左边比右边大,取右边
tempEntry = getNextAngle(getDistanceToCenter(rightAngle, RIGHT));
tempEntry.tag = RIGHT;
rightAngle += tempEntry.percent;
if (rightAngle >= 360)
rightAngle -= 360f;
tempEntry.startAngle = rightAngle;
else
//右边比左边大,取左边
tempEntry = getNextAngle(getDistanceToCenter(leftAngle, LEFT));
tempEntry.tag = LEFT;
leftAngle -= tempEntry.percent;
tempEntry.startAngle = leftAngle;
tempOrderList.add(tempEntry);
mEntrySourceList = tempOrderList;
2.然后是绘制部分,首先根据排列的顺序进行绘制,依次绘制各个弧形段,然后画轮廓线:
private void drawCylinder(Canvas canvas, Entry tempEntry, int thickness,boolean ifChangeThick)
mainPaint.setStyle(Paint.Style.FILL);
//绘制各个弧度
int perThickness =ifChangeThick? (int) ((tempEntry.percent / 360f) * thickness * (max * 0.5f)) : thickness;
float drawTempStartAngle = 0f;
RectF tempRectF;
float lineStartX = 0f;
float lineStartY = 0f;
float lineEndX = 0f;
float lineEndY = 0f;
float oX = centerX;
float oY = (area2DHeight + area3DHight) / 2;
float R = centerX;
//y轴2d3d缩放比例
float bilv = ((float) (area3DHight - area2DHeight)) / ((float) area2DHeight);
for (int j = 0; j <= perThickness; j++)
tempRectF = new RectF(0, area2DHeight - j, area2DWidth, area3DHight - j);
switch (tempEntry.tag)
case CENTER:
drawTempStartAngle = tempEntry.startAngle;
lineStartX = oX;
lineEndX = oX;
lineStartY = (area2DHeight + area3DHight) / 2;
lineEndY = oY - j;
break;
case LEFT:
drawTempStartAngle = tempEntry.startAngle;
/*
左边夹角tempAngle 0< tempAngle < 180
90 <drawTempStartAngle <270
*/
if (drawTempStartAngle <= 180)
//startAngle 90-180
lineStartX = oX - (float) (R * Math.sin(Math.toRadians(drawTempStartAngle - 90f)));
lineEndX = lineStartX;
lineStartY = oY + (float) (bilv * R * Math.cos(Math.toRadians(drawTempStartAngle - 90f)));
lineEndY = lineStartY - j;
else
//startAngle 180-270
lineStartX = oX - (float) (R * Math.cos(Math.toRadians(drawTempStartAngle - 180f)));
lineEndX = lineStartX;
lineStartY = oY - (float) (bilv * R * Math.sin(Math.toRadians(drawTempStartAngle - 180f)));
lineEndY = lineStartY - j;
break;
case RIGHT:
drawTempStartAngle = tempEntry.startAngle - tempEntry.percent;
/*
右边夹角tempAngle 0< tempAngle < 180
90 <drawTempStartAngle <270
*/
if (drawTempStartAngle <= 360)
//startAngle 270-360
lineStartX = oX + (float) (R * Math.cos(Math.toRadians(360f - drawTempStartAngle - tempEntry.percent)));
lineEndX = lineStartX;
lineStartY = oY - (float) (bilv * R * Math.sin(Math.toRadians(360f - drawTempStartAngle - tempEntry.percent)));
lineEndY = lineStartY - j;
else
//startAngle 0-90
lineStartX = oX + (float) (R * Math.cos(Math.toRadians(drawTempStartAngle)));
lineEndX = lineStartX;
lineStartY = oY - (float) (bilv * R * Math.sin(Math.toRadians(drawTempStartAngle)));
lineEndY = lineStartY - j;
break;
//弧形
mainPaint.setColor(tempEntry.color);
canvas.drawArc(tempRectF, drawTempStartAngle, tempEntry.percent, true, mainPaint);
if (j == perThickness)
drawTopLine(canvas, tempRectF, drawTempStartAngle, tempEntry.percent);
//竖直线
mainPaint.setColor(Color.WHITE);
if (tempEntry.tag == CENTER)
canvas.drawLine(lineStartX, lineStartY,
lineEndX, lineEndY,
mainPaint);
//还需要画左右两边的竖直线
float xOffset = (float) (R * Math.sin(Math.toRadians(tempEntry.percent / 2)));
float yOffset = (float) (bilv * R * Math.cos(Math.toRadians(tempEntry.percent / 2)));
canvas.drawLine(
oX - xOffset,
oY - yOffset,
oX - xOffset,
oY - yOffset - j,
mainPaint);
canvas.drawLine(
oX + xOffset,
oY - yOffset,
oX + xOffset,
oY - yOffset - j,
mainPaint);
else
canvas.drawLine(lineStartX, lineStartY,
lineEndX, lineEndY,
mainPaint);
3.两种不同的动画效果在ondraw里面分别进行判断:
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas)
super.onDraw(canvas);
switch (ANIM_STATE)
case ANIM_STATE_ALL:
for (int i = 0; i < mEntrySourceList.size(); i++)
Entry tempEntry = mEntrySourceList.get(i);
drawCylinder(canvas, tempEntry, thickness,true);
break;
case ANIM_STATE_SINGLE:
for (int i = 0; i < mEntrySourceList.size(); i++)
Entry tempEntry = mEntrySourceList.get(i);
drawCylinder(canvas, tempEntry, 1,false);
for (int i = 0; i < singleAnimIndex; i++)
if (i < mEntrySourceList.size())
Entry tempEntry = mEntrySourceList.get(i);
int tempThickNess = singleAnimValue - (i + 1) * 100;
LogUtils.INSTANCE.d("tempThickNess: " + tempThickNess);
tempThickNess = (int) ((tempEntry.percent / 360f) * tempThickNess * (max * 0.33f));
drawCylinder(canvas, tempEntry, tempThickNess,false);
break;
case ANIM_STATE_CHANGGE:
break;
至此就介绍得差不多了,具体的代码里面都有注释,希望喜欢的小伙伴们点个赞,有问题可以留言。
以上是关于Android 酷炫的3d立体圆柱动画效果实现的主要内容,如果未能解决你的问题,请参考以下文章
小超_Androidandroid上开源的酷炫的交互动画和视觉效果:Interactive-animation