基于Camu的曝光多边形动画实现

原本项目中要实现camu的曝光多边形动画,做好后,产品后来决定不用了。所以源码我就贴出来了。
支持任意多边形

import android.content.Context;
import android.graphics.*;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import java.lang.ref.WeakReference;

public class PolygonImageView extends View {
    private static final String TAG = PolygonImageView.class.getSimpleName();
    // 当前UI的宽高
    private int mWidth;
    private int mHeight;
    // 画笔
    private Paint paint;
    // 绘制目标圆位图画笔
    private static int sPolygonColor = Color.BLUE;
    private WeakReference<Bitmap> mWeakSrcOriBitmap;
    private WeakReference<Bitmap> mWeakDstOriBitmap;
    // 多边形默认边宽度
    private static float sPolygonWidth = 0f;
    // 多边形边数
    private static int sPolygonNumber = -1;
    // 多边形内角角度
    private static float sPolygonInnerAngel = 180 - (360f / sPolygonNumber);
    // 多边形内部每一份三角弧形的角度 = 圆周角度 / 多边形边数
    private static float sPolygonInnerRadianAngel = 360f / sPolygonNumber;
    // 多边形内部每一份三角弧形的的间隔,默认2px
    private static int sPolygonInnerRadianGap = 2;
    private PorterDuffXfermode mPorterDuffXferMode;
    private static int sCanvasFlag = Canvas.MATRIX_SAVE_FLAG |
            Canvas.CLIP_SAVE_FLAG |
            Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
            Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
            Canvas.CLIP_TO_LAYER_SAVE_FLAG;

    public PolygonImageView(Context context) {
        super(context);
    }

    public PolygonImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 初始化多边形view的参数
     *
     * @param polygonNumber 多边形的边数
     * @param polygonWidth  多边形边宽
     * @param polygonColor  多边形颜色 eg:Color.RED
     * @param polygonGap    多边形内部每一份三角弧形的的间隔,默认2px
     */
    public void initPolygon(int polygonNumber, int polygonWidth, int polygonColor, int polygonGap) {
        if (polygonNumber < 3)
            throw new RuntimeException("this [polygonNumber < 3] can't compose to a polygon!");

        if (polygonWidth < 0)
            throw new RuntimeException("this polygonWidth can't be negative!");

        if (polygonGap < 0) {
            Log.i(TAG, "this sPolygonInnerRadianGap can't be negative, will use the default value of 2px");
            polygonGap = 2;
        }
        sPolygonNumber = polygonNumber;
        sPolygonWidth = polygonWidth;
        sPolygonColor = polygonColor;
        sPolygonInnerRadianGap = polygonGap;

        sPolygonInnerAngel = 180 - (360f / polygonNumber);
        sPolygonInnerRadianAngel = 360f / sPolygonNumber;
        initPaint();
        mPorterDuffXferMode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
    }

    private void initPaint() {
        if (paint == null) {
            paint = new Paint();
            paint.setAntiAlias(true);
            paint.setColor(Color.RED);
            paint.setStyle(Style.FILL);
            paint.setShader(null);
        }
    }

    /**
     * 调整多变形的宽度
     *
     * @param widthDelta 宽度的差值  增大为正数,减小为负数
     */
    public void adjustPolygonWidth(float widthDelta) {
        sPolygonWidth = sPolygonWidth + widthDelta;
        if (sPolygonWidth >= mWidth >> 1) {
            sPolygonWidth = mWidth >> 1;
        }
        if (sPolygonWidth <= 0) {
            sPolygonWidth = 0;
        }

        if (mWeakDstOriBitmap != null && mWeakSrcOriBitmap.get() != null) {
            mWeakDstOriBitmap.clear();
        }
        if (mWeakSrcOriBitmap != null && mWeakSrcOriBitmap.get() != null) {
            mWeakSrcOriBitmap.clear();
        }
        invalidate();
    }

    // create a bitmap with a circle, used for the "dst" image
    private Bitmap makeDst(int w, int h) {
        Bitmap bm;
        if (mWeakDstOriBitmap != null && mWeakDstOriBitmap.get() != null) {
            return  mWeakDstOriBitmap.get();
        } else {
            bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        }
        Canvas c = new Canvas(bm);
        Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        dstPaint.setColor(sPolygonColor);
        float half = w >> 1;
        Log.i(TAG, "makeDst half = " + half);
        c.drawCircle(half, half, half, dstPaint);

        //bitmap缓存起来,避免每次分配内存
        mWeakDstOriBitmap = null;
        mWeakDstOriBitmap = new WeakReference<Bitmap>(bm);
        return bm;
    }

    // create a bitmap with a rect, used for the "src" image
    private Bitmap makeSrc(int mWidth, int mHeight) {
        Log.i(TAG, "makeSrc sPolygonWidth = " + sPolygonWidth);
        Bitmap bm;
        // 从缓存中获取bitmap,避免每次创建
        if (mWeakSrcOriBitmap != null && mWeakSrcOriBitmap.get() != null) {
            return mWeakSrcOriBitmap.get();
        } else {
            bm = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
        }
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setColor(Color.RED);

        float halfWidth = mWidth >> 1;
        float halfHeight = mHeight >> 1;

        // 获取多边形内角三角形的右三角形的底边距离
        //         圆心
        //         /|\
        //        / | \
        //       /  |  \
        //      /   |   \
        //     /____|____\
        //          |-计算|
        //          |-右边|
        //          |-三角|
        //          |-形这|
        //          |-段的|
        //          |-距离|
        double polygonRadian = (180 - sPolygonInnerAngel) / 2 * Math.PI / 180;
        float b = (float) (halfWidth * Math.tan(polygonRadian));

        Path path = new Path();
        // 移动到多边形内三角形顶点
        path.moveTo(halfWidth - sPolygonWidth, halfHeight);
        // 连接多边形内三角形右下角点
        path.lineTo(halfWidth + b - sPolygonWidth, mHeight);
        // 连接多边形内三角形左下角点
        path.lineTo(halfWidth - b - sPolygonWidth, mHeight);
        // 回连到多边形内三角形顶点
        path.lineTo(halfWidth - sPolygonWidth, halfHeight);
        path.close();

        c.drawPath(path, p);

        //bitmap缓存起来,避免每次分配内存
        mWeakSrcOriBitmap = null;
        mWeakSrcOriBitmap = new WeakReference<Bitmap>(bm);
        return bm;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (sPolygonNumber == -1)
            throw new RuntimeException("you must call initPolygon() method first!");

        if (mWidth == 0 || mHeight == 0) {
            mWidth = getWidth();
            mHeight = getHeight();
        }
        Bitmap srcB = makeSrc(mWidth, mHeight);
        Bitmap dstB = makeDst(mWidth, mHeight);

        // 不能用int,因为有些多边形的角度带有小数点,用int会导致整体角度不准
        float degree = 0;
        for (int i = 0; i < sPolygonNumber; i++) {
            int sc = canvas.saveLayer(0, 0, mWidth, mHeight, null, sCanvasFlag);
            // 绘制圆
            canvas.drawBitmap(dstB, 0, 0, paint);
            // 旋转每个三角弧形
            canvas.rotate(degree, mWidth >> 1, mHeight >> 1);
            // 累加下一次旋转的角度
            degree = degree + sPolygonInnerRadianAngel;

            // 设置显示为取两个图形相交的部分
            Log.i(TAG, "degree = " + degree);
            paint.setXfermode(mPorterDuffXferMode);

            canvas.save();
            // 多边形每部分的间隙
            canvas.translate(0, sPolygonInnerRadianGap);
            // 绘制平移间隙后的三角形
            canvas.drawBitmap(srcB, 0, 0, paint);
            canvas.restore();

            paint.setXfermode(null);
            canvas.restoreToCount(sc);
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值