简体   繁体   中英

I'll upload a WebGL rendered Canvas with toDataURL and imgurAPI, but it will be an image that will be a black image

I would appreciate any and all wisdom you can give me. Thank you in advance.

Assumptions

I wrote a Javascript game using a game engine called Phaser.js.

Issue.

  • [ ] When accessing the URL in the response, an image that is only black is displayed.

Response content from imgurAPI

{"id":"dwdGkep","title":null,"description":null,"datetime":1672621440,"type":"image\/png","animated":false,"width":800,"height":800,"size":12914,"views":0,"bandwidth":0,"vote":null,"favorite":false,"nsfw":null,"section":null,"account_url":null,"account_id":0,"is_ad":false,"in_most_viral":false,"has_sound":false,"tags":[],"ad_type":0,"ad_url":"","edited":"0","in_gallery":false,"deletehash":"445HisfrUgBgogT","name":"","link":"https:\/\/i.imgur.com\/dwdGkep.png"},"success":true,"status":200}

Since "success":true, "status":200, the upload process itself seems to be OK.

Relevant source code

Image sending process

function dataURLtoBlob(dataURL) {
  const bin = atob(dataURL.split(',')[1]);
  const buffer = new Uint8Array(bin.length);
  for (let i = 0; i < bin.length; i++) {
    buffer[i] = bin.charCodeAt(i);
  }
  return new Blob([buffer], { type: 'image/png' });
}

function canvasToImage() {
  //get image data from canvas element
  //const canvas = document.getElementsByTagName("canvas")[0];
  var gl = game.canvas.getContext("webgl", { preserveDrawingBuffer: true });
  const imageData = game.canvas.toDataURL("image/png");

  // convert image data to Blob format
  const blob = dataURLtoBlob(imageData);

  const xhr = new XMLHttpRequest();

  xhr.open("POST", "https://api.imgur.com/3/image");
  xhr.setRequestHeader('Authorization', 'Client-ID XXXXXXXXXXXXXXXX');

  xhr.onload = function() {
    if (xhr.status === 200) {
      console.log(xhr.response);
    } else {
      console.error(xhr.response.data); }
    }
  };

  xhr.send(blob);
}

The calling part of the above process (excerpt) Like setting up an event in the create function.

tweetButton.on("pointerdown", () => {
// Put what happens when the button is clicked here
canvasToImage();
});

Here is the entire code create function

var tweetButton;

// create a game object
function create() {
    // display the tweetButton
    tweetButton = this.add.image(400, 500, "tweetButton");
    tweetButton.setDepth(10);
    tweetButton.setVisible(false); tweetButton;
    tweetButton.setInteractive();
    tweetButton.on("pointerdown", () => {
    // Write the process to be executed when the button is clicked here
    canvasToImage();
    });
    var startScreenImage = this.add.image(400, 400, "startScreen");
    startScreenImage.setInteractive();
    // Create text to display the timer
    this.gameTitle = this.add.text(200, 155, "Endless Chase", {
    fontSize: "70px",
    fill: "#000000" // make the text color black
    });
    this.startMessage = this.add.text(200, 250, "Click to start game", {
    fontSize: "40px",
    fill: "#000000" // set text color to black
    });
    this.Notion = this.add.text(200, 600, "*The game is only for play on a computer", {
    fontSize: "30px",
    fill: "#DC143C" // set text color to black
    });
    startScreenImage.on("pointerdown", () => {
    // delete startScreenImage when clicked
    this.gameTitle.destroy();
    this.startMessage.destroy();
    this.Notion.destroy(); this.startMessage.destroy(); this.startScreenImage.destroy()
    startScreenImage.destroy();
    startScreenImage_flg = true;
    // Write the process to start the game here
    // Create a cursor
    this.cursor = this.add.circle(400, 400, 17, 0x0000ff);
    // create red circles
    this.redCircles = [];
    var redCircle = this.add.circle(
        Phaser.Math.Between(
        this.cursor.x - clearance, // coordinates 300px left from cursor
        this.cursor.x + clearance // coordinates 300px right of cursor
        ),
        Phaser.Math.Between(
        this.cursor.y - clearance, // coordinates 300px above the cursor
        this.cursor.y + clearance // coordinates 300px below cursor
        ),
        50, // radius
        0xff0000 // color
    );
    this.redCircles.push(redCircle);
    // start the process of adding a red circle every 5 seconds
    this.intervalId = setInterval(() => {
        // create a red circle
        var redCircle = this.add.circle(
        Phaser.Math.Between(this.cursor.x - clearance, this.cursor.x + clearance),
        Phaser.Math.Between(this.cursor.y - clearance, this.cursor.y + clearance),
        50, // radius
        0xff0000 // color
        );
        redCircle.speed = Math.random() * 0.09 + 0.01;
        this.redCircles.push(redCircle);
    }, 5000);

    // Create text to display the timer
    this.timerText = this.add.text(10, 10, "0 seconds", {
        fontSize: "32px",
        fill: "#000000" // make the text color black
    });
    // initialize the timer
    this.elapsedTime = 0;
    this.prevTime = this.time.now;
    this.gameOverText = this.add.text(200, 300, "", {
        fontSize: "32px",
        fill: "#000000" // set text color to black
    });
    this.gameOverText.setDepth(10);
    });
    // Register a process to end the game when the tab becomes inactive
    document.addEventListener("visibilitychange", () => {
    this.gameOver = true;
    startScreenImage_flg = true;
    clearInterval(this.intervalId);
    });
}

What I tried

I tried the following for the same event, but it didn't work.

var gl = game.canvas.getContext("webgl", { preserveDrawingBuffer: true });

Current Processing

  • [ ] Clicking an element in the Canvas on the Result screen will perform the following series of processes.
  • [ ] Convert the WebGL canvas to an image using toDataURL().
  • [ ] Convert the image data to Blob format.
  • [ ] Upload the data in Blob format with XMLHttpRequest using imgur's API
  • [ ] Obtain the URL in the response`

The best solution is, simply to use phaser's builtin function snapshot ( link to the documentation ), the resulting image should display correct (not special configuration needed).

This function creates an image of the current canvas and passes the taken snapshot as a HTMLImageElement to the callback function.

You would just have to adapt your code to look something like this:

function canvasToImage() {

    game.renderer.snapshot(function (image) {
        
        const blob = dataURLtoBlob(image.src);
        const xhr = new XMLHttpRequest();

        ...

        xhr.send(blob);         
    });

}

If this shouldn't work for you (I tested it, on one of my local projects, and it works, with my setup) , you just need to open an issue on the phaser github or mention it on the discourse page, and a fix will be made.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM