Canvas particles arch - javascript

It's about Cavas animation.
How do I make my particles are not cubes but circles?
Codepen Link
CSS:
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
canvas {
position: absolute;
width: 100%;
height: 100%;
background:#000000;
}
JS
var cvs = document.createElement('canvas'),
context = cvs.getContext("2d");
document.body.appendChild(cvs);
var numDots = 300,
n = numDots,
currDot,
maxRad = 900,
minRad = 100,
radDiff = maxRad-minRad,
dots = [],
PI = Math.PI,
centerPt = {x:0, y:0};
resizeHandler();
window.onresize = resizeHandler;
while(n--){
currDot = {};
currDot.radius = minRad+Math.random()*radDiff;
currDot.radiusV = 0+Math.random()*200,
currDot.radiusVS = (0.5-Math.random()*10)*0.00000005,
currDot.radiusVP = Math.random()*0,
currDot.ang = (1-Math.random()*2)*PI;
currDot.speed = (1+Math.random()*0);
//currDot.speed = 1-Math.round(Math.random())*2;
//currDot.speed = 1;
currDot.intensityP = Math.random()*PI;
currDot.intensityS = Math.random()*0.0005;
currDot.intensityO = 64+Math.round(Math.random()*64);
currDot.intensityV = Math.min(Math.random()*255, currDot.intensityO);
currDot.intensity = Math.round(Math.random()*255);
currDot.fillColor = "rgb("+currDot.intensity+","+currDot.intensity+","+currDot.intensity+")";
dots.push(currDot);
}
function drawPoints(){
n = numDots;
var _centerPt = centerPt,
_context = context,
dX = 0,
dY = 0;
_context.clearRect(0, 0, cvs.width, cvs.height);
var radDiff;
//draw dots
while(n--){
currDot = dots[n];
currDot.radiusVP += currDot.radiusVS;
radDiff = currDot.radius+Math.sin(currDot.radiusVP)*currDot.radiusV;
dX = _centerPt.x+Math.sin(currDot.ang)*radDiff;
dY = _centerPt.y+Math.cos(currDot.ang)*radDiff;
//currDot.ang += currDot.speed;
currDot.ang += currDot.speed*radDiff/400000;
currDot.intensityP += currDot.intensityS;
currDot.intensity = Math.round(currDot.intensityO+Math.sin(currDot.intensityP)*currDot.intensityV);
//console.log(currDot);
_context.fillStyle= "rgb("+currDot.intensity+","+currDot.intensity+","+currDot.intensity+")";;
_context.fillRect(dX, dY, 2, 2);
} //draw dot
window.requestAnimationFrame(drawPoints);
}
function resizeHandler(){
var box = cvs.getBoundingClientRect();
var w = box.width;
var h = box.height;
cvs.width = w;
cvs.height = h;
centerPt.x = Math.round(w/2);
centerPt.y = Math.round(h/2);
}
drawPoints();
Thanks everyone

EDIT
You're not configuring your colors correctly! When you render circles you set a fillStyle and a strokeStyle, and since you aren't setting a strokeStyle, the circles aren't rendering (see canvas with white background)
After beginPath(), before the .stroke() command, you need to set the stroke:
_context.strokeStyle= "rgb("+currDot.intensity+","+currDot.intensity+","+currDot.intensity+")";
You're already rendering squares, which means that you have generated a point for each object to occupy. Now pick a radius, probably something small like 1 or 2, and use _context.arc in place of _context.fillRect, like so:
_context.beginPath();
_context.arc(dX, dY, 2, 0, 2 * Math.PI);
_context.stroke();
This will make replaces the squares with circles of a radius of 2 (as specified by the 3rd parameter)

I'm not sure what you want, but if you want to draw a circle on an HTML5 canvas, this is the code:
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI);
context.stroke();
Where:
center(X,Y) is your initial point on the canvas (which from looking at your code will be random)
radius is self-explanatory, you probably want it small for your case
0 is the starting angle for the arch
(2*Math.PI) is the end angle of the arch (in terms of Pi), which will complete a full circle
Hopefully this helped!

Related

HTML Canvas Trying to create an animated chain of rectangle with slight delay/distance between them

I am trying to create multiple animated rectangles using Html Canvas with requestAnimationFrame. As for now, I managed to do exactly what I wanted with only one animated rectangle, but I can't find out how to create more rectangles that would simply be in line and follow each other with an equal distance.
Also, there's a random data (I, II or III) inside each rectangle.
Here's my actual code:
//Referencing canvas
var canvas = document.getElementById("my-canvas");
var ctx = canvas.getContext("2d");
//Make Canvas fullscreen and responsive
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize, false); resize();
//FPS
var framesPerSecond = 60;
//Default Y pos to center;
var yPos = canvas.height / 2;
//Default X pos offset
var xPos = -150;
//Speed (increment)
var speed = 2;
//Our array to store rectangles objects
var rectangles = [] ;
//Dynamic Number from database
var quote = ["I", "II", "III"];
//Random number for testing purpose
var rand = quote[Math.floor(Math.random() * quote.length)];
//Draw Rectangle
function drawRectangle () {
setTimeout(function() {
requestAnimationFrame(drawRectangle);
ctx.clearRect(0, 0, canvas.width, canvas.height);
//Background color
ctx.fillStyle = "yellow";
//Position, size.
var rectWidth = 70;
var rectHeigth = 55;
ctx.fillRect(xPos,yPos,rectWidth,rectHeigth);
ctx.font = "32px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "black";
//Data Layer
var dataLayer = ctx.fillText(rand,xPos+(rectWidth/2),yPos+(rectHeigth/2));
xPos += speed;
//Infinite loop for test
if (xPos > 1080) {
xPos = -150;
}
}, 1000 / framesPerSecond);
}
drawRectangle ();
canvas {background-color: #131217}
body { margin: 0; overflow: hidden; }
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Moving Blocks</title>
<style>
canvas {background-color: #131217}
body { margin: 0; overflow: hidden; }
</style>
</head>
<body>
<canvas id="my-canvas"></canvas>
</body>
</html>
Animating arrays of objects.
For animations you are best of using a single render function that renders all the objects once a frame, rather than create a separate render frame per object.
As for the squares there are many ways that you can get them to do what you want. It is a little difficult to answer as what you want is not completely clear.
This answer will use a rectangle object that has everything needed to be rendered and move. The rectangles will be kept in an array and the main render function will update and render each rectangle in turn.
There will be a spawn function that creates rectangles untill the limit has been reached.
// constants up the top
const quote = ["I", "II", "III"];
// function selects a random Item from an array
const randItem = (array) => array[(Math.random() * array.length) | 0];
// array to hold all rectangles
const rectangles = [];
var maxRectangles = 20;
const spawnRate = 50; // number of frames between spawns
var spawnCountdown = spawnRate;
//Referencing canvas
const ctx = canvas.getContext("2d");
var w, h; // global canvas width and height.
resizeCanvas(); // size the canvas to fit the page
requestAnimationFrame(mainLoop); // this will start when all code below has been run
function mainLoop() {
// resize in the rendering frame as using the resize
// event has some issuse and this is more efficient.
if (w !== innerWidth || h !== innerHeight) {
resizeCanvas();
}
ctx.clearRect(0, 0, w, h);
spawnRectangle(); // spawns rectangles
updateAllRectangles(); // moves all active rectangles
drawAllRectangles(); // I will let you gues what this does... :P
requestAnimationFrame(mainLoop);
}
function resizeCanvas() {
w = canvas.width = innerWidth;
h = canvas.height = innerHeight;
// and reset any canvas constants
ctx.font = "32px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
}
// function to spawn a rectangle
function spawnRectangle() {
if (rectangles.length < maxRectangles) {
if (spawnCountdown) {
spawnCountdown -= 1;
} else {
rectangles.push(
createRectangle({
y: canvas.height / 2, // set at center
text: randItem(quote),
dx: 2, // set the x speed
})
);
spawnCountdown = spawnRate;
}
}
}
// define the default rectangle
const rectangle = {
x: -40, // this is the center of the rectangle
y: 0,
dx: 0, // delta x and y are the movement per frame
dy: 0,
w: 70, // size
h: 55,
color: "yellow",
text: null,
textColor: "black",
draw() { // function to draw this rectangle
ctx.fillStyle = this.color;
ctx.fillRect(this.x - this.w / 2, this.y - this.h / 2, this.w, this.h);
ctx.fillStyle = this.textColor;
ctx.fillText(this.text, this.x, this.y);
},
update() { // moves the rectangle
this.x += this.dx;
this.y += this.dy;
if (this.x > canvas.width + this.w / 2) {
this.x = -this.w / 2;
// if the rectangle makes it to the other
// side befor all rectangles are spawnd
// then reduce the number so you dont get any
// overlap
if (rectangles.length < maxRectangles) {
maxRectangles = rectangles.length;
}
}
}
}
// creats a new rectangle. Setting can hold any unique
// data for the rectangle
function createRectangle(settings) {
return Object.assign({}, rectangle, settings);
}
function updateAllRectangles() {
var i;
for (i = 0; i < rectangles.length; i++) {
rectangles[i].update();
}
}
function drawAllRectangles() {
var i;
for (i = 0; i < rectangles.length; i++) {
rectangles[i].draw();
}
}
canvas {
position: absolute;
top: 0px;
left: 0px;
background-color: #131217;
}
body {
margin: 0;
overflow: hidden;
}
<canvas id="canvas"></canvas>

Sprite Animation clearRect alternative?

WHAT? I am attempting to use canvas and JavaScript to display an animation on top of a grid which also must be drawn using JavaScript. https://jsfiddle.net/cp1wqeeg/6/
PROBLEM! To remove the previous frames of the animation I have used clearRect(). This however breaks my grid which I do not want :(
JSFiddle: https://jsfiddle.net/cp1wqeeg/5
ctx.clearRect(50, 100, width, height);
QUESTION How can I remove the previous frames of my animation without breaking the grid behind my sprite?
The common action here is to clear all and redraw everything.
But it may become cumbersome if e.g in your case, your background doesn't change.
In this case, an simple solution, is to use offscreen canvases, that will act as layers.
First you draw you grid on this off-screen canvas in the init phase.
Then in your loop, you just draw your offscreen canvas on the main context, with the drawImage method.
var canvas = document.getElementById("myCanvas"),
ctx = canvas.getContext("2d"),
fov = 300,
viewDist = 5,
w = canvas.width / 2,
h = canvas.height / 2,
// here we create an offscreen canvas for the grid only
gridCtx = canvas.cloneNode().getContext('2d'),
angle = 0,
i, p1, p2,
grid = 5;
function initGrid(){
/// create vertical lines on the off-screen canvas
for(i = -grid; i <= grid; i++) {
p1 = rotateX(i, -grid);
p2 = rotateX(i, grid);
gridCtx.moveTo(p1[0], p1[1]);
gridCtx.lineTo(p2[0], p2[1]);
i++;
}
/// create horizontal lines
for(i = -grid; i <= grid; i++) {
p1 = rotateX(-grid, i);
p2 = rotateX(grid, i);
gridCtx.moveTo(p1[0], p1[1]);
gridCtx.lineTo(p2[0], p2[1]);
}
gridCtx.stroke();
}
function rotateX(x, y) {
var rd, ca, sa, ry, rz, f;
rd = angle * Math.PI / 180;
ca = Math.cos(rd);
sa = Math.sin(rd);
ry = y * ca;
rz = y * sa;
f = fov / (viewDist + rz);
x = x * f + w;
y = ry * f + h;
return [x, y];
}
initGrid();
var width = 200,
height = 200,
frames = 2,
currentFrame = 0,
imageSprite = new Image()
imageSprite.src = 'https://s27.postimg.org/eg1cjz6cz/sprite.png';
var drawSprite = function(){
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.drawImage(gridCtx.canvas, 0,0); // now draw our grid canvas
ctx.drawImage(imageSprite, 0, height * currentFrame, width, height, 50, 100, width, height);
if (currentFrame == frames) {
currentFrame = 0;
} else {
currentFrame++;
}
}
setInterval(drawSprite, 500);
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #c3c3c3;"></canvas>

Is there any way to draw a "streak" (fading "digital phosphor" effect) on HTML5 canvas?

I want to draw a moving dot on an HTML5 canvas that follows a complicated trajectory. This I know how to do; see, for example, the Lorenz attractor as implemented below. But with small dots it is hard to follow. Is there a way to add a blurry trail behind a dot? I can keep past history of drawn points, I just don't know how to make them fade.
In technical terms, I suppose this would be a polyline/curve where the opacity/width/color changes smoothly along the curve. I know how to draw polylines (and can figure out the Bezier curve stuff if I need to), but I don't know how to apply a smooth gradient along a path.
(Digital oscilloscopes solved this "problem" by having a Digital Phosphor Oscilloscope effect where the scope emulated the old analog "phosphor" effect: areas hit by the scope's "beam" would take a while to fade.)
<!DOCTYPE html>
<html>
<head><script type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
var x = 1, y = 0, z = 0, t=0;
function onTick(timestamp)
{
var ctx = document.getElementById('canvas').getContext('2d');
ctx.clearRect(0, 0, 300, 300);
ctx.fillStyle = 'black';
var cx = 150;
var cy = 150;
var r = 5;
var now = timestamp * 0.001;
var dt = now - t;
t = now;
if (dt > 0.1)
dt = 0.1;
// Lorenz attractor
var sigma = 10, rho=28, beta=8/3;
var dxdt = sigma*(y-x);
var dydt = x*(rho-z)-y;
var dzdt = x*y-beta*z;
x += dt*dxdt;
y += dt*dydt;
z += dt*dzdt;
var drawx = cx + r*x;
var drawy = cy + r*y;
var rdot = 2;
ctx.beginPath();
ctx.arc(drawx, drawy, rdot, 0, 2 * Math.PI, true);
ctx.fill();
requestAnimationFrame(onTick);
}
requestAnimationFrame(onTick);
});
</script></head>
<body>
<canvas id="canvas" width="300" height="300"/>
</body>
</html>
Instead of clearing the rectangle each frame, just paint it in an alpha channel to save those previous dots momentarily. I replaced your clearRect with fillRect the fillStyle is white see-through.
Keep in ming you can adjust the alpha channel, this will make the dot stay for longer/short duration. In my code this is the 0.04 in the ctx.fillStyle = "rgba(255, 255, 255, 0.04)";. I just adjusted it lower to make those traces stay for a longer time.
document.addEventListener("DOMContentLoaded", function(event) {
var x = 1, y = 0, z = 0, t=0;
function onTick(timestamp)
{
var ctx = document.getElementById('canvas').getContext('2d');
//ctx.clearRect(0, 0, 300, 300);
ctx.fillStyle = "rgba(255, 255, 255, 0.04)";
ctx.fillRect(0, 0, 300, 300);
ctx.fillStyle = 'black';
var cx = 150;
var cy = 150;
var r = 5;
var now = timestamp * 0.001;
var dt = now - t;
t = now;
if (dt > 0.1)
dt = 0.1;
// Lorenz attractor
var sigma = 10, rho=28, beta=8/3;
var dxdt = sigma*(y-x);
var dydt = x*(rho-z)-y;
var dzdt = x*y-beta*z;
x += dt*dxdt;
y += dt*dydt;
z += dt*dzdt;
var drawx = cx + r*x;
var drawy = cy + r*y;
var rdot = 2;
ctx.beginPath();
ctx.arc(drawx, drawy, rdot, 0, 2 * Math.PI, true);
ctx.fill();
requestAnimationFrame(onTick);
}
requestAnimationFrame(onTick);
});
canvas {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: -1;
}
<canvas id="canvas"></canvas>
Classic trick was to draw a polyline instead single dot, altering color and/or opacity per each vertice of polyline, the brightest would be the furthest along traectory

Scribing a cicrular line in Canvas

I am attempting to mouse-drag a dot around the outer perimeter of a large circle and have that dot appear to scribe a thick line around the outer perimeter behind itself. I can get everything to work except scribing the outer line behind the dot. I have researched many ideas and tried many of my own but the line still produces "spotted" results. Here is an image to show what I'm attempting.
MounseDrag Scribed Line
Thank you for taking the time to read my question. :-)
<script type="text/javascript">
var canvas1 = document.getElementById("canvas1"),
canvas2 = document.getElementById("canvas2"),
c1 = canvas1.getContext("2d"),
c2 = canvas2.getContext("2d"),
dot = 7,
started = false,
width = 350,
height = 350,
radians = 0,
cRad = 165, // Circle Radius
cord = {mX:0, mY:0, csX:0, snY:0, x:0, y:0},
init = function(){
cord.mX = 0;
cord.mY = 0;
cord.csX = width /2 + cRad;
cord.snY = height /2;
cord.x = width /2;
cord.y = height /2;
};
init();
canvas1.width = width;
canvas1.height = height;
canvas2.width = width;
canvas2.height = height;
canvas1.addEventListener("mousemove", function(event) {
cord.mX = event.clientX - canvas1.offsetLeft;
cord.mY = event.clientY - canvas1.offsetTop;
});
canvas1.addEventListener("mousedown", function(event) {
if (started) {
started = false;
} else {
started = true;
render();
};
});
function update() {
radians = Math.atan2(cord.mY - width/2, cord.mX - height/2);
cord.csX = width/2 - Math.cos(radians) * cRad * -1;
cord.snY = height/2 - Math.sin(radians) * cRad * -1;
};
function outerTheta() {
c2.beginPath();
c2.arc(cord.csX, cord.snY, 3, 0, Math.PI * 2);
c2.closePath();
c2.fillStyle = "#000";
c2.fill();
};
function render() {
c1.clearRect(0, 0, width, height);
c1.beginPath();
c1.moveTo(cord.x, cord.y);
c1.lineTo(cord.csX, cord.snY);
c1.lineWidth = 3;
c1.strokeStyle = "#000";
c1.stroke();
c1.beginPath(); //<---------------------------------- Drag-Dot
c1.arc(cord.csX, cord.snY, dot, 0, Math.PI * 2);
c1.closePath();
c1.fillStyle = "#000";
c1.fill();
if(started){
update();
outerTheta();
requestAnimationFrame(render);
};
};
render();
</script>
The browser is not able to cycle the animation as quickly as the mouse is moving. If you move the mouse slowly, then the dots that are drawn in each animation cycle overlap and the circle has a solid line. If you move the mouse quickly, then the dots do not overlap and you get "spotting".
If you pay close attention to the way drawing programs work, you will see that the "pen" tool draws a continuous line. If you move the mouse quickly while using the tool, the continuous line is made up of line segments that stretch from each point that the computer was able to capture while your mouse was moving quickly.
I modified your program so that a line segment stretches between each captured point during the animation cycle:
https://jsfiddle.net/17hvw5pp
var canvas1 = document.getElementById("canvas1"),
canvas2 = document.getElementById("canvas2"),
c1 = canvas1.getContext("2d"),
c2 = canvas2.getContext("2d"),
dot = 7,
started = false,
width = 350,
height = 350,
radians = 0,
cRad = 165, // Circle Radius
cord = {mX:0, mY:0, csX:0, snY:0, x:0, y:0},
init = function(){
cord.mX = 0;
cord.mY = 0;
cord.csX = width /2 + cRad;
cord.snY = height /2;
cord.lastCSX = cord.csX;
cord.lastSNY = cord.snY;
cord.x = width /2;
cord.y = height /2;
};
canvas1.style.position="absolute";
canvas2.style.position="absolute";
init();
canvas1.width = width;
canvas1.height = height;
canvas2.width = width;
canvas2.height = height;
canvas1.addEventListener("mousemove", function(event) {
cord.mX = event.clientX - canvas1.offsetLeft;
cord.mY = event.clientY - canvas1.offsetTop;
});
canvas1.addEventListener("mousedown", function(event) {
if (started) {
started = false;
} else {
started = true;
render();
};
});
function update() {
radians = Math.atan2(cord.mY - width/2, cord.mX - height/2);
cord.csX = width/2 - Math.cos(radians) * cRad * -1;
cord.snY = height/2 - Math.sin(radians) * cRad * -1;
};
function outerTheta() {
//draw a line from the last known coordinate to the new known coordinate
c2.beginPath();
c2.moveTo(cord.lastCSX, cord.lastSNY);
c2.lineTo(cord.csX, cord.snY);
c2.lineWidth=5;
c2.strokeStyle="#000";
c2.stroke();
cord.lastCSX = cord.csX;
cord.lastSNY = cord.snY;
c2.beginPath();
c2.arc(cord.csX, cord.snY, 3, 0, Math.PI * 2);
c2.closePath();
c2.fillStyle = "#000";
c2.fill();
};
function render() {
c1.clearRect(0, 0, width, height);
c1.beginPath();
c1.moveTo(cord.x, cord.y);
c1.lineTo(cord.csX, cord.snY);
c1.lineWidth = 3;
c1.strokeStyle = "#000";
c1.stroke();
c1.beginPath(); //<---------------------------------- Drag-Dot
c1.arc(cord.csX, cord.snY, dot, 0, Math.PI * 2);
c1.closePath();
c1.fillStyle = "#000";
c1.fill();
if(started){
update();
outerTheta();
requestAnimationFrame(render);
};
};
render();
This works better, but not perfectly: If you move the mouse quickly, the line segment will become a chord across the circle and this ruins the effect.
I attempted to modify the program to draw an arc between the two known points:
https://jsfiddle.net/17hvw5pp/1/
You can see that this implementation is also not ideal because the arc function becomes confused about which direction to draw the partial circle based on just two radians coordinates. Using quaternion math will solve this problem for you.
https://en.wikipedia.org/wiki/Quaternion
But that may be more complication that you want to introduce into this project.

HTML5 context.drawImage() crop is not working after calling context.translate()

> What I want here are 10 canvases, 5 on the top and 5 on the bottom, each with width 17% and height 25.5%, and appropriate spacing between (see bottom image). Each image drawn on a canvas corresponds to the same area of the full image (see top image). This is kind of like a destination-in, but this is really just a crop of the full image onto smaller canvases using context.translate and context.drawImage(). Please see the two lines inside the innermost for loop with comments that deal wtih context.translate and context.drawImage for clue as to what might be going on.
Please see the attached image of what I am trying to achieve with html5 context.translate() and context.drawImage().
Any help greatly appreciated. Thank you.
//get parent's width and height
var parent = document.getElementById("parent");
var parentWidth = parent.offsetWidth;
var parentHeight = parent.offsetHeight;
//get below canvas
var belowCanvas = document.getElementById('belowCanvas');
var belowCtx = belowCanvas.getContext('2d');
//create temporary canvas
var tmpCanvas = document.createElement('canvas');
var tmpCtx = tmpCanvas.getContext('2d');
//initialize width and height of temporary canvas and below canvas to equal parent
tmpCanvas.width = belowCanvas.width = parentWidth;
tmpCanvas.height = belowCanvas.height = parentHeight;
//draw below canvas in black for visual aid of how things are cropped in above canvases
belowCtx.rect(0,0,parentWidth,parentHeight);
belowCtx.fillStyle = 'black';
belowCtx.fill();
//draw temporary canvas
var centerX = parentWidth/4;
var centerY = parentHeight/4;
var radius = parentHeight/4;
tmpCtx.rect(0,0,parentWidth,parentHeight);
tmpCtx.fillStyle = 'blue';
tmpCtx.fill();
tmpCtx.beginPath();
tmpCtx.arc(centerX, centerY, radius*1.5, 0, 2 * Math.PI, false);
tmpCtx.fillStyle = 'green';
tmpCtx.fill();
tmpCtx.lineWidth = 2
tmpCtx.strokeStyle = '#003300';
tmpCtx.stroke();
tmpCtx.beginPath();
tmpCtx.arc(parentWidth - centerX, centerY, radius*1.5, 0, 2 * Math.PI, false);
tmpCtx.fillStyle = 'purple';
tmpCtx.fill();
tmpCtx.lineWidth = 2
tmpCtx.strokeStyle = '#003300';
tmpCtx.stroke();
tmpCtx.beginPath();
tmpCtx.arc(parentWidth/2, parentHeight/2, radius*2, 0, 2 * Math.PI, false);
tmpCtx.fillStyle = 'white';
tmpCtx.fill();
tmpCtx.lineWidth = 2
tmpCtx.strokeStyle = '#003300';
tmpCtx.stroke();
tmpCtx.beginPath();
tmpCtx.arc(centerX, parentHeight - centerY, radius*1.5, 0, 2 * Math.PI, false);
tmpCtx.fillStyle = 'red';
tmpCtx.fill();
tmpCtx.lineWidth = 2
tmpCtx.strokeStyle = '#003300';
tmpCtx.stroke();
tmpCtx.beginPath();
tmpCtx.arc(parentWidth - centerX, parentHeight - centerY, radius*1.5, 0, 2 * Math.PI, false);
tmpCtx.fillStyle = 'yellow';
tmpCtx.fill();
tmpCtx.lineWidth = 2
tmpCtx.strokeStyle = '#003300';
tmpCtx.stroke();
//set spacing between canvases
var horizontalSpacing = parentWidth*0.025
var verticalSpacing = parentWidth*0.03
//initialize canvases width and height
var widthCanvas = parentWidth*0.17;
var heightCanvas = parentWidth*0.255;
var xStart;
var yStart = verticalSpacing;
for(var i=0; i< 2; i++)
{
xStart = horizontalSpacing;
for(var j=0; j < 5; j++)
{
//get specific destinationInCanvas by id and its respective context
var canvas = document.getElementById("canvas" + ((i*5)+j));
var ctx = canvas.getContext('2d');
canvas.width = widthCanvas;
canvas.height = heightCanvas;
/***!!!
Problem next two lines
if the next line is commented out, each canvas is drawn, but of course not translated; so you only see last canvas, canvas9
if the next line is NOT commented out, only canvas0 is drawn and translated, the rest of the canvases 1-9 are not drawn*/
ctx.translate(xStart, yStart);//comment out this line to see effect
ctx.drawImage(tmpCanvas, xStart, yStart, widthCanvas, heightCanvas, 0, 0, widthCanvas, heightCanvas);
xStart += (horizontalSpacing + widthCanvas);
}
yStart += (verticalSpacing + heightCanvas);
}
#parent {
width: 1000px;
height: 600px;
}
#belowCanvas{
position: absolute;
z-index:-1;
}
#canvas0 {
position: absolute;
z-index:0;
}
#canvas1 {
position: absolute;
z-index:1;
}
#canvas2 {
position: absolute;
z-index:2;
}
#canvas3 {
position: absolute;
z-index:3;
}
#canvas4 {
position: absolute;
z-index:4;
}
#canvas5 {
position: absolute;
z-index:5;
}
#canvas6 {
position: absolute;
z-index:6;
}
#canvas7 {
position: absolute;
z-index:7;
}
#canvas8 {
position: absolute;
z-index:8;
}
#canvas9 {
position: absolute;
z-index:9;
}
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style-test.css">
</head>
<body>
<div id="parent">
<canvas id="belowCanvas"></canvas>
<canvas id="canvas0"></canvas>
<canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas>
<canvas id="canvas3"></canvas>
<canvas id="canvas4"></canvas>
<canvas id="canvas5"></canvas>
<canvas id="canvas6"></canvas>
<canvas id="canvas7"></canvas>
<canvas id="canvas8"></canvas>
<canvas id="canvas9"></canvas>
</div>
<script type="text/javascript" src="script-test.js"></script>
</body>
</html>
You've got some problem while incrementing yStart and xStart:
You should do so by multiplying heightCanvas/widthCanvas by i/j state of your loop.
Also, in your CSS, if you do add position:absolute without setting marginsor left/top values, your canvases will get in stack and you'll only be able to see last one. Only add position:absolute to your #belowCanvas. Actually, if you only need a black background, you might consider wrapping all your canvases into a div with background:black;. It would be easier to align your elements then.
Finally, you won't need ctx.translate since ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); already does the translation on source image (sx, sy).
Edit after I talked to OP on chat:
Actually, he wanted the canvases' holes to fit to a layout, created with a context.globalCompositeOperation='destination-out';.
I did remove the belowCanvas and replaced it with a divwhich background is set to black via CSS.
To prevent ghost margins with your small canvases, you'll need to set vertical-align:bottom; float: left since browsers display canvas element as a character.
I refactored the code to create the small canvases programmatically, this will allow you to change the grid format.
For the example, I added click listener to show/hide layers:
left click triggers destinationOutLayer and right click triggers small canvases layer.
//get parent's width and height
var parent = document.getElementById("parent");
var parentWidth = parent.offsetWidth;
var parentHeight = parent.offsetHeight;
var destinationOutCanvas = document.getElementById('destinationOutCanvas');
var destinationOutCtx = destinationOutCanvas.getContext('2d');
//create temporary canvas
var tmpCanvas = document.createElement('canvas');
var tmpCtx = tmpCanvas.getContext('2d');
//initialize width and height of temporary canvas and destOut canvas to equal parent
destinationOutCanvas.width = tmpCanvas.width = parentWidth;
destinationOutCanvas.height = tmpCanvas.height = parentHeight;
//draw temporary canvas
var centerX = parentWidth / 4;
var centerY = parentHeight / 4;
var radius = parentHeight / 4;
tmpCtx.rect(0, 0, parentWidth, parentHeight);
tmpCtx.fillStyle = 'blue';
tmpCtx.fill();
tmpCtx.beginPath();
tmpCtx.arc(centerX, centerY, radius * 1.5, 0, 2 * Math.PI, false);
tmpCtx.fillStyle = 'green';
tmpCtx.fill();
tmpCtx.lineWidth = 2
tmpCtx.strokeStyle = '#003300';
tmpCtx.stroke();
tmpCtx.beginPath();
tmpCtx.arc(parentWidth - centerX, centerY, radius * 1.5, 0, 2 * Math.PI, false);
tmpCtx.fillStyle = 'purple';
tmpCtx.fill();
tmpCtx.lineWidth = 2
tmpCtx.strokeStyle = '#003300';
tmpCtx.stroke();
tmpCtx.beginPath();
tmpCtx.arc(parentWidth / 2, parentHeight / 2, radius * 2, 0, 2 * Math.PI, false);
tmpCtx.fillStyle = 'white';
tmpCtx.fill();
tmpCtx.lineWidth = 2
tmpCtx.strokeStyle = '#003300';
tmpCtx.stroke();
tmpCtx.beginPath();
tmpCtx.arc(centerX, parentHeight - centerY, radius * 1.5, 0, 2 * Math.PI, false);
tmpCtx.fillStyle = 'red';
tmpCtx.fill();
tmpCtx.lineWidth = 2
tmpCtx.strokeStyle = '#003300';
tmpCtx.stroke();
tmpCtx.beginPath();
tmpCtx.arc(parentWidth - centerX, parentHeight - centerY, radius * 1.5, 0, 2 * Math.PI, false);
tmpCtx.fillStyle = 'yellow';
tmpCtx.fill();
tmpCtx.lineWidth = 2
tmpCtx.strokeStyle = '#003300';
tmpCtx.stroke();
//set the grid
//Strange bugs can occur with some fractions (e.g 18 rows);
var rows = 5;
var lines = 2;
//set spacing between canvases
//rounded to the nearest even number, since we divide this by 2 below
var horizontalSpacing = 2*Math.round((parentWidth*0.025)/2); // or whatever you want
var verticalSpacing = 2*Math.round((parentHeight*0.03)/2); // or whatever you want
//initialize canvases width and height
var widthCanvas = 2*(Math.round(parentWidth / rows)/2);
var heightCanvas = 2*(Math.round(parentHeight / lines)/2);
destinationOutCtx.drawImage(tmpCanvas, 0, 0);
destinationOutCtx.globalCompositeOperation = 'destination-out';
destinationOutCtx.fillStyle = 'orange';
for (var i = 0; i < lines; i++) {
for (var j = 0; j < rows; j++) {
//get specific destinationInCanvas by id and its respective context
//var canvas = document.getElementById("canvas" + ((i * rows) + j));
//create the canvases on the go
var canvas= document.createElement('canvas');
canvas.width = widthCanvas;
canvas.height = heightCanvas;
var ctx = canvas.getContext('2d');
//only needed for the demonstration toggler
canvas.className = "small";
//set the transform variables
var hS = horizontalSpacing/2,
vS = verticalSpacing/2,
xStart = (widthCanvas*j)+hS,
yStart = (heightCanvas*i)+vS,
cropedWidth = widthCanvas-hS*2,
cropedHeight = heightCanvas-hS*2;
ctx.drawImage(tmpCanvas, xStart, yStart, cropedWidth, cropedHeight, hS, vS, cropedWidth, cropedHeight);
destinationOutCtx.fillRect(xStart, yStart, cropedWidth, cropedHeight);
parent.appendChild(canvas);
}
}
//Toggle opacity with right and left click
destinationOutCanvas.addEventListener('click', function () {
if (this.style.opacity == 0) {
this.style.opacity = 1
} else {
this.style.opacity = 0
}
});
destinationOutCanvas.style.opacity = 1;
destinationOutCanvas.addEventListener('contextmenu', function (e) {
console.log('triggered');
e.preventDefault();
var smalls = document.querySelectorAll('.small');
console.log(smalls[0].style.opacity);
if (smalls[0].style.opacity == 0) {
for (i = 0; i < smalls.length; i++) {
smalls[i].style.opacity = 1
}
} else {
for (i = 0; i < smalls.length; i++) {
smalls[i].style.opacity = 0;
};
}
});
#parent {
width: 1000px !important;
height: 600px;
background:#000;
}
html, body {
margin:0
}
canvas {
vertical-align:bottom;
float: left
}
.destinationOutLayer, #tmp {
position: absolute;
z-index: 2;
top:0;
}
<div id="parent">
</div>
<canvas id="destinationOutCanvas" class="destinationOutLayer"></canvas>

Categories

Resources