I'm super new to coding and I've been trying to figure out animations with Javascript and HTML5. I have this "loading bar" like animation where a rectangle expands until it fills up the canvas. The idea is that once the canvas is covered the box clears and starts over. Instead, the rectangle fills the canvas and just starts flickering. Any ideas?
window.onload = function() {
var wipeWidth = 0
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
console.log(context);
function drawRect() {
context.clearRect(0,0,300,150)
context.fillStyle = "#305ef2";
context.rect(0, 0, wipeWidth, 150);
context.fill()
wipeWidth += 10
if(wipeWidth > 300) {
wipeWidth = 0;
}
}
setInterval(drawRect,50)
}
You forgot to clear the path (beginPath). You can use fillRect instead to avoid this.
window.onload = function() {
var wipeWidth = 0
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
console.log(context);
function drawRect() {
context.clearRect(0, 0, 300, 150)
context.fillStyle = "#305ef2";
// this
context.beginPath()
context.rect(0, 0, wipeWidth, 150);
context.fill()
// or just this
// context.fillRect(0, 0, wipeWidth, 150);
wipeWidth += 10
if (wipeWidth > 300) {
wipeWidth = 0;
}
}
setInterval(drawRect, 50)
}
<canvas id="canvas"></canvas>
fillRect, requestAnimationFrame
var wipeWidth = 0
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
var direction = +10;
function drawRect() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "#305ef2";
context.fillRect(0, 0, wipeWidth, 150);
wipeWidth += direction
if (wipeWidth > canvas.width || wipeWidth < 0) {
direction *= -1;
}
requestAnimationFrame(drawRect)
}
drawRect()
canvas {
background: gray;
}
<canvas id="canvas" width="600" height="50"></canvas>
i want to make a line that contracts and expands along x-axis randomly it should start from the middle of canvas . I was only able to move the line one way, not both ways! i have the code, if you could please help me i would really appreciate it !
<!DOCTYPE html>
<style>
canvas {
border: solid;
border-color: black; }
</style>
<canvas id="canvas">
</canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "black";
var posX = 0;
var lineLength = 50;
var speed = 2;
function drawLine() {
ctx.beginPath();
ctx.moveTo(posX, 50);
ctx.lineTo(posX + lineLength, 50);
ctx.stroke();
}
function moveLine() {
posX += speed;
if (posX < 0 || posX > canvas.width - 50) {
speed = speed * -1;
}
}
function loop() {
// clear old frame;
ctx.clearRect(0, 0, canvas.width, canvas.height);
moveLine();
drawLine();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
</script>
You want to be modifying the value of lineLength in your loop, not posX. Like this:
function moveLine() {
lineLength += speed;
if (lineLength < 0 || lineLength > canvas.width - 50) {
speed = speed * -1;
}
}
Then you just need to center your line:
function drawLine() {
var center = canvas.width/2;
ctx.beginPath();
ctx.moveTo(center - (lineLength/2), 50);
ctx.lineTo(center + (lineLength/2), 50);
ctx.stroke();
}
Since the X position of your line isn't variable, you don't need posX at all.
I have build an animation with canvas, following some code and tips from others anwers:
-Draw HTML5/Javascript Canvas Path in Time
-Animate a Fill Circle using Canvas
Here is a fiddle
canvasHolder = document.getElementById( 'canvas' );
context = canvasHolder.getContext('2d');
context.fillStyle = 'white';
var w = canvasHolder.width, h = canvasHolder.height;
context.fillRect( 0, 0, w, h);
context.scale(2, 2);
//set the direction the line draws in
//1->ltr | -1->rtl
var dir = -1;
//IMPORTANT: this must be set to greater than the length of the line
var length = 350;
//the speed of the line draw
var speed = 1;
var progress = 0;
var lineInterval;
var full = 1;
var amount = 0;
var amountToIncrease = 5;
context.globalCompositeOperation='copy';
drawLine();
function drawLine() {
//this clears itself once the line is drawn
lineInterval = setInterval(updateLine, 1);
}
function updateLine() {
//define the line
defineLine(5);
if(progress<length) {
progress+=speed;
moveDash(progress, dir);
/**/
document.getElementById("log").innerHTML = progress;
/**/
} else {
clearInterval(lineInterval);
progress = 0;
context.clearRect(0, 0, 300, 300);
fillInterval = setInterval(fillHeart, 100);
}
}
function fillHeart() {
amount += amountToIncrease;
/**/
document.getElementById("log").innerHTML = amount;
/**/
if ((amount/100) < full) {
defineLine(15);
} else {
clearInterval(fillInterval);
amount = 0; // restart
context.clearRect(0, 0, 300, 300);
lineInterval = setInterval(updateLine, 1);
}
}
function defineLine(b) {
context.beginPath();
if (b < 10) {
context.moveTo(75,40);
}
context.bezierCurveTo(75,37,70,25,50,25);
context.bezierCurveTo(15,25,20,62.5,20,62.5);
context.bezierCurveTo(20,80,40,102,75,120);
context.bezierCurveTo(110,102,130,80,130,62.5);
context.bezierCurveTo(130,62.5,135,25,100,25);
context.bezierCurveTo(85,25,75,37,75,40);
context.lineWidth = 3;
context.lineCap = "round";
context.strokeStyle = 'red';
if (b > 10) {
context.clip();
context.fillStyle = 'rgba(255, 0, 0, '+(amount/100)+')';
context.fillRect(0,0,300,300);
context.restore();
}
}
function moveDash(frac, dir) {
//default direction right->left
var dir = dir || -1
context.setLineDash([length]);
context.lineDashOffset = dir*(frac+length);
context.stroke();
}
<canvas id="canvas" width="300" height="300"></canvas>
<span id="log"></span>
But there are 3 problem that i'm not capable to solve:
-there is a thin red line staying in the canvas.
-The path from first animation doesn't disappear.
-The animations get slower each time they finish.
Can you help me figure it out, i have try but i just make it worst.
And if you can explain why is not working properly how the code is
I've got some code which draws a rectangle on a canvas, but I want that rectangle to change color when I hover the mouse over it.
The problem is after I've drawn the rectangle I'm not sure how I select it again to make the adjustment.
What I want to do:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
$('c.[rectangle]').hover(function(this){
this.fillStyle = 'red';
this.fill();
});
You can't do this out-of-the-box with canvas. Canvas is just a bitmap, so the hover logic has to be implemented manually.
Here is how:
Store all the rectangles you want as simple object
For each mouse move on the canvas element:
Get mouse position
Iterate through the list of objects
use isPointInPath() to detect a "hover"
Redraw both states
Example
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
rects = [
{x: 10, y: 10, w: 200, h: 50},
{x: 50, y: 70, w: 150, h: 30} // etc.
], i = 0, r;
// render initial rects.
while(r = rects[i++]) ctx.rect(r.x, r.y, r.w, r.h);
ctx.fillStyle = "blue"; ctx.fill();
canvas.onmousemove = function(e) {
// important: correct mouse position:
var rect = this.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top,
i = 0, r;
ctx.clearRect(0, 0, canvas.width, canvas.height); // for demo
while(r = rects[i++]) {
// add a single rect to path:
ctx.beginPath();
ctx.rect(r.x, r.y, r.w, r.h);
// check if we hover it, fill red, if not fill it blue
ctx.fillStyle = ctx.isPointInPath(x, y) ? "red" : "blue";
ctx.fill();
}
};
<canvas/>
This is a stable code in base of #K3N answer. The basic problem of his code is because when one box is over the another the two may get mouse hover at same time. My answer perfectly solves that adding a 'DESC' to 'ASC' loop.
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
var map = [
{x: 20, y: 20, w: 60, h: 60},
{x: 30, y: 50, w: 76, h: 60}
];
var hover = false, id;
var _i, _b;
function renderMap() {
for(_i = 0; _b = map[_i]; _i ++) {
ctx.fillStyle = (hover && id === _i) ? "red" : "blue";
ctx.fillRect(_b.x, _b.y, _b.w, _b.h);
}
}
// Render everything
renderMap();
canvas.onmousemove = function(e) {
// Get the current mouse position
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left, y = e.clientY - r.top;
hover = false;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(var i = map.length - 1, b; b = map[i]; i--) {
if(x >= b.x && x <= b.x + b.w &&
y >= b.y && y <= b.y + b.h) {
// The mouse honestly hits the rect
hover = true;
id = i;
break;
}
}
// Draw the rectangles by Z (ASC)
renderMap();
}
<canvas id="canvas"></canvas>
You may have to track the mouse on the canvas using JavaScript and see when it is over your rectangle and change the color then. See code below from my blog post
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="700" height="500" style="border:1px solid #c3c3c3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var myRect={x:150, y:75, w:50, h:50, color:"red"};
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = myRect.color;
ctx.fillRect(myRect.x, myRect.y, myRect.w, myRect.h);
c.addEventListener("mousemove", function(e){
if ((e.clientX>=myRect.x)&(e.clientX<=myRect.x+myRect.w)&(e.clientY>=myRect.y)&(e.clientY<=myRect.y+myRect.h)){
myRect.color = "green";}
else{
myRect.color = "red";}
updateCanvas();
}, false);
function updateCanvas(){
ctx.fillStyle = myRect.color;
ctx.fillRect(myRect.x, myRect.y, myRect.w, myRect.h);
}
</script>
</body>
</html>
I believe this is a slightly more in-depth answer that would work better for you, especially if you are interested in game design with the canvas element.
The main reason this would work better for you is because it focuses more on an OOP (object orientated programming) approach. This allows for objects to be defined, tracked and altered at a later time via some event or circumstance. It also allows for easy scaling of your code and in my opinion is just more readable and organized.
Essentially what you have here is two shapes colliding. The cursor and the individual point / object it hovers over. With basic squares, rectangles or circles this isn't too bad. But, if you are comparing two more unique shapes, you'll need to read up more on Separating Axis Theorem (SAT) and other collision techniques. At that point optimizing and performance will become a concern, but for now I think this is the optimal approach.
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;
const cx = width / 2;
const cy = height / 2;
const twoPie = Math.PI * 2;
const points = []; // This will be the array we store our hover points in later
class Point {
constructor(x, y, r) {
this.x = x;
this.y = y;
this.r = r || 0;
}
}
class HoverPoint extends Point {
constructor(x, y, r, color, hoverColor) {
super(x, y, r);
this.color = color;
this.hoverColor = hoverColor;
this.hovered = false;
this.path = new Path2D();
}
draw() {
this.hovered ? ctx.fillStyle = this.hoverColor : ctx.fillStyle = this.color;
this.path.arc(this.x, this.y, this.r, 0, twoPie);
ctx.fill(this.path);
}
}
class Cursor extends Point {
constructor(x, y, r) {
super(x, y, r);
}
collisionCheck(points) {
// This is the method that will be called during the animate function that
// will check the cursors position against each of our objects in the points array.
document.body.style.cursor = "default";
points.forEach(point => {
point.hovered = false;
if (ctx.isPointInPath(point.path, this.x, this.y)) {
document.body.style.cursor = "pointer";
point.hovered = true;
}
});
}
}
function createPoints() {
// Create your points and add them to the points array.
points.push(new HoverPoint(cx, cy, 100, 'red', 'coral'));
points.push(new HoverPoint(cx + 250, cy - 100, 50, 'teal', 'skyBlue'));
// ....
}
function update() {
ctx.clearRect(0, 0, width, height);
points.forEach(point => point.draw());
}
function animate(e) {
const cursor = new Cursor(e.offsetX, e.offsetY);
update();
cursor.collisionCheck(points);
}
createPoints();
update();
canvas.onmousemove = animate;
There is one more thing that I would like to suggest. I haven't done tests on this yet but I suspect that using some simple trigonometry to detect if our circular objects collide would preform better over the ctx.IsPointInPath() method.
However if you are using more complex paths and shapes, then the ctx.IsPointInPath() method would most likely be the way to go. if not some other more extensive form of collision detection as I mentioned earlier.
The resulting change would look like this...
class Cursor extends Point {
constructor(x, y, r) {
super(x, y, r);
}
collisionCheck(points) {
document.body.style.cursor = "default";
points.forEach(point => {
let dx = point.x - this.x;
let dy = point.y - this.y;
let distance = Math.hypot(dx, dy);
let dr = point.r + this.r;
point.hovered = false;
// If the distance between the two objects is less then their combined radius
// then they must be touching.
if (distance < dr) {
document.body.style.cursor = "pointer";
point.hovered = true;
}
});
}
}
here is a link containing examples an other links related to collision detection
I hope you can see how easily something like this can be modified and used in games and whatever else. Hope this helps.
Below code adds shadow to canvas circle on hovering it.
<html>
<body>
<canvas id="myCanvas" width="1000" height="500" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
</body>
<script>
var canvas = document.getElementById("myCanvas"),
ctx = canvas.getContext("2d"),
circle = [{
x: 60,
y: 50,
r: 40,
},
{
x: 100,
y: 150,
r: 50,
} // etc.
];
// render initial rects.
for (var i = 0; i < circle.length; i++) {
ctx.beginPath();
ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
}
canvas.onmousemove = function(e) {
var x = e.pageX,
y = e.pageY,
i = 0,
r;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < circle.length; i++) {
if ((x > circle[i].x - circle[i].r) && (y > circle[i].y - circle[i].r) && (x < circle[i].x + circle[i].r) && (y < circle[i].y + circle[i].r)) {
ctx.beginPath();
ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.shadowBlur = 10;
ctx.lineWidth = 3;
ctx.strokeStyle = 'rgb(255,255,255)';
ctx.shadowColor = 'grey';
ctx.stroke();
ctx.shadowColor = 'white';
ctx.shadowBlur = 0;
} else {
ctx.beginPath();
ctx.arc(circle[i].x, circle[i].y, circle[i].r, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.shadowColor = 'white';
ctx.shadowBlur = 0;
}
}
};
</script>
</html>
I know this is old, but I am surprised no one has mentioned JCanvas. It adds to the simplicity of animating canvas on events. More documentation here https://projects.calebevans.me/jcanvas/docs/mouseEvents/
<html lang="en">
<head>
<!-- css and other -->
</head>
<body onload="draw();">
<canvas id = "canvas" width="500" height="500" style= border:1px solid #000000;"> </canvas>
<script>
function draw() {
$('canvas').drawRect({
layer: true,
fillStyle:'#333',
x:100, y: 200,
width: 600,
height: 400,
mouseover: function(layer) {
$(this).animateLayer(layer, {
fillStyle: 'green'
}, 1000, 'swing');
}
});
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jcanvas/21.0.1/jcanvas.js" crossorigin="anonymous"></script>
</body>
</html>
Consider this following code:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
c.addEventListener("mouseover", doMouseOver, false);//added event to canvas
function doMouseOver(e){
ctx.fillStyle = 'red';
ctx.fill();
}
DEMO
You could use canvas.addEventListener
var canvas = document.getElementById('canvas0');
canvas.addEventListener('mouseover', function() { /*your code*/ }, false);
It worked on google chrome
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
$(c).hover(function(e){
ctx.fillStyle = 'red';
ctx.fill();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<canvas id="myCanvas"/>
I'm a french programmer, so excuse my English :
I make a canvas with a wave and i can't find where i must clear my canvas for good visual effect, it's my code :
window.onload = function()
{
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
if (canvas.getContext)
// If you have it, create a canvas user inteface element.
{
// Paint it black.
context.fillStyle = "black";
context.rect(0, 0, 1000, 1000);
context.fill();
// Paint the starfield.
vague();
stars();
decor();
}
function stars() {
// Draw 50 stars.
for (i = 0; i <= 70; i++) {
// Get random positions for stars.
var x = Math.floor(Math.random() * 800)
var y = Math.floor(Math.random() * 400)
// Make the stars white
context.fillStyle = "white";
context.shadowColor = 'white';
context.shadowBlur = 50;
// Give the ship some room.
if (x < 0 || y < 0) context.fillStyle = "black";
// Draw an individual star.
context.beginPath();
context.arc(x, y, 3, 0, Math.PI * 2, true);
context.closePath();
context.fill();
}
}
function decor() {
context.beginPath();
context.shadowColor = 'white';
context.shadowBlur = 30;
context.fillStyle = 'skyblue';
context.fillRect(0,400,1000,200);
context.closePath();
context.fill();
context.beginPath();
context.fillStyle = 'white';
context.shadowColor = 'white';
context.shadowBlur = 1500;
context.shadowOffsetX = -300;
context.shadowOffsetY = 400;
context.arc(680,110,100,Math.PI*2,false);
context.closePath();
context.fill();
}
function vague (){
draw(-120,50);
var i = 0;
function draw(x,y){
for ( var i = 0; i <= 20; i++) {
var x = x+50;
var y = y;
context.fillStyle = 'rgba(0,0,100,0.4)';
context.beginPath();
context.moveTo(72+x, 356+y); // Tracer autre une ligne (théorique)
context.strokeStyle = 'skyblue';
context.lineWidth=3;
context.bezierCurveTo(60+x, 360+y , 92+x , 332+y , 104+x , 323+y );
context.bezierCurveTo(114+x, 316+y , 128+x , 304+y , 140+x , 325+y );
context.bezierCurveTo(148+x, 339+y , 127+x, 307+y , 115+x , 337+y );
context.bezierCurveTo(109+x, 352+y , 159+x , 357+y , 144+x , 357+y );
context.bezierCurveTo(129+x, 357+y , 87+x , 356+y , 72+x , 356+y );
context.fill();
context.stroke();
if (x>=800){
x=-120;
}
}
setInterval( function () { draw(x,y) }, 50);
x = x+20;
}
}
};
Thanks for your answer i can't find my mistake, i become CRAZY !
My recommendation would be to put your waves on a different canvas with a transparent background that is on top of your background canvas. Then you just clear the canvas (or the area where the waves are rendered) at the start of each draw call. That way, you don't need to rerender whatever background as well.
In order to do that, you would use CSS to place the canvases on top of each other. You would also just give the other canvas a different id, like <canvas id="vagueCanvas"></canvas> and select the context the same way var vagueContext = document.getElementById( 'vagueCanvas' ).getContext( '2d' );.