How to overlap two path figures on Canvas?

37 Views Asked by At

I'm making an animation of three rays coming out of the left top corner of the screen. They should spread out and then erase the whole "upper" screen. I've made a big progress in that, but two of these rays overlap strangely like they are pushing each other, so there remains a single line which I don't need. Can someone help me out and tell me what am I doing wrong?

CustomView code:

class CustomView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
) : View(context, attrs, defStyleAttr) {

companion object {
    const val ADDITIONAL_NUM_VALUE_HOLDER = "additional_num"
    const val TEXT_TOP_PADDING_VALUE_HOLDER = "text_top_padding"
}

private var rayPaint: Paint
private var textPaint: TextPaint
private var rayPath: Path = Path()

private var additionalNum = 1f
private var shouldAnimateRays = false

private var textX = 20f
private var textY = 0f

init {
    rayPaint = Paint().apply {
        style = Paint.Style.FILL
        strokeWidth = 4f
        color = -0x1
        xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
        isAntiAlias = true
    }

    textPaint = TextPaint().apply {
        style = Paint.Style.FILL
        color = Color.BLUE
        textSize = 60f
        isAntiAlias = true
    }
}

override fun dispatchDraw(canvas: Canvas) {
    val bitmap = Bitmap.createBitmap(canvas.width, canvas.height, Bitmap.Config.ARGB_8888)
    bitmap.eraseColor(Color.WHITE)

    val additionalCanvas = Canvas(bitmap)
    additionalCanvas.drawColor(Color.GREEN)

    val someText = "Hello world"

    val textLayout = StaticLayout.Builder
        .obtain(someText, 0, someText.length, textPaint, additionalCanvas.width)
        .setAlignment(Layout.Alignment.ALIGN_NORMAL)
        .setLineSpacing(0.0f, 1.0f)
        .setIncludePad(true)
        .build()

    additionalCanvas.save()

    additionalCanvas.translate(textX, textY)
    textLayout.draw(additionalCanvas)

    additionalCanvas.restore()

    if (shouldAnimateRays) {
        val x = canvas.width.toFloat()
        val y = canvas.height.toFloat()

        // first ray
        rayPath.moveTo(0f, 0f)
        rayPath.lineTo(x, (y * 0.5f) - additionalNum)
        rayPath.lineTo(x, (y * 0.5f) + additionalNum)
        rayPath.lineTo(0f, 0f)

        // second ray
        rayPath.moveTo(0f, 0f)
        rayPath.lineTo(x, (y * 0.7f) - additionalNum)
        rayPath.lineTo(x, (y * 0.7f) + additionalNum)
        rayPath.lineTo(0f, 0f)

        // third ray
        rayPath.moveTo(0f, 0f)
        rayPath.lineTo((x * 0.7f) - additionalNum, y)
        rayPath.lineTo((x * 0.7f) + additionalNum, y)
        rayPath.lineTo(0f, 0f)

        additionalCanvas.drawPath(rayPath, rayPaint)
    }

    canvas.drawBitmap(bitmap, 0f, 0f, null)

    super.dispatchDraw(canvas)
}

fun animateShapes() {
    val textValuesHolder = PropertyValuesHolder.ofFloat(TEXT_TOP_PADDING_VALUE_HOLDER, -100f, 20f)
    val valuesHolder = PropertyValuesHolder.ofFloat(ADDITIONAL_NUM_VALUE_HOLDER, 1f, 800f)

    val animator = ValueAnimator().apply {
        setValues(valuesHolder)
        duration = 2_000
        interpolator = AccelerateDecelerateInterpolator()

        addUpdateListener {
            val addNum = it.getAnimatedValue(ADDITIONAL_NUM_VALUE_HOLDER) as Float
            additionalNum = addNum

            invalidate()
        }
    }

    val textAnimator = ValueAnimator().apply {
        setValues(textValuesHolder)
        duration = 2_000
        interpolator = AccelerateDecelerateInterpolator()

        addUpdateListener {
            val paddingTop = it.getAnimatedValue(TEXT_TOP_PADDING_VALUE_HOLDER) as Float
            textY = paddingTop

            invalidate()
        }

        doOnEnd {
            shouldAnimateRays = true
            animator.start()
        }
    }

    textAnimator.start()
    }
}

Acitivity code in order ro recreate this problem:

@AndroidEntryPoint
class CustomActivity : AppCompatActivity() {

    private lateinit var binding: ActivityCustomBinding

    protected var rootView: ViewGroup? = null
        private set

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityCustomBinding.inflate(layoutInflater)
        setContentView(binding.root)
        rootView = findViewById(android.R.id.content)

        binding.customView.animateShapes()
    }
}

Acitivity layout:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    android:background="@color/colorWhite">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"

        android:scaleX="0.8"
        android:scaleY="0.8"
        android:src="@drawable/common_google_signin_btn_icon_dark_normal"

        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <com.app.android.view.CustomView
        android:id="@+id/customView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"

        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Result of my code for now

EDITED: link with the current result

0

There are 0 best solutions below