6

I'm trying to implement a circular reveal animation which consist to reveal the view according the user finger when he's touching the view.

First, my view is inside an infinite circular reveal loop to keep the rounded aspect as long as the user do nothing. Then when the user touch the view and start to swipe I calculated a factor inside the ACTION_MOVE to increase the radius in real time and call the circular reveal at each pass.

public void reveal(final View view,
                               final float centerX,
                               final float centerY,
                               final float startRadius,
                               final float endRadius,
                               final int duration) {

                anim = ViewAnimationUtils.createCircularReveal(view, (int)centerX, (int)centerY, (int)startRadius, (int)endRadius);
                anim.setDuration(duration);
                anim.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animation) {
                    }
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        if (isRepeating) {
                            reveal(view,centerX,centerY,startRadius,endRadius,duration);
                        }
                    }
                    @Override
                    public void onAnimationCancel(Animator animation) {
                    }
                    @Override
                    public void onAnimationRepeat(Animator animation) {
                    }
                });
                anim.start();

    }

I recall the circular reveal method again inside its Listener to create the loop and the most important to avoid visual distortion.

I have noticed that if I didn't do this way some very fast distortion showed up because the animation duration is faster than each pass in ACTION_MOVE so the anim have time to finish before the next pass. The distortion is the representation of the missing part of the view when the animation is finished and came back very fast to the current radius set with the Touch like a flash.

So this way with the method recall inside the Listener is working great until there.

Problem : On one of my devices there are senseless distortion in all directions while swiping.

enter image description here

According to my logs it's not a radius distortion because the start and end radius is always the same after each pass so the radius should not be moving like this especially when the finger is stopped, and the animation never stop thanks to the Listener like I explained above.

What am I doing wrong ?

I searched a lot and didn't find the same problem so I tried other ways like drawing canvas but unfortunately it doesn't match with my needs.

Is there another way to achieve this kind of animation even completely differently ?

Any lead is appreciate thanks in advance

EDIT : inside the ACTION_MOVE

 scaleRadius = (float) (2 * distance / maxWidth + startScale);

 if (scaleRadius < 1) {
       scaleRadius = 1;
 }

 animator.reveal(this, middleCircleX, middleCircleY, 
initialCircleRadius*scaleRadius, initialCircleRadius*scaleRadius, 0);

Logs : Always the same factor then the radius should not move because my finger is stopped

2020-03-27 15:42:56.113 onTouch: scaleRadius 1.7870371
2020-03-27 15:42:56.113 onTouch: scaleRadius 1.7870373
2020-03-27 15:42:56.227 onTouch: scaleRadius 1.7870371
2020-03-27 15:42:56.227 onTouch: scaleRadius 1.7870373
2020-03-27 15:42:56.331 onTouch: scaleRadius 1.7870374
2020-03-27 15:42:56.331 onTouch: scaleRadius 1.7870371
nicover
  • 2,213
  • 10
  • 24
  • sounds interesting, can you reveal a bit more of your code, to see the input you feed your method and where you register the swipe action. a gif or video of how it should look would also help understanding better and thinking about alternatives. – quealegriamasalegre Mar 24 '20 at 23:19
  • is it possible the reveal method gets executed with old values while the anim is running and then gets executed again on animation end?? have you tryed syncronizing the method on each view? just guessing for now – quealegriamasalegre Mar 24 '20 at 23:25
  • I don't think the values are the problem because I set inside startRadius and endRadius the same value so the circle can't be distorted because of value. If what I'm telling you was wrong, math will cause a different distortion that I get – nicover Mar 25 '20 at 00:08
  • Im thinking about an alternative because I can't fix it for now and I can't modify google's animation. The weird side is that it's only on 1/10 of my devices. I will try to provide video when I will know how to share it – nicover Mar 25 '20 at 00:10
  • Ithink its easiest if you just make a gif out of it, then you can upload it as if it was an image – quealegriamasalegre Mar 25 '20 at 00:14
  • how is the reveal loop exited btw? or is there something I am not seeing – quealegriamasalegre Mar 25 '20 at 00:16
  • you're right there is no exit here I voluntarily removed the condition, I edit it – nicover Mar 25 '20 at 00:19
  • so the gif is from where it is working correctly of where there is a bug? I mean from what I see the animation is working correctly it just is playing over and over in close repetitions and maybe even overlapping cause you are resetting the anim variable everytime you reenter the loop + evrytime you move your finger ever so slightly – quealegriamasalegre Mar 25 '20 at 01:08

1 Answers1

1

I am assuming that you dont want the flickering animation but rather that the radius of your circe transitions smoothly and becomes either bigger or smaller as a function of how far you swipe on the screen. (please correct me if this assumption is wrong)

I will assume that centerX and centerY are always the same value and are basically global variables.

Create a couple of additional global variables:

boolean animIsCurrentlyPlaying=false; 
float mCurrentRadius=initialCircleSize; //i guess this is 1 in your case
float mTargetRadius; 

Now on the ACTION_MOVE event you will do the following:

1- set mTargetRadius to be some function of the distance between centerX and centerY and the position of the finger on the screen (euclidean distance might be a good pick here is an example);

mTargetRadius=Float.valueOf(Math.sqrt((double)(Math.pow(event.getX()-centerX,2)+Math.pow(event.getY()-centerY,2))));

2- excecute the following method revealModified(view, 100);

public void revealModified(final View view,
                           final int duration) {
       if(!animIsCurrentlyPlaying){
            //here i just reference the global vars
            anim = ViewAnimationUtils.createCircularReveal(view, centerX, centerY, mCurrentRadius, mTargetRadius);
            mCurrentRadius=mTargetRadius;
            anim.setDuration(duration);
            anim.addListener(new Animator.AnimatorListener() {

                @Override
                public void onAnimationEnd(Animator animation) {

                    animIsCurrentlyPlaying=false;
                    if(mCurrentRadius!=mTargetRadius){
                         revealModified(view, 100);
                    }
                }

            });
            animIsCurrentlyPlaying=true;
            anim.start();
       }

}

the method will be excecuted every time you move your finger ever so slightly. I set the duration to 100 ms but that can be shorter if you find its not reactive enough. anytime you move your finger a flag is set so no other animations can be started on top and the transition is always smooth.if you move your finger while an anim is playing then revealModified will be reexecured in the if statement within onAnimationEnd.

let me know if it works and especially if this is what you wanted

quealegriamasalegre
  • 2,887
  • 1
  • 13
  • 35
  • your conception is working I tested it on my devices, but I still have distortion on this one – nicover Mar 25 '20 at 11:31
  • hmmm, have tried adding some logs. what inputs are you getting from the screen motionEvent.getX() for instance. – quealegriamasalegre Mar 25 '20 at 15:38
  • . what device is that btw. also put some logs in the revealmodified to see the values of the radius inputs of the function – quealegriamasalegre Mar 25 '20 at 15:45
  • it seems to me like it could be weird inputs coming from that specific device. did anything change at all in the flickering after you used my code, is it worse or just different? – quealegriamasalegre Mar 26 '20 at 18:37
  • did'nt change anything, I'm looking for an alternative – nicover Mar 26 '20 at 18:49
  • If you are willing to give it one more go could you share the part of your code where you evaluate ACTION_MOVE and maybe log the values of mTargetRadius and mCurrentRadius of a swiping action. I mean its weird that it does not work on that one device out of 10. maybe it would be worthwhile emulating a similar device with the same api. maybe it its even a fault of that singular device hardwarewise – quealegriamasalegre Mar 26 '20 at 22:12
  • ok I had to changed my code configuration to make it work correctly and it's good for me thank you – nicover Mar 27 '20 at 15:12
  • happy to have helped – quealegriamasalegre Mar 27 '20 at 15:55