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立体圆柱动画效果实现的主要内容,如果未能解决你的问题,请参考以下文章

CSS3进阶酷炫的3D旋转透视

平常收藏的酷炫的Android开源特效库

巧用 CSS 实现酷炫的充电动画

小超_Androidandroid上开源的酷炫的交互动画和视觉效果:Interactive-animation

案例如何用html5 制作酷炫的Canvas粒子图形变形动画

仿智能社官网:原生JS实现简单又酷炫的3D立方体时钟