HTML 5 Canvas Image Rendering - javascript

First time poster here but definitely not a first time reader.
My question is aimed directly at this portion of code I have. I am currently learning how HTML 5 canvases work and am designing my own RPG style game for a University project. After looking around I found some good tutorials on this guys blog, I have followed his code and triple checked it but images are now showing up.
I tried putting an alert() before and after when the image is called to the canvas under drawMap(). It works before the image is drawn but not after, leading me to believe it is something to do with my image rendering. Can someone double check my code and see what is going on? It's driving me insane!
<canvas id="game-viewport" width="760" height="440"></canvas>
<script>
window.onload = init;
var map = Array([0,0],[0,0],[0,0],[0,0]);
var tileSize = 40;
tileTypes = Array("grass.png");
tileImage = new Array();
var loaded = 0;
var loadTimer;
function loadImage(){
for(i = 0; i < tileTypes.length; i++){
tileImage[i] = new Image();
tileImage[i].src = "./game/lib/icons/own_icons/" + tileTypes[i];
tileImage[i].onload = function(){
loaded++;
}
}
}
function loadAll(){
if(loaded == tileTypes.length){
clearInterval(loadTimer);
drawMap();
}
}
function drawMap(){
var mapX = 80;
var mapY = 10;
for(i = 0; i < map.length; i++){
for(j = 0; j < map[i].length; j++){
var drawTile = map[i][j];
var xPos = (i - j) * tileSize;
var yPos = (i + j) * tileSize;
ctx.drawImage(tileImage[drawTile], xPos, yPos);
}
}
}
function init(){
var canvas = document.getElementById('game-viewport')
var ctx = canvas.getContext('2d');
loadImage();
loadTimer = setInterval(loadAll, 100);
}
</script>

The only problem is that ctx is not defined in your drawMap function.
Either pass ctx in to the function as an argument or make it a global variable.
I was lazy and did the second, but you should really do the first. Working code:
http://jsfiddle.net/YUddC/
You really should have the Chrome debugger (or whatever browser you use) on 100% of the time you're developing.. If you did, you'd see an error saying that ctx is not defined in drawMap. If you're using Chrome and press F12 to open developer tools and go to the scripts tab, you'd see this:
Which makes the problem pretty clear!

Related

How to show image when all sprites are collected p5js (play)

I'm coding a simple game in p5js with the play library in which a person wins after collecting all the "sprinkles". But I can't figure out how to show an image when all the sprinkles are collected. Can someone please help me out? I put the code parts in which the "sprinkles" and collect things happen because the total code is quite long.
function setup(){
sprinkles = new Group();
for(var i = 0; i <25; i++) {
var ang = random(360);
var px = SCENE_W/2 + 1000*cos(radians(ang));
var py = SCENE_H/2 + 1000*sin(radians(ang));
createSprinkles(2, px, py);
}
}
function draw(){
for(var j=0; j<sprinkles.length; j++) {
var s = sprinkles[j];
if(s.position.x<-MARGIN) s.position.x = SCENE_W+MARGIN;
if(s.position.x>SCENE_W+MARGIN) s.position.x = -MARGIN;
if(s.position.y<-MARGIN) s.position.y = SCENE_H+MARGIN;
if(s.position.y>SCENE_H+MARGIN) s.position.y = -MARGIN;
}
mouse.overlap(sprinkles, collect);
}
function createSprinkles(type, x, y){
var a = createSprite(x, y);
var img = loadImage('assets/sprinkles/sprinkle'+floor(random(0,5))+'.png');
a.addImage(img);
a.setSpeed(2.5-(type/2), random(360));
a.rotationSpeed = 0.5;
sprinkles.add(a);
}
function collect(collector, collected) {
collector.changeAnimation('stretch');
collector.animation.rewind();
collected.remove();
}
I've found it! I made a really dumb mistake... the dumbest one ever because I'm too tired. yay.
So if anyone is wondering about the same thing:
if(sprinkles.length<1){
background(255,200,0);
}
Here I tried a simple thing with the background colour to see if it works. Just add this code in the function draw(){} part and you're done!

Why is this dynamic HTML5 video thumbnail generation failing in IE11?

I have a Javascript function setup to dynamically generate thumbnails from a given embedded HTML5 video once it loads. This works fine on other browsers. The problem arises with IE11. For some reason, it's just not outputting anything despite working perfectly on Firefox and Chrome.
I have a section of the site which has several HTML5 videos and they need to generate their respective thumbnails based on the first frame. I've done a good bit of researching around and I can't seem to find any IE11 compatibility issues with my code.
Here is the code I have now:
var vids = document.querySelectorAll('[id^=video-]');
for(var i = 0; i < vids.length; i++){
if(vids[i].tagName == 'VIDEO'){
$("#video-" + (i+1)).on("loadeddata", generate_handler(i+1));
}
...
}
generate_handler calls the shoot function (had to do it this way because of scoping issues in the loop),
function generate_handler(j) {
return function(event) {
shoot(j);
};
}
and the shoot function goes as follows:
function shoot(num){
var video = document.getElementById('video-' + num);
var output = document.getElementById('videothumbnail-' + num);
if(output.childNodes.length == 1){
var canvas = capture(video);
output.appendChild(canvas);
}
}
and finally, the capture function is as follows:
function capture(video) {
var canvas = document.createElement('canvas');
var w = 48;
var h = 40;
canvas.width = w;
canvas.height = h;
canvas.style.marginTop = "4px";
canvas.style.width = w + "px";
canvas.style.height = h + "px";
canvas.style.zIndex = "-999";
var ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, w, h);
return canvas;
}
Essentially, as a TL;DR: When the data of the video is loaded, it calls this function to shoot a screengrab and add it to the thumbnail div. In order to do that, it draws the video to a canvas. This is the order the functions are called:
on('loadeddata', generate_handler()) -> shoot() -> capture()
What's strange is that after some simple tests with console.log, it actually is reaching the inside of capture(), which inclines me to believe it's a compatibility issue with something in there, or with appendChild().
What this should do (and does do on Firefox/Chrome) is draw a 48x44px thumbnail of the HTML5 video being loaded on the page. Instead, on IE11 it displays nothing.

JavaScript running pretty slow in specific computer

I writing a code that take a Black & White image and check the pixels in an specific area (with an square shape) and finally retur the sum of how many of them are balck, each pixel of the area is read in a For loop like the next example:
function is_box_black_corner(x,y,width,heigth){
var counter=0;
for (var i=x; i<(x+width); i++){
for (var j=y; j<(y+heigth); j++){
if(my_isblack(i,j)==1){
counter++;
}
}
}
And as you can see inside the for loop a I call a function that verifies if the specific pixel is fairly black:
function my_isblack(x,y){
var p = ctx.getImageData(x, y, 1, 1).data;
if(p[0]<50 && p[1]<50 && p[2]<50){
return 1;
}
else{
return 0;
}
}
As you can imagine, this is a little bit computational expensive. but the problem is that with my computer, suddenly it got much slower than others (even with worst processors). I already check the RAM memory and the processor and none of them were used more than 30%, and the processor before running the code is close to 0%.
And don’t know where else to look. I appreciate some help, also if somebody knows how to do this much faster it will be highly apreciated
I will try wiht one call to getImageData as suggested by #ASDFGerte:
var x=10; var y=10; var width=50; var height=50;
var counter=0;
var image;
var p; //global data
function init(){
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(10, 10, 50, 50); //this is just a black square
image = ctx.getImageData(x, y, width, height); //Load the image
p = image.data; //get the data of the image
is_box_black_corner();
};
function is_box_black_corner(){
for (var i=x; i<(x+width); i++){
for (var j=y; j<(y+height); j++){
if(my_isblack(i,j)==1){
counter++;
}
}
}
console.log(counter);
};
function my_isblack(x,y){
if(p[0]<50 && p[1]<50 && p[2]<50){ //check the global data
return 1;
}
else{
return 0;
}
};

Chrome Performance Issue With Bitmap Area Sampling Effect (JavaScript)

I am writing an HTML5 game using the engine Phaser, in which I am implementing what are essentially live backgrounds, backgrounds that respond to the movements of the game objects. The first I am working with is a water ripple effect that uses area sampling on the bitmapData object. I thought I had a performance issue in my code, but it turns out that Firefox runs it like a dream. Chrome runs a little slower to begin with and slows to less than 10 FPS when my game objects go too close to the top or bottom of the screen. (I am at a loss for why that makes a difference.)
This thread suggests that Chrome has poor image processing performance and suggests to break large image data up into smaller pieces. I don't know if this is possible in my case, because this is not simply an image displaying on the screen but an effect based on pixels next to each other that refreshes each frame. Even if it is possible, I think Chrome would end up having to do the same amount of work or more to get the four individual bitmaps to interact with each other as if they were one.
I've been doing performance tests in Chrome for a few hours, and the issue is definitely that it is getting caught up on the method that actually creates the effect by reading pixels from a source imageData and writing them to another location in a target imageData (the ws.displace(x,y) method below).
function waterStage(canvas) {
var ws = new Object();
ws.dampFactor = 16;
ws.magFactor = 150;
ws.dispFactor = 0.5;
ws.lumFactor = 1;
ws.width = canvas.width;
ws.height = canvas.height;
// Initialize height data caches
ws.pMaps = [];
var map1 = new Array(ws.width+2);
var map2 = new Array(ws.width+2);
for (x=0; x < map1.length; x++) {
map1[x] = new Array(ws.height+2);
map2[x] = new Array(ws.height+2);
}
for (x=0; x < map1.length; x++) {
for (y=0; y < map1[x].length; y++) {
map1[x][y] = 0;
map2[x][y] = 0;
}
}
ws.pMaps.push(map1, map2);
ws.stageInit = function(canvas) {
canvas.fill(100,100,100);
canvas.ctx.strokeStyle = "#000000";
canvas.ctx.lineWidth = 2;
canvas.ctx.moveTo(0,0);
for (y=0; y < ws.height; y+=10) {
canvas.ctx.beginPath();
canvas.ctx.moveTo(0,y);
canvas.ctx.lineTo(ws.width,y);
canvas.ctx.closePath();
canvas.ctx.stroke();
}
ws.sourceData = canvas.ctx.getImageData(0, 0, ws.width, ws.height);
ws.targetData = canvas.ctx.getImageData(0, 0, ws.width, ws.height);
}
ws.setWave = function(pnt) {
ws.pMaps[0][pnt.x-1][pnt.y-1] = ws.magFactor//*pnt.magnitude;
}
ws.resolveWaves = function(x,y) {
// Calculate the net result of the wave heights
ws.pMaps[1][x][y] = ((ws.pMaps[0][x-1][y]+ws.pMaps[0][x+1][y]+ws.pMaps[0][x][y-1]+ws.pMaps[0][x][y+1]) / 2)
-ws.pMaps[1][x][y];
ws.pMaps[1][x][y] -= (ws.pMaps[1][x][y]/ws.dampFactor);
}
ws.displace = function(x,y) {
var displace = Math.floor(ws.pMaps[1][x][y]*ws.dispFactor);
var xCorrect = x-1, yCorrect = y-1;
var targetIndex = (xCorrect + yCorrect * ws.width)*4;
if (displace == 0) {
ws.targetData.data[targetIndex] = ws.sourceData.data[targetIndex];
ws.targetData.data[targetIndex+1] = ws.sourceData.data[targetIndex+1];
ws.targetData.data[targetIndex+2] = ws.sourceData.data[targetIndex+2];
}
else {
if (displace < 0) {
displace += 1;
}
var sourceX = displace+xCorrect;
var sourceY = displace+yCorrect;
var sourceIndex = (sourceX + sourceY * ws.width)*4;
//var lum = ws.pMaps[1][x][y]*ws.lumFactor;
ws.targetData.data[targetIndex] = ws.sourceData.data[sourceIndex];//+lum;
ws.targetData.data[targetIndex+1] = ws.sourceData.data[sourceIndex+1];//+lum;
ws.targetData.data[targetIndex+2] = ws.sourceData.data[sourceIndex+2];//+lum;
}
}
ws.stageRefresh = function(moves, canvas) {
canvas.clear();
for (j=0; j < moves.length; j++) {
ws.setWave(moves[j]);
}
for (x=1; x <= ws.width; x++) {
if (ws.pMaps[1][x][0] != 0 || ws.pMaps[0][x][0] != 0) {
alert("TOP ROW ANOMALY");
}
for (y=1; y <= ws.height; y++) {
ws.resolveWaves(x,y);
ws.displace(x,y);
}
}
ws.pMaps.sort(function(a,b) { return 1 });
//ws.pMaps[0] = ws.pMaps[1];
//ws.pMaps[1] = temp;
canvas.ctx.putImageData(ws.targetData, 0, 0);
}
return ws;
}
canvas is the bitmapData that is given as the texture for the background (not an HTML5 canvas; sorry if that's confusing). ws.stageRefresh(moves,canvas) is called on every frame update.
Before I try to make the split-into-four-bitmaps solution work, does anyone have any guidance for other ways to improve the performance of this effect on Chrome?

Drawing img in Canvas with for loop

var c=document.getElementById("cvs");
var ctx=c.getContext("2d");
var imgArray = [];
for (var i=0;i<data.length;i++){
var drawRepeat = Math.floor((data[i]/divider));
imgArray[i] = [];
for (var j=0;j<drawRepeat;j++){
//alert(j);
var xPos = ((i*30)+10);
var yPos = 250-((j*30)+30);
imgArray[i][j] = new Image();
imgArray[i][j].src="assets/orange.png";
imgArray[i][j].onload = function(){
ctx.drawImage(imgArray[i][j],xPos,yPos);
};
}
}
I want to draw multiple images with a for loop. weirdly when i place an alert() with the for loop it works? but if i comment it away it will only display 1 of the image.
Is there any solution to this?
That's the problem related to closures. I'll explain after giving the code.
var c = document.getElementById("cvs");
var ctx = c.getContext("2d");
var imgArray = [];
for (var i = 0; i < data.length; i++) {
var drawRepeat = Math.floor((data[i] / divider));
imgArray[i] = [];
for (var j = 0; j < drawRepeat; j++) {
//alert(j);
(function (_i, _j) {
var xPos = ((_i * 30) + 10);
var yPos = 250 - ((_j * 30) + 30);
imgArray[_i][_j] = new Image();
imgArray[_i][_j].src = "assets/orange.png";
imgArray[_i][_j].onload = function () {
ctx.drawImage(imgArray[_i][_j], xPos, yPos);
};
})(i, j);
}
}
UPDATE:
The reason I am doing this weird stuff is that the function registered to onload event will be called asynchronously - that is the loop will continue and the function will be called when the image loads, and not at that very moment. So what happens is that this loop continues, and once it's completed (note that this may occur even before completion), i's value will be data.length - 1. Now, some image is loaded, and then the function is called. This function refers to i (in the code you gave), which by now is data.length - 1. And so, only the last image gets drawn. I know this is confusing - even I felt it confusing when I first stumbled upon closures. I recommend you read some good article on them and you'll see what the problem is.
What we did was to create a scope (experts might find a problem with this expression) by creating an anonymous function which is called immediately, and is passed the values of i and j as it's parameters - _i and _j (Note that we could've used the same names, but to avoid confusion, I didn't use the same names). Now, these are local to the function and aren't altered. So, when ith image is loaded, it's onload function is called which draws the _ith image in the array.
I have failed to explain it well, so as I said, please read some article on closures.
Preload the images, instead of using a onload handler.
var c=document.getElementById("cvs");
var ctx=c.getContext("2d");
var img = new Image();
img.src="assets/orange.png";
img.onload = function(){
for (var i=0;i<data.length;i++){
var drawRepeat = Math.floor((data[i]/divider));
for (var j=0;j<drawRepeat;j++){
//alert(j);
var xPos = ((i*30)+10);
var yPos = 250-((j*30)+30);
ctx.drawImage(img,xPos,yPos);
}
}
}
I managed to solved the problem with onload by placing the onload outside the for loops and putting drawImage command inside the onload function and in the for loops. Works as well.

Categories