Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to wait when image load is completed #15815

Closed
Lupusa87 opened this issue Jul 11, 2018 · 10 comments
Closed

How to wait when image load is completed #15815

Lupusa87 opened this issue Jul 11, 2018 · 10 comments
Labels

Comments

@Lupusa87
Copy link
Contributor

@Lupusa87 Lupusa87 commented Jul 11, 2018

I need to load image in canvas, than to do another drawings.
When I call js function from blazor it is not waiting image load and continues execution, in result my logic is mismached.

Inside javascript when we set src property js continues execution and fires onload when image is completely loaded.

    var img = new Image();
    img.onload = function () {
    };
    img.src = imgPath;

It looks like js itself is starting async task and calls callback when it's done.

Actually I need exactly to know (in c# code) when image is loaded, to do next steps.
So I need force c# or js to wait till img.onload fires.

Can't find correct way for this, tried several approaches (including js interop async calls) no one was successful.

One solution (with I didn't tried) can be to call .net method from img.onload as callback, but to be honest I don't need to have callback hell in my blazor code.

Can js interop solve this case already?
If not is it possible to implement this functional?

Maybe I am doing or saying something wrong, or this is not correct place for asking.
In this case I am sorry and we can close issue.

@Lupusa87
Copy link
Contributor Author

@Lupusa87 Lupusa87 commented Jul 12, 2018

As writen in blazor js interop documentation

section - Invoke .NET methods from JavaScript functions

"JavaScript code in the browser might be required to call .NET methods. For example, a .NET method call can be made when a callback in JavaScript is triggered."

So it is intended to be used for my case.

With this I can give control back to c# from js, it is solution for sure but bit ugly one.

Hope we will have better options in the future.

@SteveSandersonMS
Copy link
Member

@SteveSandersonMS SteveSandersonMS commented Jul 12, 2018

The cleanest way would be to have your JS function return a Promise that completes when the image has finished loading. Then you can invoke it using RegisteredFunction.InvokeAsync and await its completion in your .NET code.

@Lupusa87
Copy link
Contributor Author

@Lupusa87 Lupusa87 commented Jul 12, 2018

Thank you for reply.
I tried it but is not working.
Will provide more details when will be at computer.
We need one working example or proof that it is not working.

@Lupusa87
Copy link
Contributor Author

@Lupusa87 Lupusa87 commented Jul 12, 2018

Here is video regarding this issue.
Advised promise await invokeasync is not working.
Js to call back .net method is working but not good for further use.
Please correct if I have mistake in syntax or mark it as bug.
https://www.youtube.com/watch?v=jFUpuzHVHAQ

@Lupusa87
Copy link
Contributor Author

@Lupusa87 Lupusa87 commented Jul 12, 2018

And sorry but closing this issue was bit premature.

@danroth27 danroth27 reopened this Jul 12, 2018
@SteveSandersonMS
Copy link
Member

@SteveSandersonMS SteveSandersonMS commented Jul 12, 2018

Could you post a minimal code sample rather than a video please? Thanks!

@Lupusa87
Copy link
Contributor Author

@Lupusa87 Lupusa87 commented Jul 12, 2018

Index.cshtml

@page "/"
<button class="btn btn-primary" onclick="@abc">Click me</button>
<canvas id="canvas1" width="500" height="500"></canvas>

@functions
{
    void abc()
    {
        //1 version - not working!!!!!!!!!!!!!!!!!!!!
        JsInterop.AsyncTest_Origin();
        JsInterop.Draw_Rect();

        //1.1 version - browser hangs up!!!!!!!!!!!!!!!
        Task<string> task1 = JsInterop.AsyncTest_Origin();

        task1.Wait();
        JsInterop.Draw_Rect();

        //   2 version - working but ugly one!!!!!!!!!!!
        //JsInterop.SyncTest_Callback();
    }

   protected override void OnAfterRender()
    {
        ClockManager.JS_Pong = HandleJSPong;
    }

    public void HandleJSPong()
    {
        JsInterop.Draw_Rect();
    }

}

JsInterop.js

Blazor.registerFunction("BlazorLib1.JsInterop.AsyncTest_Origin", function () {
    return new Promise(resolve => {
        var img = new Image();
        img.onload = () => {
            var ctx = document.getElementById("canvas1").getContext("2d");
            ctx.drawImage(img, 0, 0);
            resolve("promise resolved, loading done");
        };
        img.src = "/content/1.png";
    });
});

Blazor.registerFunction("BlazorLib1.JsInterop.SyncTest_Callback", function () {
        var img = new Image();
        img.onload = () => {
            var ctx = document.getElementById("canvas1").getContext("2d");
            ctx.drawImage(img, 0, 0);
            Blazor.invokeDotNetMethod({
                type: {
                    assembly: 'WebAppTestImageLoad',
                    name: 'WebAppTestImageLoad.ClockManager'
                },
                method: {
                    name: 'JsCallback'
                }
            });
        };
        img.src = "/content/1.png";
});

Blazor.registerFunction('BlazorLib1.JsInterop.DrawRect', (message) => {
    var ctx = document.getElementById("canvas1").getContext("2d");
    ctx.beginPath();
    ctx.fillStyle = "red";
    ctx.fillRect(50, 50, 150, 150);
    ctx.fill();
    return true;
});

JsInterop.cs

 public static bool Draw_Rect()
        {
            return RegisteredFunction.Invoke<bool>(
                "BlazorLib1.JsInterop.DrawRect");
        }

        public static async Task<string> AsyncTest_Origin()
        {
            return await RegisteredFunction.InvokeAsync<string>("BlazorLib1.JsInterop.AsyncTest_Origin");      
        }

        public static string SyncTest_Callback()
        {
            return RegisteredFunction.Invoke<string>("BlazorLib1.JsInterop.SyncTest_Callback");
        }

ClockManager.cs

 public static class ClockManager
    {
        public static Action JS_Pong { get; set; }
        
        public static void JsCallback()
        {
            JS_Pong?.Invoke(); 
        }

    }
@SteveSandersonMS
Copy link
Member

@SteveSandersonMS SteveSandersonMS commented Jul 12, 2018

When you call JsInterop.AsyncTest_Origin(); you're not awaiting it, but I think you should be. Your whole abc method should be async.

@SteveSandersonMS
Copy link
Member

@SteveSandersonMS SteveSandersonMS commented Jul 12, 2018

//1.1 version - browser hangs up!!!!!!!!!!!!!!!

Yes, you can't use task.Wait() from WebAssembly code, because that would involve blocking the browser thread, which is not something browsers allow. You must await instead of .Wait().

@Lupusa87
Copy link
Contributor Author

@Lupusa87 Lupusa87 commented Jul 12, 2018

Perfect!
Changed code and worked well :)
Thank you very much.
Tomorrow will publish interesting updates on my Blazor clock

@Lupusa87 Lupusa87 closed this Jul 12, 2018
@mkArtakMSFT mkArtakMSFT transferred this issue from dotnet/blazor Oct 27, 2019
@msftbot msftbot bot locked as resolved and limited conversation to collaborators Dec 3, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.