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;
        }
    }

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

发布了70 篇原创文章 · 获赞 75 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/qq_22770457/article/details/103655337