NativeScript / canvas

Apache License 2.0
87 stars 18 forks source link

Canvas will not render on Android when app is coming from background #67

Closed richardwalander closed 2 years ago

richardwalander commented 2 years ago

Im trying to create a new iOS type "squircle"-shaped buttons in one project and came up with the idea to draw the the curves using some kind of custom paint and found this plugin. I managed to create a custom layout using a flexbox as base and then the canvas for the left/right side of the button. I also use the canvas to draw the icon based on the SVG-path since I would like to change color programmatically. Here is a version of my template:

<StackLayout padding="100 50">
  <FlexboxLayout>
    <Canvas ref="left" width="22" height="44" @ready="renderLeft"></Canvas>
    <FlexboxLayout
      ref="center"
      alignSelf="stretch"
      flexGrow="2"
      alignItems="center"
      justifyContent="center"
      @loaded="renderCenter"
    >
      <Canvas
        id="icon"
        width="24"
        height="24"
        marginRight="8"
        @ready="renderIcon"
      ></Canvas>
      <Label text="Label" textAlignment="center" color="#ffffff"></Label>
    </FlexboxLayout>
    <Canvas id="right" width="22" height="44" @ready="renderRight"></Canvas>
  </FlexboxLayout>
</StackLayout>

Everything is working fine on iOS. The button render like it's supposed to:

image

On my real Android device the button render the first time you launch the app, but if you suspend it and then open it from running in the background the canvas will not render. It works on my Android emulator it renders but with a small delay but on my real device it will not render at all.

On first launch of app:

image

When coming back from background:

image

I have tried to generate both a angular and a vue project and have got the same result on both.

Here are my dependencies in the vue project:

"dependencies": {
    "@nativescript/canvas": "1.0.0-alpha.2",
    "@nativescript/core": "~8.0.0",
    "@nativescript/theme": "~3.0.1",
    "nativescript-vue": "~2.9.0"
  },
  "devDependencies": {
    "@nativescript/android": "8.0.0",
    "@nativescript/ios": "8.0.0",
    "@nativescript/webpack": "beta",
    "nativescript-vue-template-compiler": "~2.9.0",
    "sass": "^1.32.8"
  },

Here are my dependencies in the angular project:

"dependencies": {
    "@angular/animations": "~12.0.0",
    "@angular/common": "~12.0.0",
    "@angular/compiler": "~12.0.0",
    "@angular/core": "~12.0.0",
    "@angular/forms": "~12.0.0",
    "@angular/platform-browser": "~12.0.0",
    "@angular/platform-browser-dynamic": "~12.0.0",
    "@angular/router": "~12.0.0",
    "@nativescript/angular": "~12.0.0",
    "@nativescript/canvas": "^1.0.0-alpha.2",
    "@nativescript/core": "~8.0.0",
    "@nativescript/theme": "~3.0.1",
    "rxjs": "^6.6.0",
    "zone.js": "~0.11.4"
  },
  "devDependencies": {
    "@angular/compiler-cli": "~12.0.0",
    "@nativescript/android": "8.0.0",
    "@nativescript/ios": "8.0.0",
    "@nativescript/types": "~8.0.0",
    "@nativescript/webpack": "beta",
    "@ngtools/webpack": "~12.0.0",
    "typescript": "~4.2.0"
  },

I don't know why it stops working when coming from the background. It feels like the UI re-renders but that the "ready" event stops firing or something. It's the same behaviour on both angular and vue. It's only on real device also a OnePlus Nord2 5G running Android 11.

triniwiz commented 2 years ago

You need to use @nativescript/core@8.1 for resuming to work

richardwalander commented 2 years ago

@triniwiz cheers! I will try!

richardwalander commented 2 years ago

I can confirm that updating to the latest version of Nativescript made resuming work.

richardwalander commented 2 years ago

Even if it works from time to time, it still will not render every time i build. Some times when I build and launch the test app it will render as expected. But often if I have more than one canvas in the same layout not all of them will render.

Here I'm trying to implement button with super ellipse sides, super ellipse card and icons using 2DPath.

Screenshot_2021-11-18-22-48-37-61_3dd509e96ebae311c1293d52c8ef9f07.jpg

But often the result looks like this when launching:

Screenshot_2021-11-18-22-49-58-02_3dd509e96ebae311c1293d52c8ef9f07.jpg

Random canvases on the screen will not render.

Now I'm running the latest version of core. It works fine on iOS, but I really need this to work on Android as well since our design system heavily rely on super ellipse shapes and this is the only way I have found to draw custom shapes on screen. Is there a problem of using more than one canvas on screen at the same time?

triniwiz commented 2 years ago

Using multiple canvas is support, when developing you should use --no-hmr since hmr can cause some of the issues you're describing.

Another thing you can do is create an offscreen canvas like you would on the web by creating the canvas element then rendering into it, the only thing it's a bit different w/o using the polyfill you will need to use Canvas.createCustomView() this will create a 300x150 sized canvas then you can call .getContext("2d"). After you can do the drawing as you would and finally call that toDataURL grab the base64 image that you can then render or pass the said offscreen canvas to an onscreen canvas

CatchABus commented 2 years ago

@triniwiz I also face such issues in android. Even with --no-hmr declared, my canvas is not drawn at times and it's a very often and random behaviour on my side.

After meddling a little, I noticed that problem is gone if Canvas uses CPU to render instead. I manually set useCpu argument here to true: https://github.com/NativeScript/canvas/blob/master/packages/canvas/Canvas/index.android.ts#L30 and the problem is gone.

I believe there is something wrong with GLView surface and the issue probably lies inside its GLThread which is initialized here: https://github.com/NativeScript/canvas/blob/master/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/GLView.kt#L75