小言_互联网的博客

Android 酷炫的3d立体圆柱动画效果实现

264人阅读  评论(0)

最近在drrible上看到一个超酷炫的效果,立体圆柱缓慢上升:https://dribbble.com/shots/7077455-Spending-analytics

然后准备实现一波,做之前在网上找了很久,并没有相似的效果,所以自己做了一个,已经上传到我的代码库里:

https://github.com/jiangzhengnan/NguiLib

欢迎小伙伴们的start或者requests

下面简单说一下实现过程:

1.首先要讲传入的数据数组进行排序,因为是2d平面模拟3d,所以弧形的绘制要由内到外才不会出现错位的情况:

 


  
  1. //计算出绘制顺序
  2. private void computationOrder() {
  3. ArrayList<Entry> tempOrderList = new ArrayList<>();
  4. leftAngle = 0;
  5. rightAngle = 0;
  6. for ( int i = 0; i < max; i++) {
  7. Collections.sort(mEntries, new MyCompare());
  8. Entry tempEntry = mEntries.get( 0);
  9. float halfAngle = tempEntry.percent / 2;
  10. /*
  11. 1.算法,先绘制最大的一块,居中
  12. 2.剩下的 优先记录左边和右边的坐标值
  13. 3.优先添加不会溢出的
  14. */
  15. if (i == 0) {
  16. leftAngle = 270f - halfAngle;
  17. rightAngle = 270f + halfAngle;
  18. if (rightAngle >= 360) {
  19. rightAngle -= 360f;
  20. }
  21. tempEntry.tag = CENTER;
  22. tempEntry.startAngle = leftAngle;
  23. mEntries.remove( 0);
  24. } else {
  25. //1.找到当前小的边
  26. if (getDistanceToCenter(leftAngle, LEFT) > getDistanceToCenter(rightAngle, RIGHT)) {
  27. //左边比右边大,取右边
  28. tempEntry = getNextAngle(getDistanceToCenter(rightAngle, RIGHT));
  29. tempEntry.tag = RIGHT;
  30. rightAngle += tempEntry.percent;
  31. if (rightAngle >= 360) {
  32. rightAngle -= 360f;
  33. }
  34. tempEntry.startAngle = rightAngle;
  35. } else {
  36. //右边比左边大,取左边
  37. tempEntry = getNextAngle(getDistanceToCenter(leftAngle, LEFT));
  38. tempEntry.tag = LEFT;
  39. leftAngle -= tempEntry.percent;
  40. tempEntry.startAngle = leftAngle;
  41. }
  42. }
  43. tempOrderList.add(tempEntry);
  44. }
  45. mEntrySourceList = tempOrderList;
  46. }

2.然后是绘制部分,首先根据排列的顺序进行绘制,依次绘制各个弧形段,然后画轮廓线:

 


  
  1. private void drawCylinder(Canvas canvas, Entry tempEntry, int thickness,boolean ifChangeThick) {
  2. mainPaint.setStyle(Paint.Style.FILL);
  3. //绘制各个弧度
  4. int perThickness =ifChangeThick? ( int) ((tempEntry.percent / 360f) * thickness * (max * 0.5f)) : thickness;
  5. float drawTempStartAngle = 0f;
  6. RectF tempRectF;
  7. float lineStartX = 0f;
  8. float lineStartY = 0f;
  9. float lineEndX = 0f;
  10. float lineEndY = 0f;
  11. float oX = centerX;
  12. float oY = (area2DHeight + area3DHight) / 2;
  13. float R = centerX;
  14. //y轴2d3d缩放比例
  15. float bilv = (( float) (area3DHight - area2DHeight)) / (( float) area2DHeight);
  16. for ( int j = 0; j <= perThickness; j++) {
  17. tempRectF = new RectF( 0, area2DHeight - j, area2DWidth, area3DHight - j);
  18. switch (tempEntry.tag) {
  19. case CENTER:
  20. drawTempStartAngle = tempEntry.startAngle;
  21. lineStartX = oX;
  22. lineEndX = oX;
  23. lineStartY = (area2DHeight + area3DHight) / 2;
  24. lineEndY = oY - j;
  25. break;
  26. case LEFT:
  27. drawTempStartAngle = tempEntry.startAngle;
  28. /*
  29. 左边夹角tempAngle 0< tempAngle < 180
  30. 90 <drawTempStartAngle <270
  31. */
  32. if (drawTempStartAngle <= 180) {
  33. //startAngle 90-180
  34. lineStartX = oX - ( float) (R * Math.sin(Math.toRadians(drawTempStartAngle - 90f)));
  35. lineEndX = lineStartX;
  36. lineStartY = oY + ( float) (bilv * R * Math.cos(Math.toRadians(drawTempStartAngle - 90f)));
  37. lineEndY = lineStartY - j;
  38. } else {
  39. //startAngle 180-270
  40. lineStartX = oX - ( float) (R * Math.cos(Math.toRadians(drawTempStartAngle - 180f)));
  41. lineEndX = lineStartX;
  42. lineStartY = oY - ( float) (bilv * R * Math.sin(Math.toRadians(drawTempStartAngle - 180f)));
  43. lineEndY = lineStartY - j;
  44. }
  45. break;
  46. case RIGHT:
  47. drawTempStartAngle = tempEntry.startAngle - tempEntry.percent;
  48. /*
  49. 右边夹角tempAngle 0< tempAngle < 180
  50. 90 <drawTempStartAngle <270
  51. */
  52. if (drawTempStartAngle <= 360) {
  53. //startAngle 270-360
  54. lineStartX = oX + ( float) (R * Math.cos(Math.toRadians( 360f - drawTempStartAngle - tempEntry.percent)));
  55. lineEndX = lineStartX;
  56. lineStartY = oY - ( float) (bilv * R * Math.sin(Math.toRadians( 360f - drawTempStartAngle - tempEntry.percent)));
  57. lineEndY = lineStartY - j;
  58. } else {
  59. //startAngle 0-90
  60. lineStartX = oX + ( float) (R * Math.cos(Math.toRadians(drawTempStartAngle)));
  61. lineEndX = lineStartX;
  62. lineStartY = oY - ( float) (bilv * R * Math.sin(Math.toRadians(drawTempStartAngle)));
  63. lineEndY = lineStartY - j;
  64. }
  65. break;
  66. }
  67. //弧形
  68. mainPaint.setColor(tempEntry.color);
  69. canvas.drawArc(tempRectF, drawTempStartAngle, tempEntry.percent, true, mainPaint);
  70. if (j == perThickness) {
  71. drawTopLine(canvas, tempRectF, drawTempStartAngle, tempEntry.percent);
  72. }
  73. //竖直线
  74. mainPaint.setColor(Color.WHITE);
  75. if (tempEntry.tag == CENTER) {
  76. canvas.drawLine(lineStartX, lineStartY,
  77. lineEndX, lineEndY,
  78. mainPaint);
  79. //还需要画左右两边的竖直线
  80. float xOffset = ( float) (R * Math.sin(Math.toRadians(tempEntry.percent / 2)));
  81. float yOffset = ( float) (bilv * R * Math.cos(Math.toRadians(tempEntry.percent / 2)));
  82. canvas.drawLine(
  83. oX - xOffset,
  84. oY - yOffset,
  85. oX - xOffset,
  86. oY - yOffset - j,
  87. mainPaint);
  88. canvas.drawLine(
  89. oX + xOffset,
  90. oY - yOffset,
  91. oX + xOffset,
  92. oY - yOffset - j,
  93. mainPaint);
  94. } else {
  95. canvas.drawLine(lineStartX, lineStartY,
  96. lineEndX, lineEndY,
  97. mainPaint);
  98. }
  99. }
  100. }

3.两种不同的动画效果在ondraw里面分别进行判断:

 


  
  1. @SuppressLint( "DrawAllocation")
  2. @Override
  3. protected void onDraw(Canvas canvas) {
  4. super.onDraw(canvas);
  5. switch (ANIM_STATE) {
  6. case ANIM_STATE_ALL:
  7. for ( int i = 0; i < mEntrySourceList.size(); i++) {
  8. Entry tempEntry = mEntrySourceList.get(i);
  9. drawCylinder(canvas, tempEntry, thickness, true);
  10. }
  11. break;
  12. case ANIM_STATE_SINGLE:
  13. for ( int i = 0; i < mEntrySourceList.size(); i++) {
  14. Entry tempEntry = mEntrySourceList.get(i);
  15. drawCylinder(canvas, tempEntry, 1, false);
  16. }
  17. for ( int i = 0; i < singleAnimIndex; i++) {
  18. if (i < mEntrySourceList.size()) {
  19. Entry tempEntry = mEntrySourceList.get(i);
  20. int tempThickNess = singleAnimValue - (i + 1) * 100;
  21. LogUtils.INSTANCE.d( "tempThickNess: " + tempThickNess);
  22. tempThickNess = ( int) ((tempEntry.percent / 360f) * tempThickNess * (max * 0.33f));
  23. drawCylinder(canvas, tempEntry, tempThickNess, false);
  24. }
  25. }
  26. break;
  27. case ANIM_STATE_CHANGGE:
  28. break;
  29. }
  30. }

至此就介绍得差不多了,具体的代码里面都有注释,希望喜欢的小伙伴们点个赞,有问题可以留言。


转载:https://blog.csdn.net/qq_22770457/article/details/103655337
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场