I've found this example on codePen, and the first iteration when letters moving in one line looks terrible,
is it possible to start moving columns from different postions from start? (not from second iteration)
my solution is terrible too: I've added #keyframes with opacity 0 to 100 and added animation with duration 3s and animation-timing-function: step-end;
// geting canvas by Boujjou Achraf
var c = document.getElementById("c");
var ctx = c.getContext("2d");
//making the canvas full screen
c.height = window.innerHeight;
c.width = window.innerWidth;
//chinese characters - taken from the unicode charset
var matrix = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789##$%^&*()*&^%+-/~{[|`]}";
//converting the string into an array of single characters
matrix = matrix.split("");
var font_size = 10;
var columns = c.width/font_size; //number of columns for the rain
//an array of drops - one per column
var drops = [];
//x below is the x coordinate
//1 = y co-ordinate of the drop(same for every drop initially)
for(var x = 0; x < columns; x++)
drops[x] = 1;
//drawing the characters
function draw()
{
//Black BG for the canvas
//translucent BG to show trail
ctx.fillStyle = "rgba(0, 0, 0, 0.04)";
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = "#f4427d";//green text
ctx.font = font_size + "px arial";
//looping over drops
for(var i = 0; i < drops.length; i++)
{
//a random chinese character to print
var text = matrix[Math.floor(Math.random()*matrix.length)];
//x = i*font_size, y = value of drops[i]*font_size
ctx.fillText(text, i*font_size, drops[i]*font_size);
//sending the drop back to the top randomly after it has crossed the screen
//adding a randomness to the reset to make the drops scattered on the Y axis
if(drops[i]*font_size > c.height && Math.random() > 0.975)
drops[i] = 0;
//incrementing Y coordinate
drops[i]++;
}
}
setInterval(draw, 35);
/* By Boujjou Achraf*/
/*basic reset */
*{
margin: 0;
padding: 0;
}
body {background: black;}
canvas {display:block;}
/* By Boujjou Achraf*/
<html>
<head>
</head>
<body>
<canvas id="c"></canvas>
</body>
</html>
The vertical screen position for each of the animations' letters is stored in an array called drops.
At startup each letter gets the same initial position by the following two lines:
for (var x = 0; x < columns; x++)
drops[x] = 1;
You can make each letter's position a random number from 0 to the height of the canvas element by changing the above to this:
for (var x = 0; x < columns; x++)
drops[x] = parseInt(Math.random() * c.height);
Math.random() returns a floating point number between 0 and 1 and c.height returns the height of the <canvas> element. By multiplying these two values we get a random number between 0 and the canvas' height.
Here's your modified example:
var c = document.getElementById("c");
var ctx = c.getContext("2d");
//making the canvas full screen
c.height = window.innerHeight;
c.width = window.innerWidth;
//chinese characters - taken from the unicode charset
var matrix = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789##$%^&*()*&^%+-/~{[|`]}";
//converting the string into an array of single characters
matrix = matrix.split("");
var font_size = 10;
var columns = c.width / font_size; //number of columns for the rain
//an array of drops - one per column
var drops = [];
//x below is the x coordinate
//1 = y co-ordinate of the drop(same for every drop initially)
for (var x = 0; x < columns; x++)
drops[x] = parseInt(Math.random() * c.height);
//drawing the characters
function draw() {
//Black BG for the canvas
//translucent BG to show trail
ctx.fillStyle = "rgba(0, 0, 0, 0.04)";
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = "#f4427d"; //green text
ctx.font = font_size + "px arial";
//looping over drops
for (var i = 0; i < drops.length; i++) {
//a random chinese character to print
var text = matrix[Math.floor(Math.random() * matrix.length)];
//x = i*font_size, y = value of drops[i]*font_size
ctx.fillText(text, i * font_size, drops[i] * font_size);
//sending the drop back to the top randomly after it has crossed the screen
//adding a randomness to the reset to make the drops scattered on the Y axis
if (drops[i] * font_size > c.height && Math.random() > 0.975)
drops[i] = 0;
//incrementing Y coordinate
drops[i]++;
}
}
setInterval(draw, 35);
* {
margin: 0;
padding: 0;
}
body {
background: black;
}
canvas {
display: block;
}
<canvas id="c"></canvas>
Related
This question already has an answer here:
Unable to update HTML5 canvas pixel color using putImageData in JavaScript
(1 answer)
Closed last year.
I am trying to draw a gray gradient (or a few of them actually) to the canvas. To do this I am using the getImageData() and putImageData() methods of canvas. data is an Uint8ClampedArray with size 1000 * 1000 * 4. In the for loops the rgb color elements of data are correctly set to be x%255, as shown by printing the data array to console.
However the result of code differs from what is expected. A completely white cnvas is shown while 4 gray gradients are expected.
Minimal working example:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id = "canvas" width = "1000" height = "1000"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
draw();
function draw(){
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for(let y = 0; y < canvas.height; y++){
for(let x = 0; x < canvas.width; x++){
for(let color = 0; color < 3; color++){
let idx = (y * canvas.width + x) * 4;
data[idx + color] = x % 255;
}
}
}
ctx.putImageData(imageData, 0, 0);
}
</script>
</body>
</html>
You're not setting the alpha channel.
for (let y = 0; y < canvas.height; y++) {
for (let x = 0; x < canvas.width; x++) {
let idx = (y * canvas.width + x) * 4;
for (let color = 0; color < 3; color++) {
data[idx + color] = x % 255;
}
data[idx + 3] = 255; // alpha
}
}
I have to create a moving rectangle from left to right, and from right to left.
I want to change the color of the rectangle (to a random color) after every 20 pixels the rectangle passes. When the rectangle is on right, its size will change to 100px, and when it is on the left, its size will go back to 50px.
For now I have:
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
x = 0,
last = performance.now();
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function draw(timestamp) {
requestAnimationFrame(draw);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.rect(x, 50, 50, 50);
ctx.fillStyle = "#ffffff";
ctx.fill();
x += (timestamp - last) / 10;
last = timestamp;
}
requestAnimationFrame(draw);
Thanks for the answers in advance!
You need to do a few things. Firstly, you need to create variables for each thing which has the potential to change. This includes the size of the box, the color of the box, and the direction of the box:
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
x = 0,
size = 50,
dir = 1,
boxColor = "#ffffff",
last = performance.now();
The variable dir is 1, which means it will take your box's current direction and times it by 1. If you want to change the direction of your box, we need to subtract from x. This means we need to change dir to -1 each time the box hits the right wall, and change it back to 1 when the box hits the left wall.
To accomplish this, we can add a helper function called bounce which checks if the box's width + its size is passed the width of the canvas. If it is, we know the box has gone past the right-hand side of the canvas container. Thus, we can change the boxes direction to -1. We can also reposition the box's location to be outside of the wall in case it managed to clip into the wall while moving. We can apply the same logic for checking if the box surpasses the left edge of the canvas. During this checking we can also change the size of our box such that it grows when it hits the right wall, and shrinks when it hits the left wall:
function bounce() {
if(x+size > canvas.width) { // if right edge passes the widtth of the canvas, change the dir
dir *= -1;
size = 100;;
x = canvas.width-size; // move the box outside of the wall (boundary)
} else if(x < 0) {
dir *= -1;
size = 50;
x = 0;
}
}
And thus, we must also change our change in x equation to be:
x += dir*(timestamp - last) / 10;
Lastly, we need to add a way to change the box's colour. This can be done by taking the modulo (%) of the x position. If x % 20 equals 0 we know our box is on an x position which is a multiple of 20 and so we change its colour. However, as the x position doesn't change by 1's each time, we need to round x before we do this check. Moreover, we need to give this check a bit of leeway as we may skip the 20th pixel and thus won't ever get a multiple of 20, so, we can use <=1 as our check. (Note: this isn't the perfect check, but it's the best I could come up with for now :/):
function color() {
if(Math.round(x) % 20 <= 1 ) { // if the x divide 20 gives a remainder of 0
var r = Math.floor(Math.random() * 255);
var g = Math.floor(Math.random() * 255);
var b = Math.floor(Math.random() * 255);
boxColor = "rgb(" +r +", " +g +", " +b +")";
} else {
console.log(Math.round(Math.abs(x)) % 20);
}
}
See example below:
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
x = 0,
size = 50,
dir = 1,
boxColor = "#ffffff",
last = performance.now();
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function draw(timestamp) {
bounce();
color();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.fillStyle = boxColor;
ctx.rect(x, canvas.height / 2 - size / 2, size, size);
ctx.fill();
x += dir * (timestamp - last) / 10;
last = timestamp;
requestAnimationFrame(draw);
}
function bounce() {
if (x + size > canvas.width) { // if right edge passes the widtth of the canvas, change the dir
dir *= -1;
size = 100;
x = canvas.width - size; // move the box outside of the wall (boundary)
} else if (x < 0) {
dir *= -1;
size = 50;
x = 0;
}
}
function color() {
if (Math.round(x) % 20 <= 1) { // if the x divide 20 gives a remainder of 0
var r = Math.floor(Math.random() * 255);
var g = Math.floor(Math.random() * 255);
var b = Math.floor(Math.random() * 255);
boxColor = "rgb(" + r + ", " + g + ", " + b + ")";
}
}
requestAnimationFrame(draw);
body {
margin: 0;
}
<canvas id="canvas" style="border: 1px solid black; background-color: black;"></canvas>
I am trying to create a game (at beginning stages). I am trying to create balls that bounce around a canvas, I have created balls, randomized them and have animated them.
But when trying to add a boundary I can only seem to get the balls to act as one object rather than separate ones. If NumShapes is changed to 1 it works perfectly.
if( shapes[i].x<0 || shapes[i].x>width) dx=-dx;
if( shapes[i].y<0 || shapes[i].y>height) dy=-dy;
For movement:
shapes[i].x+=dx;
shapes[i].y+=dy;
See this:
var ctx;
var numShapes;
var shapes;
var dx = 5; // speed on the X axis
var dy = 5; // speed on the Y axis
var ctx = canvas.getContext('2d');
var width = canvas.width;
var height = canvas.height;
function init() // draws on the Canvas in the HTML
// calling functions here would not run them in the setInterval
{
numShapes = 10;
shapes = [];
drawScreen();
ctx = canvas.getContext('2d');
setInterval(draw, 10); // Runs the Draw function with nestled functions
makeShapes();
}
function draw() {
clear();
drawShapes();
}
function clear() {
ctx.clearRect(0, 0, width, height); // clears the canvas by WIDTH and HEIGHT variables
}
function makeShapes() {
var i;
var tempRad;
var tempR;
var tempG;
var tempB;
var tempX;
var tempY;
var tempColor;
for (i = 0; i < numShapes; i++) { // runs while i is less than numShapes
tempRad = 10 + Math.floor(Math.random() * 25); // random radius number
tempX = Math.random() * (width - tempRad); // random X value
tempY = Math.random() * (height - tempRad); // random Y value
tempR = Math.floor(Math.random() * 255); // random red value
tempG = Math.floor(Math.random() * 255); // random green value
tempB = Math.floor(Math.random() * 255); // random blue value
tempColor = "rgb(" + tempR + "," + tempG + "," + tempB + ")"; // creates a random colour
tempShape = {
x: tempX,
y: tempY,
rad: tempRad,
color: tempColor
}; // creates a random shape based on X, Y and R
shapes.push(tempShape); // pushes the shape into the array
}
}
function drawShapes() {
var i;
for (i = 0; i < numShapes; i++) {
ctx.fillStyle = shapes[i].color;
ctx.beginPath();
ctx.arc(shapes[i].x, shapes[i].y, shapes[i].rad, 0, 2 * Math.PI, false);
ctx.closePath();
ctx.fill();
shapes[i].x += dx; // increases the X value of Shape
shapes[i].y += dy; // increases the Y value of Shape
// Boundary, but applies to all shapes as one shape
if (shapes[i].x < 0 || shapes[i].x > width) dx = -dx;
if (shapes[i].y < 0 || shapes[i].y > height) dy = -dy;
}
}
function drawScreen() {
//bg
ctx.fillStyle = '#EEEEEE';
ctx.fillRect(0, 0, width, height);
//Box
ctx.strokeStyle = '#000000';
ctx.strokeRect(1, 1, width - 2, height - 2);
}
canvas {
border: 1px solid #333;
}
<body onLoad="init();">
<div class="container container-main">
<div class="container-canvas">
<canvas id="canvas" width="800" height="600">
This is my fallback content.
</canvas>
</div>
</div>
</body>
Your dx and dy are globals, they should be unique for each ball object that you are simulating. Either clear them to 0 in your rendering loop (draw) or actually implement a ball object/class to hold variables unique to that object.
When you do your collision detection you change dx and dy which then persists to the next ball object as they are global.
Your fiddle, edited to add local dx and dy per shape: https://jsfiddle.net/a9b3rm5u/3/
tempDx = Math.random()*5; // random DX value
tempDy = Math.random()*5; // random DY value
shapes[i].x+=shapes[i].dx;// increases the X value of Shape
shapes[i].y+=shapes[i].dy;// increases the Y value of Shape
if( shapes[i].x<0 || shapes[i].x>width) shapes[i].dx= - shapes[i].dx;
if( shapes[i].y<0 || shapes[i].y>height) shapes[i].dy= -shapes[i].dy;
I'm trying to draw a row of bricks
the bricks should have random lengths
the bricks should not extend out of the canvas.
the canvas length is 400 and I want there to be padding between the row and the canvas on each side of 10 px
there should be 10 pixels of padding between each brick (or better since a user could put in a large number like 35 you could make the padding smaller like 2 px [optional])
In this bad code you see that I attempted to create 7 bricks drawRow(7). They have random sizes but they are not proportional to the width of the canvas minus the canvas padding. and they don't have 10 px padding between the bricks. This is kind of a math problem I cant figure out the proportions maybe. Any help would be appreciates.
$(function(){
var canvas = $("canvas")[0],
context = canvas.getContext("2d"),
cWidth = canvas.width,
brickHeight = 20,
canvasPadding = 10,
lengthOfRow = cWidth - 20 *2,
width = 0,
x = canvasPadding,
brickPadding = 10;
function drawBrick(x,y,w,h){
context.beginPath();
context.rect(x,y, w, h)
context.fillStyle = "red";
context.fill()
context.strokeStyle = "black"
context.stroke()
context.closePath()
}
function drawRow(num){
// width = 50;
for(var i = 0; i < num; i ++){
if(x + width < lengthOfRow + width){
// x= x + width + brickPadding;
var numOfBlocks = lengthOfRow / num;
width = Math.floor(Math.random() * ((numOfBlocks + 10 - (numOfBlocks) + 1) + (numOfBlocks -10) ))
x = i * (width + width /brickPadding)
drawBrick(x +canvasPadding ,0,width * 2,brickHeight)
// width += width;
// x += x
console.log(x, " ", width, " numblock ", numOfBlocks)
}
}
}
drawRow(7)
})
background: #ece;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>
Your drawRow() function is using a single loop to compute the random brick widths and to draw the bricks. But there is no way to compute an individual brick width until you know the ratio of the individual brick width to the sum of the all the brick widths. I would suggest using two loops. In the first loop, compute a random number for each brick and sum these random numbers. You can now compute the ratio of an individual brick width to the sum of the brick widths (e.g. brick's random number divided by sum of random numbers). In the second loop, compute the brick widths and draw the bricks. An individual's brick width equals the sum of all brick widths times the brick's ratio to the sum. The sum of all brick widths is the canvas width minus sum of canvas padding minus sum of brick padding. For example, the code might look something like...
$(function() {
var canvas = $("canvas")[0],
context = canvas.getContext("2d"),
canvasWidth = canvas.width,
canvasPadding = 10,
brickPadding = 10,
brickY = 0,
brickHeight = 20;
function drawBrick(x, y, w, h) {
context.rect(x, y, w, h)
context.fillStyle = "red";
context.fill()
context.strokeStyle = "black"
context.stroke()
}
function drawRow(num){
var brickX = canvasPadding;
var brickRandom = [];
var sumBrickRandom = 0;
var sumBrickWidths = canvasWidth - 2 * canvasPadding - (num - 1) * brickPadding;
for (var i = 0; i < num; i++) {
var randomNumber = 1 + 3 * Math.random();
brickRandom[i] = randomNumber;
sumBrickRandom += randomNumber;
}
for(var i = 0; i < num; i++) {
var brickWidth = sumBrickWidths * brickRandom[i] / sumBrickRandom;
drawBrick(brickX, brickY, brickWidth, brickHeight);
brickX += brickWidth + brickPadding;
}
}
drawRow(7);
});
Note that I generated random numbers between 1 and 4. This ensures that the largest width is no more that 4 times the smallest width. This avoids cases where the smallest width could be near zero.
This is a matrix-like animation and it works perfectly when tested on Firefox, but on Chrome and IE only the buttons are displayed. It has something to do with the functions for changing color, but actually any extra function i include in the code will make the whole thing not work.
var c = document.getElementById("c");
var ctx = c.getContext("2d");
var color = "blue";
// setting canvas dimension
c.height = 500;
c.width = 500;
//the numbers that will be printed out
var numbers = "0123456789";
//create an array of single numbers
numbers = numbers.split("");
var font_size = 12;
var columns = c.width/font_size; //number of columns for the rain
//create an array of drops - one per column
var matrix = [];
for(var x = 0; x < columns; x++)
matrix[x] = c.height/font_size+1;
//functions to change the colour of the matrix
function setBlue() color = "blue";
function setGreen() color = "green";
function setRed() color = "red";
//drawing the matrix
function draw() {
//black background with transparency so that the drops leave trail
ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
ctx.fillRect(0, 0, c.width, c.height);
if (color == "blue"){
ctx.fillStyle = "#00ffd2";
}else if (color == "green"){
ctx.fillStyle = "#0f0";
}else if (color == "red"){
ctx.fillStyle = "#ff0008";
}
ctx.font = font_size + "px papyrus";
//loop the drawing
for(var i = 0; i < matrix.length; i++)
{
//print random character
var text = numbers[Math.floor(Math.random()*numbers.length)];
ctx.fillText(text, i*font_size, matrix[i]*font_size);
//add randomness and send the character to the top of the screen
if(matrix[i]*font_size > c.height && Math.random() > 0.95)
matrix[i] = 0;
matrix[i]++;
}
}
setInterval(draw, 30);
Oops! Your function definitions are malformed...
Try this:
//functions to change the colour of the matrix
function setBlue(){ color = "blue";}
function setGreen(){ color = "green";}
function setRed(){ color = "red";}
Good luck with your project!