Canvas pre rendering? - javascript

Is there any point in pre-rendering images on canvas?
An example,
var img; // Img object
var pre = document.createElement("canvas");
pre.width = img.width;
pre.height = img.height;
var precon = pre.getContext("2d");
precon.drawImage(img, 0, 0);
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
for(var i =0; i < 10000; ++i) {
ctx.drawImage(pre, Math.random() * canvas.width, Math.random() * canvas.height);
}
I don't see the point as you are still calling context.drawImage no matter what you do, unless the canvas api is faster drawing an image from a canvas object rather than image object?

Firstly, I must say that your example is not suitable to highlight the need and benefits of canvas pre-rendering.
I'll give you a better example were you need to draw multiple times something that requires heavy computation on a canvas.
Let's say you have this draw function :
function complexDraw(ctx){
ctx.drawImage(img, width, height);
// heavy computation goes here
// some transforms maybe
ctx.ctx.setTransform(-1, 0, 0, 1, 200, 200);
ctx.fillStyle = "rgba(100, 100, 255, 0.5)";
ctx.fillRect(50, 50, 100, 100);
//maybe draw another img/video/canvas
ctx.drawImage(anotherImg, width, height);
// ...
}
function draw(){
complexDraw(ctx);
}
Now let's say you want to show the current time on the canvas too. That means that we're going to add this at the bottom of our draw function :
function drawTime(ctx){
ctx.fillText(new Date().getTime(), 10, 50);
}
And now our draw function looks like this :
function draw(){
complexDraw(ctx);
drawTime(ctx);
}
Since you want to always show the current time, you need to call the draw function every second :
setInterval(draw, 1000);
This actually means that every second you are doing some heavy computation just to update a silly little text.
If only there could be a way to split the draw function and compute only the things that need computing (the ones that change)... but there is: say hello to canvas pre-rendering!
The key idea is to draw the part that doesn't change (and doesn't need to be re-computed) on a separate canvas - let's call it cacheCanvas - and just copy it's content on our app's canvas whenever we want to redraw stuff :
// suppose we have a `clone` function
var cacheCanvas = clone(canvas),
cacheCtx = cacheCanvas.getContext('2d');
// let's draw our complex stuff on the cacheCanvas
complexDraw(cacheCtx);
// modify our main `draw` function to copy the result of the `complexDraw`
// function, not to call it
function draw(){
ctx.drawImage(cacheCanvas, width, height);
drawTime();
}
And now we're basically redrawing the whole canvas each second, but we're not re-computing all the heavy work in complexDraw.
I just want to note that most of the canvas based games can't run at 60fps (redraw 60 times per second) without doing some performance boost with pre rendering or another technique called canvas layering (which is also worth looking into).

Related

Canva image dont display the image sometime using drawImage() method? [duplicate]

This question already has an answer here:
CanvasContext2D drawImage() issue [onload and CORS]
(1 answer)
Closed 3 years ago.
I want just to draw an image on canva using drawImage() method .But it happen sometime that the image is not drawn . even when I use the onload event it still fail to display the image.
Before I ask my question I want just to precise that the image is drawn somtimes and not drawn sometime .So what is the reason of the this problem and how can I tackle or fix it .
let imagez = new Image();
imagez.src="photo/run.png";
context.drawImage(imagez,10,10,50,60); // it does not draw the image always. why?
I expect the image to be drawn whenever I refresh the page
That most likely happens because the drawImage() method is called before the actual image is completely loaded.
Wait for it to finish loading before you call drawImage().
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);
canvas.width = 400;
canvas.height = 300;
var context = canvas.getContext("2d");
let imagez = new Image();
imagez.onload = function() {
context.drawImage(imagez, 10, 10, 50, 60);
}
imagez.src = "https://picsum.photos/400/300";
For this you have to wait for the image(s) to load into memory before they are available to draw to the canvas.
A simple loadIMAGE() function to achieve this might look something like this...
function loadIMAGE(path, x, y, w, h) {
var img = new Image();
img.addEventListener("load", function(e) {
// 'this' === the image Object
context.drawImage(this, x, y, w, h);
}, !1);
img.src = path;
}
// USAGE
loadIMAGE("photo/run.png", 10, 10, 50, 60);
It is important that the event-Handling function is defined before the src value is assigned.
Hope that helped :)
You are attempting to draw the image without knowing if it was successfully loaded...
Here is a working sample, using the onload event
const canvas = document.getElementById("gameArea");
context = canvas.getContext("2d");
let imagez = new Image();
imagez.onload = function () {
context.drawImage(imagez, 10, 10, 50, 60);
}
imagez.src = "https://upload.wikimedia.org/wikipedia/commons/e/e2/3D_Gyroscope.png";
<canvas id=gameArea></canvas>

Creating objects in javascript as functions - and control them with arrow keys in game

I am trying to make a simple "game" in HTML5 (mainly Javascript), where you simply move a ball around.
I would like to create a ball (which can be moved with arrow keys) by drawing a circle on canvas. Is this possible, or is another approach simpler?
I would like to create it as a function, so that I have a means for selecting the ball.
Anyways, I am able to draw a triangle, but not a circle. This is the code:
var canvas = document.getElementById("mycanvas");
var ctx = canvas.getContext("2d");
canvas.width = canvas.height = 500;
function circle() {
// the circle - DOESNT SHOW UP
ctx.beginPath();
ctx.arc(50, 50, 25, 0, Math.PI * 2, true);
ctx.fillStyle = "blue";
ctx.fill();
}
cicle();
function drawTriangle() {
// the triangle - works perfectly
ctx.beginPath();
ctx.moveTo(200, 100);
ctx.lineTo(170, 150);
ctx.lineTo(230, 150);
ctx.closePath();
// the fill color
ctx.fillStyle = "rgba(255, 204, 0, 1)";
ctx.fill();
}
drawTriangle();
Can anyone figure, why the circle doesn't appear?
IMPORTANT: The circle appears perfectly, when it is NOT part of a function
P.S.: Im very new to web development etc.
Thanks for any advice
Is it possible that that is due to the typo when you call the circle function? So circle() instead of cicle()
Well, it is quite simple. When you call a function in javascript, you must spell it the same as in the function declaration otherwise you will be calling a function that does not exist.
function circle(){
//code to draw circle
}
//then when i want to call circle function i do this
circle();
//this calls non-existant "cicle" function
cicle();
So, there is nothing wrong with your function, just how you called it.

canvas sprite sheet error

I found a lovely code snippet on canvas spritesheet animations. this is ts:
http://jsfiddle.net/m1erickson/h85Gq/
I tried to beautify this code, by writing an animate function that accepts Image objects, so that I can animate multiple images in my canvas simultaneously. This is my attempt at it:
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var spritePosition=0;
var spriteWidth=100;
var spriteHeight=100;
var spriteCount=40;
var spritePlayCount=0;
var maxSpritePlays=2;
var objectS=new Image();
objectS.src="sprites/first.png";
var fps = 50;
function animate(sprite) {
setTimeout(function() {
if(spritePlayCount<maxSpritePlays){
requestAnimationFrame(animate);
}
// Drawing code goes here
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(sprite,spritePosition*spriteWidth, 0,spriteWidth, spriteHeight, 0,0,spriteWidth, spriteHeight);
spritePosition++;
if(spritePosition>spriteCount-1){
spritePosition=0;
spritePlayCount++;
}
}, 1000 / fps);
}
objectS.onload=function(){
animate(objectS);
}
}); // end $(function(){});
I am getting a much observed error, but I cant seem to find the fix for it:
index3.html:59 Uncaught TypeError: Failed to execute 'drawImage' on
'CanvasRenderingContext2D': The provided value is not of type
'(CSSImageValue or HTMLImageElement or HTMLVideoElement or
HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
Can you help me out finding my bug?
OMDG!
quote OP "Imagine having 50 spritesheets you want to animate."
50!
Looking at the code "accepted answer version"
function animate(sprite) {
// create a timer event to fire in 1/50th second
setTimeout(function() {
if (spritePlayCount < maxSpritePlays) {
// create a animation frame event that may fire at some time
// between 0 and 16ms or 32ms from now
requestAnimationFrame(function() {
animate(sprite);
});
}
// Drawing code etc... Make canvas dirty
// exiting with dirty canvas outside the requestAnimationFrame
// forces DOM to swap canvas backbuffer immediately on exit.
}, 1000 / 50);
}
This is the worst possible way to animate one, let alone more than one sprite.
The timing is out of sync with the display refresh.
Using requestAnimationFrame's callback to create a timed event that renders, completely negates the reason for using requestAnimationFrame. requestAnimationFrame tells the DOM that what you draw in the callback is part of an animation. Using a timer to draw means you don't draw anything in the requested frame making the request redundant.
requestAnimationFrame does its best to get all the callbacks in before the next display refresh (1/60th) but it will delay the callback if there is no time to update the DOM (swap all dirty buffers) before the next refresh. You have no control over the timing of your animation, they may fire anywhere from 20ms to 36ms or more with no consistency over time.
By using timers to draw to the canvas you are forcing a backbuffer swap for each sprite you draw. This is an expensive process and will severely limit the speed that you can draw sprites at, and cause sprites to flicker and shear randomly during animation.
The best practice way.
Use a single animation loop triggered by requestAnimationFrame.
Use an array to store all sprites and update them in the main loop if/as needed.
Only render from within the main loop. Do not render or do anything (at a regular interval) to the DOM outside the main loop's execution.
Don't render inside events like timers, IO, or any other rapidly firing event.
You'll also need to pass the sprite parameter when calling the animate function using requestAnimationFrame.
$(function() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var spritePosition = 0;
var spriteWidth = 100;
var spriteHeight = 100;
var spriteCount = 40;
var spritePlayCount = 0;
var maxSpritePlays = 2;
var objectS = new Image();
objectS.src = "sprites/first.png";
var fps = 50;
function animate(sprite) {
setTimeout(function() {
if (spritePlayCount < maxSpritePlays) {
requestAnimationFrame(function() {
animate(sprite);
});
}
// Drawing code goes here
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(sprite, spritePosition * spriteWidth, 0, spriteWidth, spriteHeight, 0, 0, spriteWidth, spriteHeight);
spritePosition++;
if (spritePosition > spriteCount - 1) {
spritePosition = 0;
spritePlayCount++;
}
}, 1000 / fps);
}
objectS.onload = function() {
animate(objectS);
};
});

Does canvas drawing work in Ionic 2?

I am currently using a html5 canvas and by using ionic 2 (tap)="markPoint($event)" on the canvas in html, I am getting the position of the tap event. Below is the function which should place the mark:
public markPoint(event) {
var position = event.center;
let ctx = this.canvas.getContext('2d');
ctx.beginPath();
ctx.arc(position.x, position.y, 20, 0, 2 * Math.PI);
ctx.fillStyle = '#00DD00';
ctx.fill();
}
I am setting the canvas like so, where canvas is the id set in the html:
this.canvas = document.getElementById('canvas');
I don't see an issue with this code, however I am also not sure whether or not this is the best way to make marks within an application in ionic 2. Do you know if this should work, and if not why? Also if there are any better ways it would be awesome to here about them.
First of all we have to make more typescript<6> friendly. It's not enough just to get the canvas object like another HTML element using the id. In this case we should help a little, so my first change will be:
this.canvas = document.getElementById('canvas');
FOR =>
this.canvas = <HTMLCanvasElement>document.getElementById('canvas');
Then your function will look something like this:
public markPoint(event) {
var position = event.center;
let ctx: CanvasRenderingContext2D = this.canvas.getContext("2d");
ctx.beginPath();
ctx.arc(position.x, position.y, 20, 0, 2 * Math.PI);
ctx.fillStyle = '#00DD00';
ctx.fill();
}
Hope this help.

How can I write this canvas painting loop with a JS iterator?

I'm reading through an article about continuously drawing frames of an HTML5 video player on canvas. I heard about JS iterators a while back and I think there's supposedly a performance advantage, so I'd like to know how to write this set of canvas painting functions into an Iterator:
function canvasVideo(){
//DECLARE VARIABLES
var $videoPlayer = $('#Video-Player');
var $canvasPlayer = $('#Canvas-Player');
var videoPlayer = $videoPlayer[0];
var canvasPlayer = $canvasPlayer[0];
var context = canvasPlayer.getContext("2d");
//DEFINE FUNCTIONS
//draw the video
function draw() {
//Background
context.fillStyle = '#ffffaa';
context.fillRect(0, 0, canvasPlayer.width, canvasPlayer.height);
//video
context.drawImage(videoPlayer , 0, 0);
}
//update canvas at set interval with video frames
function canvasRun(int){
//set timer with interval
var canvasTimer = setTimeout(canvasRun, int);
//draw the video frame
draw();
}
function videoLoaded(event){
canvasRun(20);
}
//HANDLE EVENTS
$videoPlayer.on("canplaythrough", function (){
videoLoaded();
});
}
How can this be done with JS Iterators?
I've tried using tutorials to learn iterators, but the generic examples have confused me, and think seeing it done with an example like this would help me learn.

Categories

Resources