HTML canvas on mobile (react-native-canvas) flickering

561 Views Asked by At

I am trying to make a somewhat simple animation work on react-native. I'm using react-native-canvas and it seems to implement the normal html canvas well enough but I get some awful flickering on a real device. I get almost no flickering on iOS emulator but the Android one is bad.

My current code already implements the following suggestions I found:

  • Use requestAnimationFrame
  • Double-buffering
  • Framerate handling
  • Avoid using states for variables (as they re-trigger renders)

My goal is to get an animation that doesn't flicker. I don't mind if on a slower device it plays a bit slower but flickering is not a good feeling for the user.

What am I doing incorrectly?

Here is the code:

initTempCanvas = (canvas) => {
    if (canvas !== null) {
        canvas.width = this.window_size.height;
        canvas.height = this.window_size.width + 50;
        this.tempCanvas = canvas;
        this.tempCtx = canvas.getContext('2d');
        canvas.hidden = true;
    }
}

initCanvas = (canvas) => {
    if (canvas !== null) {
        var fps = 25;
        this.interval = 1000 / fps;
        this.speed = 30;
        // These are switched because of landscape mode
        canvas.width = this.window_size.height;
        canvas.height = this.window_size.width + 50;
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');

        requestAnimationFrame(this.drawLoop);
    }
}

drawLoop = (now) => {
    if (this.tempCtx !== undefined) {
        if (!this.then) {
            this.then = now;
        }
        this.delta = now - this.then;

        if (this.delta > this.interval) {
            this.ctx.save();
            this.tempCtx.clearRect(0, 0, this.tempCanvas.width, this.tempCanvas.height)

            this.drawCircle(this.tempCtx, (this.canvasTimer * 100), this.tempCanvas.height / 2 + (-125 * Math.sin(this.canvasTimer * (Math.PI / 2) - Math.PI * 0.5)), 30, 'purple', 'purple', 2);

            this.ctx.drawImage(this.tempCanvas, 0, 0);
            this.ctx.restore();
            this.deltaTime = this.delta / 100000 * this.speed;
            this.canvasTimer += this.deltaTime;
        }
        this.then = now - (this.delta % this.interval);
    }
    requestAnimationFrame(this.drawLoop);
}

drawCircle = (ctx, x, y, radius, fill, stroke, strokeWidth) => {
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
    if (fill) {
        ctx.fillStyle = fill;
        ctx.fill();
    }
    if (stroke) {
        ctx.lineWidth = strokeWidth;
        ctx.strokeStyle = stroke;
        ctx.stroke();
    }
}

render() {
    return (
        <View>
            {this.tempCanvas == undefined && <Canvas ref={this.initCanvas} />}
            <Canvas style={"display: none"} ref={this.initTempCanvas} />
        </View>
    )
}

0

There are 0 best solutions below