canvas drawImage doesn't draw images the first time - javascript

I'm working on a simple JavaScript game using HTML5 canvas. It's a very typical game-loop setup involving:
init() function that initializes objects to be used the playfield, randomizing some start x/y positions and similar setup tasks
draw() function that draws all the game objects, including some simple canvas shapes and a few images
setInterval to call draw every 25 milliseconds
While getting this project started, I had the setInterval call at the end of the init() function, so it'd just start drawing and animating as soon as you loaded the page, and everything worked fine. Now that I'm further along, I'd like to draw a fully populated, static playfield on load and then use a button to kick off the main game loop interval. So I added a single call to draw() at the end of init(), and the issue is that when I do this, all the canvas shapes get drawn properly but none of the images render on the canvas.
They do render if I let draw() run a few times, like...
var previewDraw = setInterval(draw, 25);
var stopPreviewDraw = function() { clearInterval(previewDraw) }
setTimeout(stopPreviewDraw, 100)
...but that seems dumb. Why doesn't a single call to draw() work? I've logged the objects in Chrome's and Firefox's consoles and everything about them looks fine; they already have the appropriate img src paths and start x/y values available to pass to assign to the new Image() that gets created and then called via my canvas.2dcontext.drawImage() method.
I'm testing this in Chrome 6 and Firefox 3.6.10 as I go, and they're both puzzling me with this behavior. No errors or issues show in Chrome's console but if I try calling draw() only once the Firefox throw this:
uncaught exception: [Exception... "Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [nsIDOMCanvasRenderingContext2D.drawImage]" nsresult: "0x80040111 (NS_ERROR_NOT_AVAILABLE)" location: "JS frame :: http://localhost/my-game-url/ :: drawItem :: line 317" data: no]
My Google searches for that error suggest a corrupt image, but they all open in Preview and Photoshop without any problems.

I assume you are calling init() after the onload event of the window object is fired. The problem is, that does not guarantee to you that the image data is actually available at that point. AFAIR it would fire after the images are fetched from the network but then they still need to be decoded.
Your best bet would be to wait for the images' onload event instead.

Related

Any way to quickly find out from what Javascript file and code line a custom event is dispatched?

I would like to be able to find the JavaScript file and code line from which a custom event is dispatched.
Let's say we have two JavaScript files, A.js and B.js. I define and dispatch a custom event from A.js at line X. I listen for it in B.js at line Y.
I could jump into Chrome DevTools and run monitorEvents($0) to find out where the event was 'heard' (where the eventListener is in my code base, namely: line Y of B.js). But is there a similarly quick way to find out where the event was dispatched from (namely line X from A.js)?
If I understand correctly, in the event listener, create a new Error - the second line in its .stack property is the source of the event dispatch - note, second line in Firefox, third line in Chrumium based bruisers.
note: stack property of an Error is "non-standard" - however it's available in every modern browser released in the last 7 years (or older in most cases), and even in IE10
// Chrum browsers stack starts at the second line
const fixChrum = +(new Error().stack.split('\n')[0].startsWith('Error'));
window.addEventListener('custom:event', () => {
console.log('dispatched from', new Error().stack.split('\n')[1 + fixChrum]);
});
// the following is not required, it just shows the line number before the dispatch
console.log('dispatched from next line', new Error().stack.split('\n')[0 + fixChrum]);
window.dispatchEvent(new Event('custom:event'));

how to solve TypeError: cv.Mat is not a constructor opencv.js?

I have a problem using opencv.js when I want to detect aruco markers from my camera. Every time I try to use the method
let image = new cv.imread('img');
I get the following error:
TypeError: cv.Mat is not a constructor
at Object.matFromImageData (VM52998 opencv.js:55)
I tried to use this question to try and solve my issue. The solution from the question was to use async load, call onOpenCvReady() when the script is loaded, which will set a handler for onRuntimeInitialized event of cv. The handler should perform the necessary operations.
<script>
function onOpenCvReady() {
cv['onRuntimeInitialized']=()=>{
alert('OpenCV.js is ready.');
// do all operations
</script>
<script async src="<%= BASE_URL %>static/opencv.js" onload="onOpenCvReady();"></script>
However, when I tried to use the solution from the question I encoutered a problem:
the function onOpenCvReady() wasn't called at all.
In addition, I waited for more than 15 minutes (just to be sure) and then tried to use the line javascript let image = new cv.imread('img'); directly in console. Every move I made, and every step I took results in the same issue.
After going back to square 1, I had retried the most basic of solutions:
I just added this to my HTML page, instead of everything written here.
<script src="<%= BASE_URL %>static/opencv.js" id="opencvjs"></script>
This didn't directly worked, but after debugging some other areas of my code, I made sure that my page doesn't contain other errors (which are not related to this issue at all). For some reason I've yet to investigate and fully understand, having other errors while loading the opencv.js script has interrupted the loading process. Although it sounds unrelated, this had a huge effect on opencv commands.
The end result was magnificent:
let image = new cv.imread('img');
console.log(image);
OUTPUT:
Mat {$$: {…}}

excanvas refusing to work in IE8 despite executing in load event

if(/MSIE (\d+\.\d+); /.test(navigator.userAgent)) {
var foo = document.getElementById('herd-reports-report-table-div');
var c = document.createElement('canvas');
c.setAttribute("width",800);
c.setAttribute("height",400);
c.setAttribute("class","mapping");
foo.appendChild(c);
c = G_vmlCanvasManager.initElement(c);
} else {
var c = document.getElementById('herd-report-chart-canvas');
}
var context = c.getContext('2d');
Let's say that I have this code executing in a function on load of a page, and I need a chart to be built. I have included excanvas, yet it won't work. IE8 throws an error at the last line. All I get is "null" error at the appendChild step. Or i get "Object is not supported by this property or method".
I'm at my wit's end. I need this canvas to work in IE8. Somehow.
So I ultimately found the fix for why it (getContext) wasn't working. A few things
My conditional comment for including excanvas wasn't correct.
Outside that, mine didn't have the support for certain functions called on the canvas element by chart.js, so an update to the excanvas.js fixed that issue.
I have everything working on load now, so that issue is also resolved.

How to use nsITimer in a Firefox Extension?

I am working on a Firefox extension, and wish to make use of a timer to control posting of data every 60 seconds.
The following is placed inside an initialization function in the main .js file:
var timer = Components.classes["#mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
timer.init(sendResults(true), 60000, 1);
However, when I try to run this I get the following error in the Firefox console:
"Component returned failure code: 0x80004003 (NS_ERROR_INVALID_POINTER) [nsITimer.init]" nsresult: "0x80004003"...
And so on. What did I do wrong?
UPDATE:
The following works for my needs, although the initial problem of using nsITimer instead still remains:
var interval = window.setInterval(function(thisObj) { thisObj.sendResults(true); }, 1000, this);
}
Useful links that explain why this works (documentation on setInterval/sendResults, as well as solution to the 'this' problem:
https://developer.mozilla.org/En/DOM/window.setTimeout
https://developer.mozilla.org/En/Window.setInterval (won't let me post more than two hyperlinks)
http://klevo.sk/javascript/javascripts-settimeout-and-how-to-use-it-with-your-methods/
nsITimer.init() takes an observer as first parameter. You probably want to use a callback instead:
timer.initWithCallback(function() {sendResults(true); }, 60000, Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
But window.setInterval() is easier to use nevertheless - if you have a window that won't go away (closing a window removes all intervals and timeouts associated with it).

Javascript error in IE7 only (jQuery / Cycle.js

I'm using jQuery and cycle.js to fade through some images on this site.
On the first and last slides, I've put a callback function to go to the next/prev pages, however in IE7 I'm receiving a runtime javascript error (not in any other browsers).
Here's the link to my javascript file: http://bit.ly/dAKEof
The error I'm receiving in IE7 is:
Line: 126
Char: 4
Error: Object Expected
Code: 0
Here's the code I have at line 126 of the functions.js file:
window.location = $('#next').find("a").attr("href");
Any ideas?
I didn't see a link to the site with the error in your question but saw a url in your profile and went there to see if that might be the one. Looks like it is...
IE is notorious for giving incorrect line numbers in its javascript errors. The error is actually on line 125:
function onBefore(curr, next, opts, fwd) {
// If the page isn't loading or refreshing - initCycle == false (fixes auto unbinding of slide opts on load)
// If is last slide and next slide is fwd direction go to next project instead of wrapping cycle
if(opts.currSlide + 1 == opts.slideCount && fwd && !initCycle){
opts.end(opts); // <-- this line is failing b/c the end function is null
window.location = $('#next').find("a").attr("href");
}
// If is first slide, and next slide is bkwd direction go to prev project instead of wrapping cycle
else if(opts.currSlide + 1 == 1 && !fwd){
opts.end(opts); // <-- this also fails for the same reason
window.location = $('#prev').find("a").attr("href");
}
}
The end function in the ops object is null, and that's where the error is. Interestingly, I checked in FF as well and this is also true there, but it seems to recover more gracefully than IE in this case.
It's not entirely clear from the rest of the code how the onBefore function gets called. Looks like it might be the cycle plugin itself. In any event, wherever that call happens the opts object appears to not be getting populated with all the data that is needed by your function.
Update:
I took a look at the cycle plugin code and opts.end is a callback that the user of the plugin must supply. So the solution to your problem is to either provide your own callback when you create the cycle object (line 101 in your js file) or remove the lines in your code that invoke the end function.
Update in response to OP's comment below:
Upon closer inspection your code will actually do exactly what you want without the need to invoke an end callback, so you can safely remove those lines. The plugin triggers your onBefore callback before it does any animations. And since you reload a new url in the browser when moving backward from the 1st slide or forward from the last one, it never even gets to the fade animation, which is your goal.
However, to answer your question on how to add an end callback to the original cycle object, you would do it exactly as you did for onBefore and onAfter. Create a function with whatever code you want to execute and then include end: yourFuncName in the object hash passed to the plugin. Another way to stop the cycle plugin is $(selector).cycle('stop');. But again, none of this is needed in your case. Just remove the calls to end and you should be fine.

Categories

Resources