Is it possible to use multiple images in an HTML5 canvas? - javascript

I'm trying to load 10 different images into a canvas. My plan is to eventually animate these images but right now they seem to be overwriting one another. Here is my code:
var DrawLetters = function()
{
for (i = 0; i < howManyLetters; i++)
{
thisWidth = letters[i][0];
thisHeight = letters[i][1];
imgSrc = letters[i][2];
letterImg = new Image();
letterImg.onload = function()
{
context.drawImage(letterImg,thisWidth,thisHeight);
}
letterImg.src = imgSrc;
}
};
letters is an array with 10 elements where each element contains a path to the image. Any help on this would be greatly appreciated.

I've tried your code and the onload method always use the LAST value of the vars, not the value when the array was iterated.
Try setting the X and the Y to properties of the image object:
// I assume you are storing the coordinates where the letters must be
letterImg.setAtX = letter[i][XPOS];
letterImg.setAtY = letter[i][YPOS];
and on the onload:
context.drawImage(this, this.setAtX, this.setAtY);
this is the image raising the onload event.
Edit I've changed the properties used to carry the coordinates. Now they're setAtX/Y. You cannot use x and y because they're reserved.

You're drawing them on the same point. drawImage doesn't care about the height or width with your given parameters; it just wants an image and a coordinate. See https://developer.mozilla.org/en/Canvas_tutorial/Using_images
So, you're gonna need to give your images more data; something like:
thisWidth = letters[i][0];
thisHeight = letters[i][1];
imgSrc = letters[i][2];
thisX = letters[i][3]; //<---
thisY = letters[i][4]; //<---
letterImg = new Image();
letterImg.onload = function()
{
context.drawImage(letterImg, thisX, thisY, thisWidth, thisHeight);
//or, just
context.drawImage(letterImg, thisX, thisY);
}
letterImg.src = imgSrc;
Edit: Just had a thought - you can do it dymagically:
context.drawImage(letterImg, letters[i-1][0]+thisWidth, letters[i-1]+thisHeight, thisWidth, thisHeight);
With this way you'll have to check for stuff, but I think you get the overall intention.

You have to reposition the draw start position every time so the images arent overwritten.
[context . drawImage(image, dx, dy, dw, dh)][1]
There's an image on the link explaning what every parameter means.

Related

How to preload an image and examine its properties before display

This is for a carousel side show. I'd like to place the fwd arrow at a fixed location, which would be the location of the widest picture plus an increment. So I'd like to find the width of the widest picture in the array before displaying any of them.
This does not work. What should I do instead? I'm not using JQuery or Angular. Barebones.
I see a previous answer to a similar question, but that will not work for me because it assumes that the number of images is fixed. In my case, they will vary from site to site.
To be included in js.config for every web site:
var pictures = ["CodeTalker.jpg", "challenger.jpg", "Hotline.jpg", "EarliestDigraphic.jpg", "Bombe.jpg", "SIGABA.jpg", "SIGCALYSign.jpg", "Cray.jpg"];
var maxWidth = 0;
var image = new Image()
for (picture in pictures) {
image.src = picture;
image.loading = "eager";
if (image.naturalWidth > maxWidth) {
maxWidth = image.naturalWidth;
}
}
var currentPic = 0;
When executed, the natural length always comes back zero.
Hi #Rin,
The issue with your code is the image.naturalWidth property is always returning 0. As Mr.#Dave Newton described in the above comment, The image object is not properly being loaded before its properties are being accessed.
for (var i = 0; i < pictures.length; i++) {
var image = new Image();
image.src = pictures[i];
image.onload = function() {
if (this.naturalWidth > maxWidth) {
maxWidth = this.naturalWidth;
}
}
}
onload event handler function when image is fully loaded. To ensure that the image is fully loaded before its properties are accessed.

Javascript canvas images are coming out blocky when stacking on top of each other

So I'm randomly generating two types of clouds, and the clouds are intended to stack on top of each other a bit and move slowly (like clouds irl would).
So essentially I'm drawing a cloud image, clearing the area, moving the image over (by recreating it in a different position), and creating another cloud and repeating the process, but when the clouds are stacking on top of each other I'm getting a square area in between the stacked clouds. Not sure how to fix it.
Check this out: (see how the clouds have a block-like look as they stack over eachother)
http://testcloudsmc.bitballoon.com
Here's the full code:
https://repl.it/FTo8/3
And here's the most relevant parts of the code:
------------------------------------------------
var canvas; var ctx; var frameRate; var assets = [];
window.onload = function(){
canvas = document.getElementById('mycanvas');
ctx = canvas.getContext('2d');
frameRate = 1000/30; //30 fps
//Decides on a x position for clouds:
for(var f = 0;f<Math.floor(Math.random()*40)+10;f++){
cloudsarrayxpos[f] = Math.floor(Math.random()*800)+10;
}
/* Move/Render images */
for (var a = 0; a < 21; a++){
assets[a] = document.getElementById(a);
console.log(assets[a]);
};
/* CREATE OBJECTS FUNCTIONS: */
var clouds = function(x,y,h,w){
ctx.clearRect(x,y,w,h);
if((x>300)||(x<100)){
ctx.drawImage(assets[7],x,y,w,h);
}else{
ctx.drawImage(assets[20],x,y,w,h);
}
} //end of clouds function
function animate(){
for(var g = 0; g<cloudsarrayxpos.length;g++){
clouds(cloudsarrayxpos[g],(5*g),300,200);
cloudsarrayxpos[g]+=(Math.random()*.1)+.05; //changing the x position of each cloud in the array making it appear to be moving
}
window.requestAnimationFrame(animate);
}
}//end of window.onload
The problem is that you are clearing the canvas in each cloud spot before drawing it.
That means that the transparency (alpha) values of the cloud images don't come into play as each resulting pixel is composed only of the last image that was drawn on it.
To fix this, only clear the canvas once per animation cycle.
Specifically:
Remove ctx.clearRect from clouds function
Add this at the top of the animate function:
ctx.clearRect(0, 0, 1300, 600);

Rendering too many points on Javascript-player

As part of a project, I have to render a video on a JS-player from a text file which has the points - all the changed coordinates along-with the color in each frame. Below is the code I'm using to draw these point on the screen.
But the issue is that the number of changed pixels in each frame are as high as ~20,000 and I need to display these in less than 30ms (inter-frame time difference). So, when I run this code the browser hangs for almost each frame. Could someone suggest an improvement for this?
Any help is really appreciated.
c.drawImage(img,0,0,800,800);
setInterval(
function(){
while(tArr[index]==time) {
var my_imageData = c.getImageData(0,0,width, height);
color(my_imageData,Math.round(yArr[index]),Math.round(xArr[index]),Math.round(iArr[index]),255);
c.putImageData(my_imageData,0,0);
index=index+1;
}
time = tArr[index];
}
,30);
xArr, yArr, iArr, tArr are arrays of x-coordinate, y-coordinate, intensity value and time of appearance respectively for the corresponding point to be rendered
function color(imageData,x,y,i,a){ //wrapper function to color the point
var index = (x + y * imageData.width) * 4;
imageData.data[index+0] = i;
imageData.data[index+1] = i;
imageData.data[index+2] = i;
imageData.data[index+3] = a;
}

Drag and Drop not recognizing child of drag object in Animate CC

I'm trying to make a drag and drop, but it's been giving me a bunch of issues. I fix one and another comes up. I had it working where it would see if any part of my drag object entered a target area, but I wanted it to just recognize one part (it's graphic of a pointy thermometer, and you can't measure temperature with the head in real life) demo here.
The error I'm getting is that "Uncaught TypeError: Cannot read property 'drag' of undefined" (I labeled 'drag' on the demo, but its a child movieclip inside the drag object)
also, 'thermometer' and 'drag' are both named instances of movieclips.
The Code:
var dragger = this.thermometer;
//tried this to see if it would help
var tip = this.thermometer.drag;
var right = this.targetRight;
var wrong = this.targetWrong;
For moving it:
dragger.on("pressmove", function(evt){
evt.currentTarget.x = evt.stageX;
evt.currentTarget.y = evt.stageY;
if(intersect(tip, right)){
evt.currentTarget.alpha=0.2;
}
else if(intersect(tip, wrong)){
evt.currentTarget.alpha=0.7;
}
else{
evt.currentTarget.alpha=1;
}
stage.update(evt);
}, this);
For releasing it
dragger.on("pressup", function(evt){
//lock position of drag object and play animation if any
dragger.x = dragger.x;
dragger.y = dragger.y;
if(hitTestArray.length > 1){
dragger.alpha = 1;
stage.update(evt);
}//else{
if(intersect(tip, right)){ //Intersection testing for good (also tried 'dragger.drag' to see if that would work. it didn't)
alert("YAY you're right AND it works!");
dragger.alpha = 1;
}else if(intersect(tip, wrong)){ //intersection Testing for bad
alert("BOO its wrong, but YAY it works");
dragger.alpha = 1;
}
stage.update(evt);
//}
}, this);
UPDATED INTERSECT:
for testing intersection.
function intersect(obj1, obj2){
var objBounds1 = obj1.nominalBounds.clone();
var objBounds2 = obj2.nominalBounds.clone();
var pt = obj1.globalToLocal(objBounds2.x, objBounds2.y);
var h1 = -(objBounds1.height / 2 + objBounds2.height);
var h2 = objBounds2.height / 2;
var w1 = -(objBounds1.width / 2 + objBounds2.width);
var w2 = objBounds2.width / 2;
if(pt.x > w2 || pt.x < w1) return false;
if(pt.y > h2 || pt.y < h1) return false;
return true;
}
To sum up, I need to know how to make it not undefined so that I can test for that little box being in one of the big boxes.
The error is happening because you used this.currentTarget instead of evt.currentTarget on that one line.
Note that your actual code is not the same as the code you posted above. Here is the code in the live demo:
if(intersect(this.currentTarget.drag, right)){
evt.currentTarget.alpha=0.2;
} else if(intersect(this.currentTarget.drag, wrong)){
evt.currentTarget.alpha=0.7;
}
else{
evt.currentTarget.alpha=1;
}
Not sure if this will solve all your issues, but it should at least get you moving forward.
[UPDATE]
Looking a little deeper, there a number of issues that are likely contributing to your intersect function not working:
Your right/left bounds are not correct. EaselJS objects do not have a width or height (more info), so the bounds you set just have an x and y.
You can use nominalBounds to get the proper bounds. This provides the untransformed, original bounds of the symbol. You will have to account for any scaling. In this case, the bounds are:
* left: {x: 0, y: 0, width: 275, height: 300}
* right: {x: 0, y: 0, width: 413, height: 430}
Your intersection will have to consider the display list hierarchy. Specifically, when comparing position and size, they should be relative to each other. If your drag target is positioned inside another clip, it will need to consider the parent positioning. I recommend always doing localToGlobal on coordinates when comparing them.
Example:
// Find the clip's top/left position in the global scope
var p = myContainer.localToGlobal(myClip.x, myClip.y);
// OR
// Find the clip's position in the global scope
var p = myClip.localToGlobal(0, 0);

Linking two.js with dat.GUI

I want to link the change letter.linewidth = 10 with a control in dat.GUI.
Here is the code for the full letter variable:
var letter = two.interpret(document.querySelector('.assets svg'));
letter.linewidth = 10;
letter.cap = letter.join = 'round';
letter.noFill().stroke = '#333';
To add an element to dat.GUI it says in the docs "The property must be public, i.e. defined by this.prop = value", though when I add this. in front of letter.linewidth it breaks the functionality of two.js and does not interpret the SVG.
I'm kinda' new to JavaScript and having a tough time figuring this one out.
Any help would be greatly appreciated.
So after lots of playing around, I found the fix.
Here is the code to draw the SVG through two.js:
var letter = two.interpret(document.querySelector('.assets svg'));
letter.linewidth = 100;
letter.cap = letter.join = 'round';
letter.noFill().stroke = '#272727';
letter.scale = 1;
I was calling the letter wrong with dat.GUI. Here is my code for dat.GUI:
window.onload = function() {
var gui = new dat.GUI();
gui.add(letter, 'linewidth', 1, 100);
}
I don't know if this will be useful for anyone, but hey, hopefully this will help if somebody is running into the same problem.

Categories

Resources