Why do transparent objects become opaque when animating canvas element? - javascript
I'm trying to do a js app that would basically move some balls on a canvas element. I set the context.fillStyle = "rgba(12, 34, 56, 0.2)"; the problem is that the balls become opaque from transparent after a short period of time. How can I maintain their transparency and why do they become opaque?
here is simplified version of my code:
function startScript(){
var layer1 = document.getElementById("layer1");
var context1 = layer1.getContext("2d");
var posX = 5;
context1.fillStyle = "rgba(12, 34, 56, 0.05)";
animate();
function animate() {
posX+=3;
context1.arc(posX, 200, 5, 0, Math.PI*2);
context1.fill();
// request new frame
requestAnimFrame(function() {
animate();
});
}
}
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
You have to use context1.beginPath before your new line draws
Otherwise, the context remembers and redraws the previous arc in addition to the new arc.
Also, you should do context1.closePath() after drawing a circle-arc.
Otherwise the context is drawing an unclosed arc instead of a circle.
context1.beginPath();
context1.arc(posX, 200, 5, 0, Math.PI*2);
context1.closePath();
context1.fill();
Related
HTML5 Canvas tile based map not showing up
I am new to html5 canvas game dev and I am having a probably newbie problem. I am making a tile based map that is supposed to turn a 2d array into a map with walls and open space, but whenever I open the game it just doesn't show up... I don't even get errors!? Please help me ( I am using chrome BTW ) pastebin code: http://pastebin.com/5GcQwCVa# // Declares global variables var canvas = document.createElement("canvas"); c = canvas.getContext("2d"), make = {}, maps = {}, width = 800, height = 600; // Creates the requestAnimationFrame variable (function () { var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; window.requestAnimationFrame = requestAnimationFrame; }) (); // Modifies the canvas' properties canvas.width = width, canvas.height = height; // 2D arrays that make up maps maps = { one: [ ["w","w","w","w","w","w","w","w"], ["w","o","o","o","o","o","o","w"], ["w","o","w","w","w","w","o","w"], ["w","o","w","o","o","o","o","w"], ["w","o","w","o","w","o","o","w"], ["w","o","w","o","o","w","o","w"], ["w","o","o","o","o","o","o","w"], ["w","w","w","w","w","w","w","w"] ], two: [ ["w","w","w","w","w","w","w","w"], ["w","o","o","o","o","o","o","w"], ["w","o","o","o","o","o","o","w"], ["w","o","o","o","o","o","o","w"], ["w","o","o","o","o","o","o","w"], ["w","o","o","o","o","o","o","w"], ["w","o","o","o","o","o","o","w"], ["w","w","w","w","w","w","w","w"] ] }; // Maps drawing functions make = { map: function ( map2d ) { var i, j, tile, tilesX = 8, tilesY = 8; for (i = 0; i < tilesX; i++) { for(j = 0; j < tilesY; j++) { if (map2d[i][j] === "w") { this.tile(i * 64, j * 64); } } } }, tile: function (x, y, TD) { switch (TD) { case "w": c.rect(x, y, 64, 64); c.fillStyle = wallColor; c.fill(); c.lineWidth = 8; c.strokeStyle = "black"; c.stroke(); break; case "o": c.rect(x, y, 64, 64); c.fillStyle = "white"; c.fill(); c.lineWidth = 8; c.strokeStyle = "white"; c.stroke(); break; } } } // Updates constantly function update () { c.clearRect(0, 0, width, height); make.map(maps.two); requestAnimationFrame(update); } // Begins updating when window is ready window.addEventListener("load", function () { update(); });
So there are a few things. The first is you need to actually add the canvas to the document, you can do that like so. document.body.appendChild(canvas); I added this to your windows load event listener. The next thing is you aren't passing "o" or "w" to your function for the switch statement to be called. So I just hard coded w for now because you have this bit if (map2d[i][j] === "w") { this.tile(i * 64, j * 64, "w"); } So you are only calling draw if its a wall anyway. After that you still see nothing because you have a variable called wallcolor that doesn't actually exist, so I changed your fill to just use black for now. c.beginPath(); c.rect(x, y, 64, 64); c.fillStyle = "black"; c.fill(); c.lineWidth = 8; c.strokeStyle = "black"; c.stroke(); c.closePath(); Another thing you will notice is the addition of beginPath and closePath if you are creating paths you need to use these otherwise all your shapes will keep being added to the same path and every time you call fill or stroke it will fill or stroke everything you've already drawn making it really slow over time. The following is a good explanation of what paths are Live Demo // Declares global variables var canvas = document.createElement("canvas"); c = canvas.getContext("2d"), make = {}, maps = {}, width = 800, height = 600; // Creates the requestAnimationFrame variable (function () { var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; window.requestAnimationFrame = requestAnimationFrame; }) (); // Modifies the canvas' properties canvas.width = width, canvas.height = height; // 2D arrays that make up maps maps = { one: [ ["w","w","w","w","w","w","w","w"], ["w","o","o","o","o","o","o","w"], ["w","o","w","w","w","w","o","w"], ["w","o","w","o","o","o","o","w"], ["w","o","w","o","w","o","o","w"], ["w","o","w","o","o","w","o","w"], ["w","o","o","o","o","o","o","w"], ["w","w","w","w","w","w","w","w"] ], two: [ ["w","w","w","w","w","w","w","w"], ["w","o","o","o","o","o","o","w"], ["w","o","o","o","o","o","o","w"], ["w","o","o","o","o","o","o","w"], ["w","o","o","o","o","o","o","w"], ["w","o","o","o","o","o","o","w"], ["w","o","o","o","o","o","o","w"], ["w","w","w","w","w","w","w","w"] ] }; // Maps drawing functions make = { map: function ( map2d ) { var i, j, tile, tilesX = 8, tilesY = 8; for (i = 0; i < tilesX; i++) { for(j = 0; j < tilesY; j++) { if (map2d[i][j] === "w") { this.tile(i * 64, j * 64, "w"); } } } }, tile: function (x, y, TD) { switch (TD) { case "w": c.beginPath(); c.rect(x, y, 64, 64); c.fillStyle = '#000'; c.fill(); c.lineWidth = 8; c.strokeStyle = "black"; c.stroke(); c.closePath(); break; case "o": c.rect(x, y, 64, 64); c.fillStyle = "white"; c.fill(); c.lineWidth = 8; c.strokeStyle = "white"; c.stroke(); break; } } } // Updates constantly function update () { c.clearRect(0, 0, width, height); make.map(maps.two); requestAnimationFrame(update); } // Begins updating when window is ready window.addEventListener("load", function () { // Add the canvas document.body.appendChild(canvas); update(); });
Understand Code
I do not understand the code highlighted below that has a comment "I DO NOT UNDERSTAND WHAT IS GOING ON HERE". I know it is a call back function. But I do not understand how the function operates and the purpose. For the record I understand everything else. I have never seen a function written like this before. I do understand that the OR statements are for detecting which browser. <body> <canvas id="myCanvas" width="578" height="200"></canvas> <script> window.requestAnimFrame = (function(callback) { // I DO NOT UNDERSTAND WHAT IS GOING ON HERE return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { console.log("inside callback"); window.setTimeout(callback, 1000 / 60); }; })(); function drawRectangle(myRectangle, context) { context.beginPath(); context.rect(myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height); context.fillStyle = '#8ED6FF'; context.fill(); context.lineWidth = myRectangle.borderWidth; context.strokeStyle = 'black'; context.stroke(); } function animate(myRectangle, canvas, context, startTime) { // update var time = (new Date()).getTime() - startTime; var linearSpeed = 100; // pixels / second var newX = linearSpeed * time / 1000; if(newX < canvas.width - myRectangle.width - myRectangle.borderWidth / 2) { myRectangle.x = newX; } // clear context.clearRect(0, 0, canvas.width, canvas.height); drawRectangle(myRectangle, context); // request new frame requestAnimFrame(function() { animate(myRectangle, canvas, context, startTime); }); } var canvas = document.getElementById('myCanvas'); var context = canvas.getContext('2d'); // var myRectangle = { //position x: 0, y: 75, //dimension width: 100, height: 50, // stlye borderWidth: 5 }; drawRectangle(myRectangle, context); // wait one second before starting animation setTimeout(function() { var startTime = (new Date()).getTime(); animate(myRectangle, canvas, context, startTime); }, 1000); </script>
When you OR two values together with JavaScript, you don't explicitly get a boolean... you get back the first truthy value. This is often used to provide defaults or fallbacks. For example: console.log( null || 'some string'); // Logs 'some string' var myOption = 'some value'; myOption = myOption || 'default value'; // myOption is 'some value' What's happening specifically in your code is that the function requestAnimationFrame took a long time to be standardized. Each browser vendor made their own version of it with their own prefix. This function is going through each possible name of that function until it hits one. (A function is "truthy" where undefined or null is not.)
Slow performance drawing dashed lines in HTML5 Canvas
I am attempting to make a Pong clone using HTML5 canvas. I want to draw a dashed line down the middle of the playing field as is found in the original Pong. I am doing this by extending the CanvasRenderingContext2D object as shown in David Geary's excellent book: CanvasRenderingContext2D.prototype.dashedLine = function (x1, y1, x2, y2, dashLength) { dashLength = dashLength === undefined ? 5 : dashLength; var deltaX = x2 - x1; var deltaY = y2 - y1; var numDashes = Math.floor( Math.sqrt(deltaX * deltaX + deltaY * deltaY) / dashLength); for (var i=0; i < numDashes; ++i) { context[ i % 2 === 0 ? 'moveTo' : 'lineTo' ] (x1 + (deltaX / numDashes) * i, y1 + (deltaY / numDashes) * i); } I then have a render() function that actually makes all the calls to render elements on the canvas. Included in this is my renderBackground() function which colors the background and draws the dashed line: function render() { ctx.clearRect(0, 0, cWidth, cHeight); renderBackground(); // Rest removed for brevity } function renderBackground() { ctx.lineWidth = 5; ctx.strokeStyle = '#FFFFFF'; ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, cWidth, cHeight); ctx.dashedLine(0, 0, 0, cHeight, 10); ctx.stroke() } Then at the end I have a function called animLoop() that actually calls the render() function and makes use of requestAnimationFrame() for smoother animations: function animLoop() { render(); requestAnimationFrame(animLoop); } window.requestAnimationFrame = (function() { return ( window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); } ); })(); If I let my game run for more than 30 seconds it starts slowing down dramatically to the point that it is unplayable and CPU usage by the browser hovers around 134% for both Firefox and Chrome. The slowness is only present when I am rendering the dashed line. I am not sure what is going, but below I also ran my code through Chrome Inspectors profiler and get the following: My renderBackground() function is only taking .46% of the CPU time. Also I am not sure what the (program) is supposed to signify. Any thoughts on what could be causing the slowness? Also you can see the complete code I have so far on my Github repo.
You are accumulating all calls of lineTo on the default path each time ctx.dashedLine is called and call stroke will stroke all lines in path since the application start. Because you are running an animation, quickly the path will have a LOT of lines to draw when stroke is called each frame. Add ctx.beginPath() before ctx.dashedLine to solve the problem. function renderBackground() { ctx.lineWidth = 5; ctx.strokeStyle = '#FFFFFF'; ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, cWidth, cHeight); ctx.beginPath(); // <-- add it here ctx.dashedLine(0, 0, 0, cHeight, 10); ctx.stroke(); } When drawing using a path, you are using a virtual "pen" or "pointer". So you'll create a virtual path with begin path, draw the lines and finally stroke that lines. In next frame you'll begin a new virtual path, draw the new lines in the path and stroke again. This way the performance stays stable. Demo
Animating game using requestAnimationFrame
I tried to make some simply game using requestAnimFrame but animation doesn't work and I don't know why. Maybe some one can help? Here is the code: // requestAnimationFrame() shim by Paul Irish // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(/* function */ callback, /* DOMElement */ element){ window.setTimeout(callback, 1000 / 60); }; })(); //Create canvas var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); canvas.width = 640; canvas.height = 480; document.body.appendChild(canvas); // The main game loop var lastTime; function main() { var now = Date.now(); var dt = now - lastTime; draw(); update(dt); lastTime = now; requestAnimFrame(main); } main(); function ball(){ this.radius = 5; this.x = 300; this.y = 50; this.vx = 200; this.vy = 200; this.ax = 0; this.ay = 0; this.color = "red"; this.draw = function(){ ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc( this.x, this.y, this.radius, 0, 2 * Math.PI ); ctx.fill(); }; } function draw() { newBall = new ball(); newBall.draw(); } function update(dt) { newBall = new ball(); newBall.x += newBall.vx * dt; } In update(dt) function ball dosen't move and I don't know why...
There are several errors in your code: You're initializing variables outside of a function, always use an initializer (immediately invoked function) for that. As mentioned by kalley you are creating a new ball at the starting position for every draw instead of using a global object. Even if your ball would draw correctly it would be outside the drawing area within the next frame because Date.now() is measured in seconds (use .getMilliseconds()). Lastly the ball stays at the same position because the canvas isn't cleaned up after each draw. what you're looking for: function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); theBall.draw(); } There are several other things but this fiddle should do for now.
HTML5-Javascript animation not working on Mozilla but works on Chrome
I'm trying to make a simple animation for cow with HTML5 canvas. The code works for Google Chrome but does not work in Mozilla Firefox, Safari etc. My function (code) is given below: function animalView(objectToMove) { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'); this.a = 0; (function() { var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; window.requestAnimationFrame = requestAnimationFrame; })(); (function drawFrame() { window.requestAnimationFrame(drawFrame, canvas); context.save(); context.clearRect(0, 0, canvas.width, canvas.height); this.a += 10; objectToMove.tail = Math.sin( this.a ); objectToMove.draw(context); context.restore(); }()); } I noticed that the value of this.a is not being remembered when the call back function calls function().