drawImage on HTML5 canvas is not working - javascript

I'm creating a 'catch falling items' game, and have successfully drawn the 'catcher' image and the 'falling items' images onto the canvas. But I'd like to also show an image (x.png) when a falling image is not caught and hits the base of my html5 canvas. The generateX function is called from the animate function and when a falling image hits the base of the canvas. The sound.play() is working. The console.logs in the generateX function log the following - 2,3 | 1,3 | 1,3 - so I know that the image is not completely loaded the first time this function runs. Everything seems to be working EXCEPT for the actual image displaying. I tried to structure it in ways that other threads have suggested, but nothing is working. Any help is much appreciated!
generateX: function() {
console.log('inside generateX func');
var imgX = new Image();
imgX.src = 'assets/x.png';
function drawX() {
console.log(3);
counter+=20;
context.drawImage(imgX, xIcon.x + counter, xIcon.y, xIcon.width, xIcon.height);
xArr.push(imgX);
console.log('xArr', xArr);
}
if (imgX.complete) {
console.log(1);
drawX();
} else {
console.log(2);
imgX.onload = drawX;
}
helper.checkMisses();
}
animate: function() {
var sound;
if (continueAnimating) {
requestAnimationFrame(helper.animate);
}
for (var i = 0; i < surveys.length; i++) {
var survey = surveys[i];
if (helper.isColliding(survey, dinoSaurus)) {
sound = new Audio("audio/coinSound.mp3");
sound.play();
score += 5;
helper.resetSurvey(survey);
}
survey.y += survey.speed;
// if the survey is below the canvas,
if (survey.y > canvas.height) {
sound = new Audio("audio/buzzerSound.mp3");
sound.play();
helper.generateX();
helper.resetSurvey(survey);
}
}
// redraw everything
helper.drawAll();
}
--
resetSurvey: function(survey) {
// randomly position survey near the top of canvas
survey.x = Math.random() * (canvas.width - surveyWidth);
survey.y = 40 + Math.random() * 30;
survey.speed = (0.55 + Math.random()) * 0.9;
}

Try loading your image earlier
If I understand your situation correctly, I think the issue is that you need to load the image earlier so that it is easily ready by the time you want to draw it, rather than waiting to load it only inside generateX. The way you are currently coding it, you are loading the image (imgX.src = ...) and then immediately trying to draw it. True, you are checking if the loading is complete and, if not, trying again shortly after. However, a better strategy is to load the image much earlier, e.g. during game initialization.
Demo
The code snippet below demonstrates the difference that this makes. It loads 2 different images. The only difference between the two is that one (on the left) is loaded ahead of time and the other (on the right) is only loaded inside generateX. The former image displays properly the 1st time through the game cycle while the latter image is missing the 1st time and only displays properly the 2nd time through.
function loadLeftImageWellBeforeRunningGenerateX() { // correct timing
imgs[0] = new Image();
imgs[0].src = 'http://placehold.it/200x100.png';
}
function loadRightImageInsideGenerateX() { // incorrect timing
imgs[1] = new Image();
imgs[1].src = 'http://placehold.it/100x50.png';
}
function generateX() {
loadRightImageInsideGenerateX();
log('--inside generateX func');
imgs.forEach(function(imgX, num) { // same as your original code, just done for 2 different images
if (imgX.complete) {
log("----image #" + num + " (" + side[num] + "); " + 1 + ", i.e. image complete");
drawX(imgX, num);
} else {
log("----image #" + num + " (" + side[num] + "); " + 2 + ", i.e. image not complete");
imgX.onload = drawX(imgX, num);
}
});
}
function drawX(imgX, num) { // same as your original code, just allows placement of 2 images side-by-side
log("----image #" + num + " (" + side[num] + "); " + 3 + ", i.e. drawing image (" + prefix[num] + "successfully)");
context.drawImage(imgX, num * 150 + 10, 10, 100, 50);
if (num === 1) {prefix[1] = "";}
}
var
imgs = [],
numClicks = 0,
side = ["left", "right"],
prefix = ["", "un"],
button = document.querySelector("button"),
canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d');
context.fillStyle = "yellow";
context.fillRect(0, 0, 300, 150);
// as far as game logic goes, the only important lines below are marked with arrows
// the rest is just "fluff" to make the demo more understandable to the user
button.addEventListener("click", function() {
numClicks += 1;
if (numClicks === 1) {
log("You must clear your browser's recent cache every time you run this snippet " +
"in order for it to demonstrate the problems and solutions " +
"associated with loading images in this code.");
button.innerHTML = "Clear browser cache and click here again to start game initialization";
} else if (numClicks === 2) {
loadLeftImageWellBeforeRunningGenerateX(); // <----- ***
log("Game initializes. No images should yet be visible, " +
"but image #0 (left) should be loading/loaded now, i.e. ahead of time. " +
"Image #1 (right) is not yet loading/loaded. Click again to 'play game'.");
button.innerHTML = "Do NOT clear the browser cache and click here again to start playing game";
} else {
button.innerHTML = "Do NOT clear the browser cache and click here again to continue playing game";
if (numClicks === 3) {
log("Game begins. Images are required for the first time. Only the pre-loaded left image shows " +
"even though loading both is attempted.");
} else {
log("Game continues. On second and subsequent re-draws, both images are now available and visible.");
}
generateX(); // <----- ***
}
});
function log(msg) {
document.body.appendChild(document.createElement("p")).innerHTML = msg;
}
p {
margin: 0.2em;
}
<canvas id="myCanvas" width="300" height="80"></canvas>
<div>
<button>Click here to begin demo</button>
</div>
Your browser's cache can hide this bug
By the way, if and when you try to debug this kind of thing, be aware that many/most/all browsers cache images, making it potentially difficult to replicate this kind of bug if you're not aware of what's going on. For example, you might run your buggy code and the image is missing the first time through the game cycle. You study your code, make a change to try to fix the bug and run the code a second time. Voila, it looks like the problem is fixed because the image now appears the first time through the game cycle. However, it may, in fact, only be showing up in that first cycle not because the bug is fixed but because the browser is able to use the cached image even in the first game cycle, eliminating the need to load it from its source. In short, if you're trying to debug an issue like this where you suspect the timing of file loading is important but the bug only appears sometimes, try clearing your browser's recent cache to see if that makes the bug more apparent.
To clear a browser's cache:
Firefox (for Mac or Windows, v44...): click "History" / click "Clear Recent History..." / click to open "Details" if necessary / check "Cache" / click "Clear now"
Chrome (for Mac, v48...) "Chrome" (left menu item) / "Clear Browsing Data..." / select "Cached images and files" / click "Clear browsing data"
Chrome (for Windows, v48...) click customize-and-control icon (at the right) / click "More tools..." / click "Clear browsing data..." / click "Clear browsing data"
Internet Explorer (for Windows, v11...) click "Tools" / click "Delete browsing history" / select "Download history" / click "Delete"

Related

EventListener with delay or fire on load of class

I am tweaking the code of a chrome extension to display the content of a page element in the tab title and scroll it fir navigating open ticket tabs easer. As below
// Need to change this, mousemove listener is super harsh on resource
document.addEventListener("mousemove", function() {
// call the banner element
let bannerTextElements = document.getElementsByClassName("cw_BannerView");
// console.log(bannerTextElements);
// console.log(bannerTextElements[0]);
if (bannerTextElements[0]) {
// console.log("ok");
// console.log(bannerTextElements[0].innerHTML);
// Find and get ticket detail
let bannerTextLine = bannerTextElements[0].getElementsByClassName("detailLabel");
// console.log(bannerTextLine);
if (bannerTextLine[0]) {
// console.log(bannerTextLine[0].innerHTML);
// Trim "Service Ticket for cleanness Need to do this for 'Manage' too"
let tickettext = bannerTextLine[0].innerHTML.split("Manage ");
let tickettext1 = bannerTextLine[0].innerHTML.split("Service Ticket ");
// console.log(tickettext[0]);
// console.log(tickettext[1]);
// Set the title equal to everything after "Service Ticket"
if (tickettext1[1]) {
document.title = tickettext1[1];
// now works!! bit clunky though
var documentTitle = tickettext1 + " - ";
(function titleMarquee() {
document.title = documentTitle = documentTitle.substring(1) +
documentTitle.substring(0,1);
setTimeout(titleMarquee, 160);
})();
} else {
// For everything else, just display what's already there
document.title = tickettext1[0];
}
}
}
}, false);
(ignore the appalling indents i promise thats just on this post! )
However, due to the slow page load the only eventlistener i can get to pick the content of the banner text is 'mousemove' which naturally has the negative side effects of A. Firing infinite times as you view the page and also resetting the scroll to the start as you mouse over.
document.OnLoad and window.onload just stops this working and it loads the default tab title
Is there a way to get onLoad to run after an arbitrary delay, say 10s or to check to make sure that the classname cw_bannerview is loaded up and populate with the ticket title?

Why does my JavaScript code not work on refresh?

I'm taking a Udemy course and one of the exercises is to build a Dice game.
Basically it's just two dice (for each player/stakeholder) on screen both showing the value of 6. On refresh, it should 'roll' the dice (generate a random side for each dice, which are all images), and the header text should change to state the outcome of the game.
But my code doesn't work on refresh. I'm trying to find out why. Here's a comparison of my code VS the instructor's code:
This is my code, which doesn't work on refresh (but I've tested it and it works when I run it as a function).
//Generating Random Numbers for Dice 1 & Dice 2
var RandomNo1 = (Math.floor(Math.random() * 6) + 1)
var RandomNo2 = (Math.floor(Math.random() * 6) + 1)
//Making a var for image sources Dice 1-6 (images/dice#.png)
var imageSource1 = ("images/dice" + RandomNo1 + ".png")
var imageSource2 = ("images/dice" + RandomNo2 + ".png")
//set.Attribute function to change the img source for the dice
document.querySelector('.player1').setAttribute("src", imageSource1);
document.querySelector('.player2').setAttribute("src", imageSource2);
//Setting conditional statements to change the header depending on results of the dice roll.
if (RandomNo1 > RandomNo2) {
document.querySelector("h1").innerText="Player 1 Wins!";
}
else if (RandomNo2 > RandomNo1) {
document.querySelector("h1").innerText="Player 2 Wins!";
}
else {
document.querySelector("h1").innerText="It's a draw!";
}
This is the instructor's code, which does work on refresh.
var randomNumber1 = Math.floor(Math.random() * 6) + 1; //1-6
var randomDiceImage = "dice" + randomNumber1 + ".png"; //dice1.png - dice6.png
var randomImageSource = "images/" + randomDiceImage; //images/dice1.png - images/dice6.png
var image1 = document.querySelectorAll("img")[0];
image1.setAttribute("src", randomImageSource);
var randomNumber2 = Math.floor(Math.random() * 6) + 1;
var randomImageSource2 = "images/dice" + randomNumber2 + ".png";
document.querySelectorAll("img")[1].setAttribute("src", randomImageSource2);
//If player 1 wins
if (randomNumber1 > randomNumber2) {
document.querySelector("h1").innerHTML = "🚩 Play 1 Wins!";
}
else if (randomNumber2 > randomNumber1) {
document.querySelector("h1").innerHTML = "Player 2 Wins! 🚩";
}
else {
document.querySelector("h1").innerHTML = "Draw!";
}
How does her page change on refresh? Why doesn't it change immediately upon loading? Why doesn't my code, like hers, work on refresh?
Your script is fine. In essence it does the same as the script your teacher provided. To be honest, your script is better on several aspects, not in the least because you have added explanatory comments throughout, and I really like to see someone using .innerText, and not .innerHTML. The latter is really intended to inject HTML, so you did right to opt for .innerText. Personally, I would use .textContent, but that is just a detail.
The only explanation that is left, is that your script runs too soon, when the DOM is not ready yet, and so the querySelector() calls return undefined.
Move your <script> element towards the end of your HTML document, after the other elements you need to address in your script. A good place to put it, is right before the closing </body> tag.
welcome to stackoverflow!
There are a few mistakes in your code:
Do not begin your variable names with capital lettes, these are for naming constructors. (It's some sort of unspoken law to make your code better readable for other developers as well)
I don't think this is a problem in your code you showed us. I think you should
paste the script tag on the end of the html file, so document.querySelector() can select the html elements.
I made a pen here where I took your code and show you that it works. (I just changed the $element.setAttribute("src") to $element.innerText)
You can also use $element.src = instead of $element.setAttribute("src")

Copy and paste multiple times from illustrator to photoshop

So this is the small issue I am having. I am trying to send an object from illustrator to photoshop, but I want to copy and past 2 versions one that is stroked and one that is stroked. The main problem I have is the paste in photoshop comes after I have done the 2 different versions. the P is the path item that is the outline. so it will paste two versions that have no stroke because it runs the first past after the second copy has already run.
function CreateLabel(Label)
{
var P = Label.pageItems[0]
app.copy()
// get Print Area Width and Height and convert to inces at 300 DPI
var LW = "\"" + String(Math.floor((Label.width/72)*300)) + "px\""
var LH = "\"" + String(Math.floor((Label.height/72)*300)) + "px\""
// create the new document
var Cmd = "app.documents.add(" + LW + "," + LH + ", 300,\"Label\", NewDocumentMode.RGB,DocumentFill.TRANSPARENT);";
TellPhoto(Cmd)
app.copy()
TellPhoto('app.activeDocument.paste();')
P.stroked = false
app.copy()
TellPhoto('app.activeDocument.layers.getByName(\"Layer 1\").remove();' )
//bt.body = 'BridgeTalk.bringToFront("illustrator");';
//bt.send(5)
};
// paste the saved bit
function TellPhoto(Message)
{
var bt = new BridgeTalk();
bt.target = "photoshop";
bt.body = Message
bt.onError = function(e)
{
alert(e.body);
};
bt.send(1);
}
so I need something that will almost pause until photoshop has pasted the current clipboard in.
I come across similar glitches from time to time with app.copy() and app.paste() commands. Quite often an additional pause ($.sleep(200)) before app.paste() helps. My guess is the app (Illustrator) just can't keep up to copy all the data by the time when another app (Bridge or even the same Illustrator) tries to paste it.

How to hide elements between turns of a game in javascript?

I have a basic javascript/html boardgame to be played by two players on one screen. When player1 finishes their turn, I want their board to be hidden, and an alert to pop up asking player2 if they are ready to take their turn. Then player2's board becomes visible. I have tried a bunch of things and can't get it to work.
var hide = false;
displayBoard(player1);
function displayBoard(player){
for (i = 0; i < boardSize + 1; i++) {
for (j = 0; j < boardSize + 1; j++) {
var gameSquare = document.createElement("div");
var squareText = document.createElement("span");
squareText.textContent = boardArray[j][i];
if(hide){
gameSquare.style.visibility = 'hidden';
squareText.style.visibility = 'hidden';
}
gameSquare.appendChild(squareText)
boardContainer.appendChild(gameSquare);
var topPosition = j * squareSize;
var leftPosition = i * squareSize;
gameSquare.style.top = topPosition + 'px';
gameSquare.style.left = leftPosition + 'px';
}
}
function fire(){ //This happens when a player clicks on the board
event = event || window.event;
var source = event.target || event.srcElement;
//a bunch of game logic, changing up arrays
//end of player's turn
hide = true; //I want to hide the board until the next player starts
displayBoard(thisPlayer); //To update the board being hidden
window.alert("press okay to begin " + nextPlayer + "'s turn");
hide = false;
displayBoard(nextPlayer);
}
When I do this, the visibility doesn't change in the html DOM.
I have also tried setting the visibility to hidden in the fire() function, which didn't work. I've tried setting all children of the boardContainer to hidden. I've tried doing all of these before, during, and after the bulk of the displayBoard function. I've tried disabling the cache. I've tried setting display to none. I've tried adding waiting periods after displayBoard().
What is the correct way to do this?
I'm also not allowed to use JQuery for this FYI.
Edit: instead of iterating through the elements I added this to the beginning of displayBoard():
if (hide){
boardContainer.style.visibility = "hidden";
opposingBoardContainer.style.visibility = "hidden";
} else{
boardContainer.style.visibility = "visible";
opposingBoardContainer.style.visibility = "visible";
}
The result of this is that if I eliminate the else block, the board disappears after the end of the first turn. If I leave the else block in, no change seems to happen. So I suspect that it is disappearing and coming back too quickly for me to tell instead of disappearing and waiting for the alert window like I want it to. Any thoughts?
The result of this is that if I eliminate the else block, the board disappears after the end of the first turn. If I leave the else block in, no change seems to happen. So I suspect that it is disappearing and coming back too quickly for me to tell instead of disappearing and waiting for the alert window like I want it to. Any thoughts?
hide = true; //I want to hide the board until the next player starts
displayBoard(thisPlayer); //To update the board being hidden
window.alert("press okay to begin " + nextPlayer + "'s turn");
hide = false;
displayBoard(nextPlayer);
So basically what is going on here is when you call alert the DOM has not yet rendered again, so your board is still there when the alert fires and blocks the rest of the code from executing.
So when you click okay two things may be happening.
It renders the board invisible and then executes displayBoard which renders it visible immediately again.
It executes displayBoard again before rendering and then when it next renders it will be visible so nothing happens.
Either way you are going to need to change the way you handle turns so that it doesn't rely on alert to block execution.

change image jQuery without making a new request to the server

I am using jQuery to change a background image every second. This is working as I want, however, when I look at the NETWORK tab in Google chrome I see that a new GET request is made for the image every second which is obviously very uneconomical. Can anyone advise a better approach to take here. I have read on other posts that the images should be cached and that the browser should know not to make a request to the server, but as far as I can tell the browser is still making the request.
Please see current code below.
var x = 0,
homepageImages = ["1","2","3","4"];
setInterval(function(){
x++;
if(x === 4){
x = 0;
}
$('.desktop-main-img').attr('src', 'img' + homepageImages[x] + '.jpg');
}, 1000);
You want to preload the images then.
var x = 0,
homepageImages = ["1", "2", "3", "4"];
// invisible preload the images
for( var i = 0; i < homepageImages.length; i++ )
$(new Image()).src('img' + homepageImages[i] + '.jpg');
setInterval(function() {
x = ++x === 4 ? 0 : x;
$('.desktop-main-img').attr('src', 'img' + homepageImages[x] + '.jpg');
}, 1000);
But keep in mind, the network tab may show a get request for the images. But you should see, that the response is cached. The server tell your browser, that the image has not changes/he already knowns the image and will transfer no data.
you must preload all images in body before run that loop

Categories