How to resize Canvas Width on Viewport - javascript

I have a canvas demo that spawns some circles. I can't seem to figure out how to have it be responsive in setting either canvas.width or canvas.style.width (what's the difference here?) on a windows resize using window.innerWidth.
Code works fine but it rerenders strangely on smaller viewports. I tried adding this snippet of code at the bottom of my javascript file, but it broke animation.
// ADDING THESE LINES PREVENTS ANIMATION FROM RUNNING
if (window.innerWidth < 1000) {
canvas.width = window.innerWidth;
} else {
canvas.width = 1000;
}
var canvas = document.querySelector("canvas");
canvas.width = 1000;
canvas.height = 100;
var c = canvas.getContext("2d");
// Constructor Function (object blueprint)
function Circle(x, y, dx, dy, radius, counter) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.counter = counter;
this.draw = function() {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
c.strokeStyle = "white";
c.stroke();
c.fillStyle = "white";
c.fill();
};
this.update = function() {
if (this.y + this.radius > canvas.height) {
this.y = 0;
}
this.x += this.dx;
this.y += this.dy;
this.draw();
};
}
// Initialize array to store snow objects
var circleArray = [];
// Initialize objects with constructor
for (var i = 0; i < 50; i++) {
var radius = 1 + Math.random() * 5;
var x = Math.random() * canvas.width;
var y = 0 - Math.random() * 50; // start at top, render some circles off screen
var dx = (Math.random() - 0.5) * 2;
var dy = 0.5 + Math.random() * 0.5; // use gravity
circleArray.push(new Circle(x, y, dx, dy, radius, 0));
}
function animate() {
requestAnimationFrame(animate); // recurisvely run
c.clearRect(0, 0, innerWidth, innerHeight); // erases previously drawn content
for (var i = 0; i < circleArray.length; i++) {
circleArray[i].update();
}
// ADDING THESE LINES PREVENTS ANIMATION FROM RUNNING
//if (window.innerWidth < 1000) {
// canvas.width = window.innerWidth;
// } else {
// canvas.width = 1000;
// }
}
animate();
body {
background-color: grey;
display: flex;
justify-content: center;
}
.wrapper {
position: relative;
width: 1000px;
height: 110px;
min-height: 110px;
margin-top: 50vh;
}
.wrapper > * {
width: 1000px;
position: absolute;
}
canvas {
position: absolute;
}
img {
width: 100%;
height: 110px;
}
<div class="wrapper">
<img class="stars" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/stars.png
" alt="" />
<img class="tress" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/trees.png
" alt="" />
<img class="clouds" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/clouds.png" alt="" />
<canvas></canvas>
</div>

The difference between canvas.width and canvas.style.width is that canvas.width specifies the actual size of the canvas in pixels, while canvas.style.width stretches or compresses the canvas to the width you specify. This will set the canvas width to 300px:
canvas.width = 300;
This will also set the canvas width to 300px, but will stretch it until it reaches 600px:
canvas.width = 300;
canvas.style.width = "600px";
Using canvas.width is better practice.
For the animation to run you must first fix the canvas width. Putting the code that resizes the canvas at the beginning of the animate() function solves the problem:
var canvas = document.querySelector("canvas");
canvas.width = 1000;
canvas.height = 100;
var c = canvas.getContext("2d");
// Constructor Function (object blueprint)
function Circle(x, y, dx, dy, radius, counter) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.counter = counter;
this.draw = function() {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
c.strokeStyle = "white";
c.stroke();
c.fillStyle = "white";
c.fill();
};
this.update = function() {
if (this.y + this.radius > canvas.height) {
this.y = 0;
}
this.x += this.dx;
this.y += this.dy;
this.draw();
};
}
// Initialize array to store snow objects
var circleArray = [];
// Initialize objects with constructor
for (var i = 0; i < 50; i++) {
var radius = 1 + Math.random() * 5;
var x = Math.random() * canvas.width;
var y = 0 - Math.random() * 50; // start at top, render some circles off screen
var dx = (Math.random() - 0.5) * 2;
var dy = 0.5 + Math.random() * 0.5; // use gravity
circleArray.push(new Circle(x, y, dx, dy, radius, 0));
}
function animate() {
if (window.innerWidth < 1000) {
canvas.width = window.innerWidth;
} else {
canvas.width = 1000;
}
requestAnimationFrame(animate); // recurisvely run
c.clearRect(0, 0, innerWidth, innerHeight); // erases previously drawn content
for (var i = 0; i < circleArray.length; i++) {
circleArray[i].update();
}
}
animate();
body {
background-color: grey;
display: flex;
justify-content: center;
}
.wrapper {
position: relative;
width: 1000px;
height: 110px;
min-height: 110px;
margin-top: 50vh;
}
.wrapper>* {
width: 1000px;
position: absolute;
}
canvas {
position: absolute;
}
img {
width: 100%;
height: 110px;
}
<div class="wrapper">
<img class="stars" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/stars.png
" alt="" />
<img class="tress" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/trees.png
" alt="" />
<img class="clouds" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/clouds.png" alt="" />
<canvas></canvas>
</div>

Related

Resize canvas size as window is being resized (JavaScript)

What I want to do
I want to keep the canvas size full to the window.
What the problem is
I set the canvas size as stated below.
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
It worked.
But when I resized the window, the canvas size didn't change but instead kept its initial size.
What should I do to keep the canvas size full to the window without changing the balls' size?
Here is my code
const canvas = document.querySelector('#sandBox');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const ctx = canvas.getContext('2d');
class Circle {
constructor(x, y, r, c) {
this.x = x;
this.y = y;
this.r = r;
this.c = c;
}
draw() {
ctx.beginPath();
ctx.fillStyle = this.c;
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
}
const balls = [];
for (let i = 0; i < 20; i++) {
let r = Math.floor(Math.random() * 30) + 15;
let x = Math.random() * (canvas.width - r * 2) + r;
let y = Math.random() * (canvas.height - r * 2) + r;
let c = 'rgba(255,255,255,0.2)';
balls.push(new Circle(x, y, r, c));
}
balls.forEach(ball => ball.draw());
body {
position: relative;
}
canvas {
/* width: 100vw;
height: 100vh; */
background-color: #393939;
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
<canvas id="sandBox"></canvas>
You can have a callback onresize and use that to recreate the canvas:
Each time you resize the window you'll have to redraw the canvas, unless you save it somewhere.
function init() {
const canvas = document.querySelector("#sandBox");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const ctx = canvas.getContext("2d");
class Circle {
constructor(x, y, r, c) {
this.x = x;
this.y = y;
this.r = r;
this.c = c;
}
draw() {
ctx.beginPath();
ctx.fillStyle = this.c;
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
}
const balls = [];
for (let i = 0; i < 20; i++) {
let r = Math.floor(Math.random() * 30) + 15;
let x = Math.random() * (canvas.width - r * 2) + r;
let y = Math.random() * (canvas.height - r * 2) + r;
let c = "rgba(255,255,255,0.2)";
balls.push(new Circle(x, y, r, c));
}
balls.forEach((ball) => ball.draw());
}
window.onload = function() {
init();
};
body {
position: relative;
}
canvas {
width: 100vw;
height: 100vh;
background-color: #393939;
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
<body onresize="init()">
<canvas id="sandBox"></canvas>
</body>

How to Display DIV OnTop of Canvas Background

I have nice Beautiful Stars Canvas Background script, and i wonder- how do i Display a DIV ontop of that Canvas script.
I try to put the DIV in or under the canvas tags (you can see the results on the link below) but it display nothing, or bring it at the bottom of the page in a way that you have no way to see it .
Here is my code:
HTML:
<canvas></canvas>
<div style="background-color:red">this is a test</div>
CSS:
body{
margin: 0;
overflow: hidden;
background: black
}
JS:
var n_stars = 150
var colors = [ '#176ab6', '#fb9b39']
for ( let i = 0; i < 98; i++) {
colors.push( '#fff')
}
var canvas = document.querySelector('canvas')
canvas.width = innerWidth
canvas.height = innerHeight
addEventListener( 'resize', () => {
canvas.width = innerWidth
canvas.height = innerHeight
stars = []
init()
})
canvas.style.background = '#000'
var c = canvas.getContext('2d')
const randomInt = ( max, min) => Math.floor( Math.random() * (max - min) + min)
var bg = c.createRadialGradient( canvas.width/ 2, canvas.height * 3, canvas.height ,canvas.width/ 2,canvas.height , canvas.height * 4);
bg.addColorStop(0,"#32465E");
bg.addColorStop(.4,"#000814");
bg.addColorStop(.8,"#000814");
bg.addColorStop(1,"#000");
class Star {
constructor( x, y, radius, color) {
this.x = x || randomInt( 0, canvas.width)
this.y = y || randomInt( 0, canvas.height)
this.radius = radius || Math.random() * 1.1
this.color = color || colors[randomInt(0, colors.length)]
this.dy = -Math.random() * .3
}
draw () {
c.beginPath()
c.arc( this.x, this.y, this.radius, 0, Math.PI *2 )
c.shadowBlur = randomInt( 3, 15)
c.shadowColor = this.color
c.strokeStyle = this.color
c.fillStyle = 'rgba( 255, 255, 255, .5)'
c.fill()
c.stroke()
c.closePath()
}
update( arrayStars = [] ) {
if ( this.y - this.radius < 0 ) this.createNewStar( arrayStars )
this.y += this.dy
this.draw()
}
createNewStar( arrayStars = [] ) {
let i = arrayStars.indexOf( this )
arrayStars.splice( i, 1)
arrayStars.push( new Star( false, canvas.height + 5))
}
}
var stars = []
function init() {
for( let i = 0; i < n_stars; i++ ) {
stars.push( new Star( ) )
}
}
init()
function animate() {
requestAnimationFrame( animate)
c.clearRect( 0, 0, canvas.width, canvas.height)
c.fillStyle = bg
c.fillRect(0, 0, canvas.width, canvas.height)
stars.forEach( s => s.update( stars ))
}
animate()
Here is a live one: Codepen
You mean something like this (used a wrapper element and positioned div absolutely on top of canvas element):
var n_stars = 150
var colors = [ '#176ab6', '#fb9b39']
for ( let i = 0; i < 98; i++) {
colors.push( '#fff')
}
var canvas = document.querySelector('canvas')
canvas.width = innerWidth
canvas.height = innerHeight
addEventListener( 'resize', () => {
canvas.width = innerWidth
canvas.height = innerHeight
stars = []
init()
})
canvas.style.background = '#000'
var c = canvas.getContext('2d')
const randomInt = ( max, min) => Math.floor( Math.random() * (max - min) + min)
var bg = c.createRadialGradient( canvas.width/ 2, canvas.height * 3, canvas.height ,canvas.width/ 2,canvas.height , canvas.height * 4);
bg.addColorStop(0,"#32465E");
bg.addColorStop(.4,"#000814");
bg.addColorStop(.8,"#000814");
bg.addColorStop(1,"#000");
class Star {
constructor( x, y, radius, color) {
this.x = x || randomInt( 0, canvas.width)
this.y = y || randomInt( 0, canvas.height)
this.radius = radius || Math.random() * 1.1
this.color = color || colors[randomInt(0, colors.length)]
this.dy = -Math.random() * .3
}
draw () {
c.beginPath()
c.arc( this.x, this.y, this.radius, 0, Math.PI *2 )
c.shadowBlur = randomInt( 3, 15)
c.shadowColor = this.color
c.strokeStyle = this.color
c.fillStyle = 'rgba( 255, 255, 255, .5)'
c.fill()
c.stroke()
c.closePath()
}
update( arrayStars = [] ) {
if ( this.y - this.radius < 0 ) this.createNewStar( arrayStars )
this.y += this.dy
this.draw()
}
createNewStar( arrayStars = [] ) {
let i = arrayStars.indexOf( this )
arrayStars.splice( i, 1)
arrayStars.push( new Star( false, canvas.height + 5))
}
}
var stars = []
function init() {
for( let i = 0; i < n_stars; i++ ) {
stars.push( new Star( ) )
}
}
init()
function animate() {
requestAnimationFrame( animate)
c.clearRect( 0, 0, canvas.width, canvas.height)
c.fillStyle = bg
c.fillRect(0, 0, canvas.width, canvas.height)
stars.forEach( s => s.update( stars ))
}
animate()
body{
margin: 0;
overflow: hidden;
background: black
}
.wrapper{
position: relative;
display: block;
}
.wrapper > canvas {
position: relative;
display: block;
}
.wrapper > div {
position: absolute;
z-index: 10;
top: 0px;
left: 0px;
width: 100px;
height: 100px;
display: block;
}
<div class="wrapper">
<canvas></canvas>
<div style="background-color:red">this is a test</div>
</div>

Pixels between rects instead of drawn lines in HTML5 Canvas

Basically I want background to be seen between rects. Right now I'm trying to leave a distance which is dependable on rect size between them, can I make that size to be exact number of pixels?
Because right now these empty spaces merge, sometimes are not seen at all, etc.
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
var canvas = document.getElementById("posHourCanvas");
var context = canvas.getContext('2d');
var boxes = [];
function init() {
context.clearRect(0, 0, canvas.width, canvas.height);
boxes.length = 0;
const strokeWidth = 0.2;
canvas.width = $('#two')[0].clientWidth;
var cellSize = canvas.width / 27;
canvas.height = 7 / 27 * canvas.width;
var x = y = 0;
draw(x, y, canvas.width, canvas.height, cellSize, strokeWidth);
}
function Box(x, y, day, hour) {
this.x = x;
this.y = y;
this.day = day;
this.hour = hour;
}
function draw(x, y, w, h, cellSize, strokeWidth) {
let onePixel = cellSize * strokeWidth;
cellSize = cellSize * (1 - strokeWidth);
context.beginPath();
context.lineWidth = 0.01;
context.strokeStyle = 'rgba(255, 255, 255, 0)';
for (let i = 0; i < 7; i++) {
context.beginPath();
dayRect(context, cellSize, i, onePixel);
let startX = 3 * cellSize + onePixel;
for (let j = 0; j < 25; j++) {
context.rect(startX, i * cellSize + i * onePixel, cellSize, cellSize);
context.fillStyle = "white";
context.fill();
startX += cellSize + onePixel;
}
}
}
function dayRect(context, cellSize, day, onePixel) {
var offSet = day * onePixel;
context.rect(0, day * cellSize + offSet, 3 * cellSize, cellSize);
context.fillStyle = "white";
context.fill();
}
init();
window.addEventListener("resize", init, false);
body {
margin: auto;
color: white;
background-color: black;
}
div#two {
margin-left: 5%;
width: 10%;
height: 30%;
}
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
<div id="parent">
<div>text above</div>
<div id="two">
<canvas id="posHourCanvas" width="600" height="300"></canvas>
</div>
<div>text under</div>
</div>
JSFIDDLE: http://jsfiddle.net/kgbh9352/

Canvas leaves an "imprint" outside when arcs near the edge enlarged

This is hard to explain in words, rather I uploaded the site here to demonstrate the problem. There's a snippet below that has the code and simulates the same problem.
Notice how when you scroll down, and hover near the edge, white imprints are left outside the canvas on neighbouring divs. Why does this happen, and how do I fix it?
var canvas = document.querySelector('canvas');
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
var context = canvas.getContext('2d');
var mouse = {
x: undefined,
y: undefined
}
window.addEventListener('mousemove', function(event){
mouse.x = event.x;
mouse.y = event.y;
});
function Circle(x, y, dx, dy, radius, bg){
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.defaultRadius = radius;
this.bg = bg;
this.draw = function(){
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
context.fillStyle = this.bg;
context.fill();
}
this.update = function(){
if (this.x + this.radius > innerWidth || this.x - this.radius < 0) {
this.dx = -this.dx;
}
if (this.y + this.radius > innerHeight || this.y - this.radius < 0) {
this.dy = -this.dy;
}
if (this.x - mouse.x < 50 && this.x - mouse.x > -50 && this.y - mouse.y < 50 && this.y - mouse.y > -50) {
if (this.radius < 30) {
this.radius += 5;
}
} else {
if (this.radius > this.defaultRadius) {
this.radius -= 1;
}
}
this.x += this.dx;
this.y += this.dy;
this.draw();
}
}
var circlesArray = [];
for(var i = 0; i < 300; i++){
addNewCircle();
}
function addNewCircle(){
var radius = (Math.random() * 2) + 1;
var x = (Math.random() * (innerWidth - (radius*2))) + radius;
var y = (Math.random() * (innerHeight - (radius*2))) + radius;
var dx = Math.random() - 0.5;
var dy = Math.random() - 0.5;
var bg = 'rgba(255, 255, 255, 0.1)';
circlesArray.push(new Circle(x, y, dx, dy, radius, bg));
}
function animate(){
requestAnimationFrame(animate);
context.clearRect(0, 0, innerWidth, innerHeight);
for(var i = 0; i < circlesArray.length; i++){
circlesArray[i].update();
}
}
animate();
*{
box-sizing: border-box;
font-family: 'Roboto', sans-serif;
font-weight: 400;
margin: 0;
padding: 0;
color: rgba(0,0,0, 0.8);
}
#page{
width: 100%;
height: 100vh;
background: rgba(0, 0, 0, 0.7);
}
#page canvas{
position: absolute;
top: 0;
left: 0;
}
#top-bar{
width: 100%;
height: 200px;
background: black;
}
<!DOCTYPE html>
<html>
<body>
<div id="page">
<canvas></canvas>
</div>
<div id="top-bar">
</div>
</body>
</html>
Your canvas is setting height based on body, but your #top-bar div is taking up some of that space.
So that extra space is where you #top-bar div is
You could set the canvas size to be (body - #top-bar height).

Canvas spark lines

I would like to create a spark lines with the various lengths and animate them by moving them from center to end of the canvas and the process has to loop on a canvas. To achieve this i started with a particle system.
Please check the below code and here is the codepen link https://codepen.io/yesvin/pen/XPKNYW
window.onload = function() {
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
w = canvas.width = window.innerWidth,
h = canvas.height = window.innerHeight,
centerX = w / 2,
centerY = h / 2,
speed = 0,
time = 0,
numObjects = 5,
x, y, vx, vy, life, maxLife;
var lines = {},
lineIndex = 0;
function Line() {
this.x = centerX;
this.y = centerY;
this.vx = Math.random() * 16 - 8;
this.vy = Math.random() * 16 - 8;
this.life = 0;
this.maxLife = Math.random() * 10 + 20;
lineIndex++;
lines[lineIndex] = this;
this.id = lineIndex;
}
Line.prototype.draw = function() {
this.x += this.vx;
this.y += this.vy;
this.life++;
if (this.life >= this.maxLife) {
delete lines[this.id];
}
context.fillStyle = "#000";
context.fillRect(this.x, this.y, 3, 3)
}
setInterval(function() {
context.fillStyle = 'rgba(255,255,255,.05)';
context.fillRect(0, 0, w, h);
new Line();
for (var i in lines) {
lines[i].draw();
}
}, 30)
};
body {
overflow: hidden;
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>
So, How do we create this same effect using the lineTo(), and moveTo() methods? I tried with the following code (which is commented in a codepen)
context.beginPath();
context.moveTo(centerX, centerY);
context.lineTo(this.x * Math.random() * w, this.y * Math.random() * h);
context.lineWidth = 1;
context.stroke();
context.strokeStyle = "#000";
Sample GIF
Thanks in advance.
Here is one approach...
With lines you will get a more continuous effect:
The change I'm making to your code is to keep track of the start and end points.
window.onload = function() {
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
w = canvas.width = window.innerWidth,
h = canvas.height = window.innerHeight,
centerX = w / 2,
centerY = h / 2;
var lines = {},
lineIndex = 0;
function Line() {
this.start = { x: centerX, y: centerY };
this.end = { x: centerX, y: centerY };
this.vx = Math.random() * 16 - 8;
this.vy = Math.random() * 16 - 8;
this.life = 0;
this.maxLife = Math.random() * 10 + 20;
lineIndex++;
lines[lineIndex] = this;
this.id = lineIndex;
}
Line.prototype.draw = function() {
this.end.x += this.vx;
this.end.y += this.vy;
this.life++;
if (this.life >= this.maxLife) {
delete lines[this.id];
}
//context.fillStyle = "#000";
//context.fillRect(this.x, this.y, 1, 1)
context.beginPath();
context.moveTo(this.start.x, this.start.y);
context.lineTo(this.end.x, this.end.y);
context.lineWidth = 1;
context.stroke();
context.strokeStyle = "#000";
}
setInterval(function() {
context.fillStyle = 'rgba(255,255,255,.05)';
context.fillRect(0, 0, w, h);
new Line();
for (var i in lines) {
lines[i].draw();
}
}, 30)
};
body {
overflow: hidden;
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>

Categories