Canvas won't draw rectangle after for loop in function - javascript

I'm trying to create a news ticker that renders text in little square "pixels". I say "pixels" because they look like pixels but the actual squares being displayed are bigger than just 1px.
So far I can get all the letters rendered from an object that I built which contains the pixel coordinates for each rectangle. Code can be seen here:
https://jsfiddle.net/9u3ez6gu/2/
The letters render correctly, but after my for loop (see code sample below) I'm trying to create a lime colored space between each letter. This lime space never gets rendered no matter what I do. Here is the for loop and the code I'm using to render the space. Does anyone know why canvas will not let me draw the lime colored rectangle?
for (i = 0; i <= inv[letter].length; i++) {
var x = inv[letter][i][1] * full;
var y = inv[letter][i][0] * full;
context.beginPath();
context.rect(x, y, unit, unit);
context.fillStyle = 'black';
context.closePath();
context.fill();
}// End for loop
//Add a gap between letters
var gapx = full * 5;
context.beginPath();
context.rect(gapx, 0, full, full);
context.fillStyle = 'lime';
context.closePath();
context.fill();
}// End function

Your for loop goes one iteration too far:
for (i = 0; i <= inv[letter].length; i++) {
Should be <, not <=. Keep your developer console open!
Also, i should be declared with var, either at the top of the function or in the for loop header itself. In this case it (probably) doesn't matter, but it's good to get in the habit. If you don't declare the variable, it'll be global. If another of your functions also fails to declare another i, they'll be the same thing, and weird bugs can result.
If you put
"use strict";
at the top of your <script> blocks or the very top of each function, the parser (in modern browsers) will flag assignments to implicit global variables as errors.

Related

How do I change a variable while using a function?

I'm coding on the p5.js Website Editor
So I'm trying to make a lot of rectangles that move when I press a specific key.
To make that I thought of making a function where I would put everything related to the rectangles moving, so that I don't have to rewrite a code to make them move every time. I want all of them to move the same way.
This is what I tried
function wall(x, y, sx, sy){
rect(x, y, sx, sy);
if(keyIsDown(65)){
return x+1;
}
}
wall(300, 300, 20, 30);
and just got a rectangle in the right coordinates but not moving when I press the "a" key
When you want to move things in p5.js, you need to use the draw() function. The draw() function runs the code inside of it repeatedly multiple times a second. You can use that to create an illusion of movement by updating the location of your object by a small amount every frame and then re-rendering the object.
I don't see you using the draw() function, so I'm guessing this is the first part of your problem.
The second part I see is that you are referring to "a lot of rectangles", but you are only making one.
Consider the code below for a quick solution (copy-paste it into the p5.js editor to see it in action).
let wallX = 200;
let wallY = 100;
let wallSpeed = 2;
let wallWidth = 20;
let wallHeight = 30;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
if (keyIsDown(LEFT_ARROW)) {
wallX = wallX - wallSpeed;
} else if (keyIsDown(RIGHT_ARROW)) {
wallX = wallX + wallSpeed;
}
if (keyIsDown(UP_ARROW)) {
wallY = wallY - wallSpeed;
} else if (keyIsDown(DOWN_ARROW)) {
wallY = wallY + wallSpeed;
}
wall(wallX, wallY, wallWidth, wallHeight);
}
function wall(x, y, sx, sy) {
for (let i = 0; i < 5; i++){
rect(x, y + sy * i, sx, sy);
}
}
In this code snippet, I am defining some global variables (generally not a good practice, but for the purposes of a p5.js sketch this is fine). The important ones are the wallX and the wallY variables. These variables are defined outside of the draw() function, so they will not be reset every time the loop runs.
Inside the loop, there are key handlers defined (I used the arrow keys for clarity). If the arrow keys are pressed, the position of the wall will change by the wallSpeed value per frame.
Also, note the background(220) call - this redraws the grey background at the beginning of the loop. If you remove that, the results of the previous renders will be visible, chances are that you don't want that.
Finally, the wall() function. You can see that the key press handling is not done inside of it - it needs the state information to come from the outside. All this function does is use a loop to draw five rectangles in a stack, at the position it is given. When the position changes, it redraws the wall in a different place on the canvas. The rectangles all refer to the root x and y values, so they will all move together as a unit.
Another way would be to create the class to represent a Wall and make instances of that class and call methods on those instances, but that could be something to revisit once you gain more experience with p5.js.
Hope this helps!

How to check if mouse position is inside HTML5 Canvas path?

I'm new to HTML5 Canvas and Javascript. I'm currently working on a HTML5 Canvas project (I wrote a separate question about it here and here).
The logic of the game is pretty simple: track the mouse position and if the mouse position exits the blue region, the game resets. The user starts on level 1 (function firstLevel). If their mouse position enters the red box region, they advance onto the next level (return secondLevel()). I did this previously by a hefty list of if statements that compared the mouse's x and y coordinates.
I've corrected the code to create a global constructor function I can reference in each of the level functions:
function Path(array, color) {
let path = new Path2D();
path.moveTo(array[0][0], array[0][1]);
for (i = 1; i < array.length; i++) {
path.lineTo(array[i][0], array[i][1]);
}
path.lineTo(array[0][0], array[0][1]);
return path;
}
And its referenced in the level functions as such:
// In the individual levels, put code that describes the shapes locally
var big = [[350,200], [900,200], [900,250], [700,250], [600,250], [600,650], [350,650]];
var blue = Path(big);
var small = [[900,200], [900,250], [850,250], [850, 200], [900, 200]];
var red = Path(small);
// 2. create cursor
c.beginPath();
c.rect(mouseX, mouseY, mouseWidth, mouseHeight);
c.fillStyle = "#928C6F";
c.fill();
// Local code that draws shapes:
c.beginPath()
c.fillStyle = '#C1EEFF';
c.fill(blue);
c.beginPath()
c.fillStyle = "#FF4000";
c.fill(red);
I want to know how can I check for the mouse position so it can run those conditional statements using the isPointInPath method? I now have a reference (refs. blue and red) for the Canvas shape, so I'm hoping there's a way I can check if the mouse's x and y position is a point that is inside the shape/path.
Link to my project: https://github.uconn.edu/pages/ssw19002/dmd-3475/final-project/maze-page-1.html
Source code: https://github.uconn.edu/ssw19002/dmd-3475/tree/master/final-project
You could use the MouseEvent.offsetX and MouseEvent.offsetY properties of a mouse event such as mousemove. (You would need to add an event listener for mouse events to the canvas element of course.)
Then use the x and y offset values obtained to call isPointInPath like
ctx.isPointInPath(path, x, y)
where path is a return value from function Path.
While MDN lists the offset properties as "experimental" they seem to have been around since IE9 at least.

Having trouble trying to make an circle move on a canvas element, while programming with JS compiled Kotlin

I am building a very simple web-based 'breakout', and I have run into trouble when trying to make the ball move on the canvas element. I do have the game up and running in javascript. Thing is, I am now trying to port it to Kotlin(javascript compiled). Even after doing the adaptations I think were necessary, the ball won't move.
I am also having trouble with types(thats why you will see those "*1.0" lying around), since I've not been able to convert from int to double, but I wouldn't say that's the issue here. Also, the IDE is showing a remark I am not familiar with. I talk about that on the section about error messages, but the message is as follows: "Wrapped into a reference object to be modified when captured in a closure".
I am not sure where exactly the problem lies, but so far I have tried:
-Other signatures for my 'setInterval' call
-Reformating the program so that my code is within main(), as to get rid of the return and 'val canvas = initializeCanvas()' call.
import org.w3c.dom.*
import kotlin.browser.*
import kotlin.math.*
val canvas = initializeCanvas()
fun initializeCanvas(): HTMLCanvasElement {
val canvas = document.createElement("canvas") as HTMLCanvasElement
val ctx = canvas.getContext("2d") as CanvasRenderingContext2D
ctx.canvas.width = 480
ctx.canvas.height = 320
document.body!!.appendChild(canvas)
var x = canvas.width/2
var y = canvas.height-30
val dx = 2
val dy = -2
fun drawBall(){
ctx.beginPath()
ctx.arc(x*1.0, y*1.0, 10.0, 0.0, PI*2)
ctx.fillStyle = "#000000"
ctx.fill()
ctx.closePath()
}
fun draw(){
ctx.clearRect(0.0, 0.0, canvas.width*1.0, canvas.height*1.0)
drawBall()
x += dx
y += dy
}
window.setInterval(draw(),10)
return canvas
}
The expected output would be that of the ball moving towards the top-right of the canvas, and then vanishing into the wall, since I have not yet implemented collisions.
The current output, as I stated, is of a static ball.
As for error messages, there are none. But an "IDE Note" has been bothering me. on the code I provided, inside the draw function, I increment x and y. When I hover over them, intellij says that they are "Wrapped into a reference object to be modified when captured in a closure". I have never seen such warning before and a web search has not been fruitful.
Wrapped into a reference object to be modified when captured in a
closure
That merely means that you have to wrap the method you want to execute - draw - inside a closure. Without it will just invoked once.
So simply change
window.setInterval(draw(), 10)
to
window.setInterval( { draw() }, 10)

Grid offset by one

Ok, so I've been trying to get a grid to work on my canvas. Here's what I've got:
codepen link
for (var j = 0; j < rows; j++) {
for (var i = 0; i < cols; i++) {
var pix = new Pixel(i, j);
grid.push(pix);
}
}
Now, the problem is that on the grid array, the pixel on index 0 is actually the 2nd one and the last is the 1st. I want to change that but I can't see what the problem in the code is.
Take a look at your Pixel#draw() function:
this.draw = function () {
rect(this.x, this.y, l, l);
fill(0);
stroke(20);
if (this.on)
fill(255);
}
Read this code line by line and really think about exactly what it's doing. Step through it with a piece of paper and a pencil to understand exactly what's going on.
Notice that you're calling the rect() function first, and then you're setting the fill and stroke.
This will cause each square to have the color of the previous cell. So it's not that your first Pixel is located in the second square, it's that the second Pixel is drawing itself based on the color of the first Pixel. The same is true for the first Pixel drawing itself based on the color of the last Pixel.
To fix your problem, just set the stroke and fill before you call the rect() function.
Side note: you also probably want to get into the habit of using { } curly brackets for every if statement, even one-liners like this:
if(this.one){
fill(255);
}
This isn't causing any problems in your code yet, but it's a good habit to get into.

Html5 canvas graph fill on hover

I'm drawing a graph on a html 5 canvas tag from a array with numbers like
arr = [6,3,16,6,53,1,3,54,67,6,3,21,6,49,7,8,31,66,51,32,56,49,4,78,9,65,43,1,3,54,67,6,3];
These numbers will be the height of the rectangle that is drawn on the canvas and it will be filled white with a transparent background;
for (var i = 0; i < arr.length; i += 1) {
ctx.fillStyle = "#ffffff";
// Fill rectangle with gradient
ctx.fillRect(
arr[i] * 10,
c_height - arr[i],
8,
arr[i]
);
}
Users can hover these rectangles and then see some more data.
I can make them change color but if there are to many rectangles the site laggs a little bit, so my question is if it is possible to make some kind of big horizontal rectangle that will mask(white rectangles) without filling the transparent background?
1) You can define the array as a typed array instead:
var arr = new Uint8Array([6,3,16,6,53,1,3,...,3]);
Just make sure the type (here unsigned 8-bit) fits the values. If you have higher values than 255 then use a 16-bit, or 32-bit, if floating point use Float32Array and so on.
2) If the color is the same don't set the fill style inside the loop. fillStyle is rather expensive as it has to parse the string and convert it to the color it defines.
3) use path to add the rectangle to, defining and filling each time is slower than to define all rects, then fill all at the same time outside the loop.
4) use a smarter for-loop by using the array entry as a conditional statement as well. Not only is this faster in itself but by storing the array entry to a value will be faster too as JS does not have to look up an array entry every time you use arr[i]:
ctx.fillStyle = "#ffffff"; // set fill style outside loop
ctx.beginPath(); // make sure we use a clean path
for (var i = 0, a; a = arr[i]; i++) { // fetch item and use as cond. for loop
ctx.rect(a * 10, c_height - a, 8, a); // add rect to path, but not fill yet
}
ctx.fill(); // fill all rects with fillstyle
Hope this helps!

Categories

Resources