Use Chrome DevTools for rendering measurement for HTML5 graphics - javascript

I would like to measure the performance between canvas and svg in HTML5.
I have done so far. I have created multiple circles in svg and canvas.
Both have a 500 x 500 Element width and height.
I found out I am measuring the scripting time. If I use the dev tools in Chrome, the scripting time is nearly equal to my measured time.
Now, how can I measure the rendering time? Would be a code with separate canvas and svg circle creation and devtools for rendering a good way to compare svg and canvas rendering performance?
<html>
<head>
<script type="text/javascript">
var svgNS = "http://www.w3.org/2000/svg";
function createCircle1() {
var t3 = performance.now();
for (var x = 1; x <= 1000; x++) {
for (var y = 1; y <= 100; y++) {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(x, y, 5, 0, 2 * Math.PI);
ctx.stroke();
}
}
var t4 = performance.now();
console.log("canvas time " + (t4 - t3) + " milliseconds.")
var t0 = performance.now();
for (var x = 1; x <= 1000; x++) {
for (var y = 1; y <= 100; y++) {
var myCircle = document.createElementNS(svgNS, "circle"); //to create a circle, for rectangle use rectangle
myCircle.setAttributeNS(null, "cx", x);
myCircle.setAttributeNS(null, "cy", y);
myCircle.setAttributeNS(null, "r", 5);
myCircle.setAttributeNS(null, "stroke", "none");
document.getElementById("mySVG").appendChild(myCircle);
}
}
var t1 = performance.now();
console.log("svg time " + (t1 - t0) + " milliseconds.")
}
</script>
</head>
<body onload="createCircle1();">
<svg id="mySVG" width="500" height="500" style="border:1px solid #d3d3d3;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #d3d3d3;"></canvas>
</body>
</html>
Somehow the scripting time and my measured performance time is different.
Can someone tell me if this performance comparison is useful?
I did the test multiple times, the performance time is always different but canvas is faster than svg in rendering and also in scripting.
Why in rendering? Scripting should be because of the DOM reference of svg?
This test I did with seperate svg and canvas, I just rendered first only svg, and in the next test only canvas.

The problem with SVG.
Below is a performance test of drawing a big circle on a canvas and a SVG image.
Both get about the same performance 30ms per circle on my machine and chrome.
Run the test and see the result. If you watch the progress you may notice that it begins to slow down a little. When the first test is run click the button again and this time you will notice that there is even more of a slow down.
Do a 3rd test and slower still, but the performance per circle for both canvas and SVG has not changed, where is the slow down coming from.
The DOM is not javascript.
The code run to add a node to the SVG does not care how many nodes the SVG has, but when add nodes to the SVG image and your code exits you have informed the DOM that your SVG element is dirty and needs to be redrawn.
I grouped the test into groups of ~10 before exiting. This means that for every ten circles added the DOM will redraw all the SVG nodes from scratch and outside the javascript context and your ability to measure or control it.
When you click test the second time the SVG already has 10000 circles, so after adding the first ten the DOM happily re-renders 10000+10 circles.
It is next to impossible to get an accurate measure of SVG performance.
Using timeline
I ran the code snippet below with timeline recording. I ran the test two times.
The next two images show the same period. The top one is at the start of the test and the next one is at the end of the second test.
I have mark the GPU sections that are likely involved in rendering the SVG. Note how they change from trivial to excessive.
This image shows one cycle of the test 10 renders in the second test cycle. The code execution is barely visible at ~1ms but the GPU is flat out with a huge 175ms devoted to drawing all the SVG circle again.
When you use SVG you must remember that when you make a change to it the DOM will re-render all of it. It does not care if it's visible or not. If you change the size it's redrawn.
To use SVG you must bundle all your calls into one execution context to get the best performance.
Canvas V SVG
Both SVG and Canvas use the GPU with very similar shaders to do the work. Drawing a circle in SVG or Canvas takes the same time. The advantage that the canvas has over SVG is that you control the rendering, what when and where. For SVG you only control content and have little say over rendering.
var running = false;
var test = function(){
if(running){
return;
}
var mySVG = document.getElementById("mySVG");
var myCanvas = document.getElementById("myCanvas");
var ctx = myCanvas.getContext("2d");
var myCircle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
running = true;
const testCount = 1000;
const groupCount = 10;
var times = [[],[]];
var names =["Canvas test.","SVG test."];
var indexs = [0,0];
var tests = [function(){
now = performance.now();
ctx.beginPath();
ctx.arc(250, 250, 250, 0, 2 * Math.PI);
ctx.fill();
return performance.now()-now;
},
function(){
now = performance.now();
var circle = myCircle.cloneNode();
circle.setAttributeNS(null, "cx", 250);
circle.setAttributeNS(null, "cy", 250);
circle.setAttributeNS(null, "r", 250);
circle.setAttributeNS(null, "stroke", "none");
mySVG.appendChild(circle);
return performance.now()-now;
}];
for(var i = 0; i < testCount; i ++){ // preallocate and zeor arrays
times[0][i] = 0;
times[1][i] = 0;
}
var testComplete = false;
function doTests(){
for(i = 0; i < groupCount; i ++){
var testIndex = Math.floor(Math.random()*2);
times[testIndex][indexs[testIndex]] = tests[testIndex]();
indexs[testIndex] += 1;
if(indexs[testIndex] >= testCount){
testComplete = true;
return;
}
}
}
function getResults(){
// get the mean
var meanA = 0;
var meanB = 0;
var varianceA = 0;
var varianceB = 0;
var totalA = 0;
var totalB = 0;
for(var i = 0; i < testCount; i ++){ // preallocate and zero arrays
totalA += i < indexs[0] ? times[0][i] : 0;
totalB += i < indexs[1] ? times[1][i] : 0;
}
meanA = Math.floor((totalA / indexs[0]) * 1000) / 1000;
meanB = Math.floor((totalB / indexs[1]) * 1000) / 1000;
for(var i = 0; i < testCount; i ++){ // preallocate and zero arrays
varianceA += i < indexs[0] ? Math.pow((times[0][i] - meanA),2) : 0;
varianceB += i < indexs[1] ? Math.pow((times[1][i] - meanB),2) : 0;
}
varianceA = Math.floor((varianceA / indexs[0]) * 1000) / 1000;
varianceB = Math.floor((varianceB / indexs[1]) * 1000) / 1000;
result1.textContent = `Test ${names[0]} Mean : ${meanA}ms Variance : ${varianceA}ms Total : ${totalA.toFixed(3)}ms over ${indexs[0]} tests.`;
result2.textContent = `Test ${names[1]}. Mean : ${meanB}ms Variance : ${varianceB}ms Total : ${totalB.toFixed(3)}ms over ${indexs[1]} tests.`;
}
function test(){
doTests();
var p = Math.floor((((indexs[0] + indexs[1]) /2)/ testCount) * 100);
if(testComplete){
getResults();
p = 100;
running = false;
}else{
setTimeout(test,10);
}
progress.textContent = p+"%";
}
test()
}
startBut.addEventListener("click",test);
#ui {
font-family : Arial;
}
<div id="ui">
<h3>Comparative performance test</h3>
Adding circles to canvas V adding circles to SVG.<br> The test adds 1000 * 10 circles to the canvas or SVG with as much optimisation as possible and to be fair.<br>
<input id="startBut" type="button" value = "Start test"/>
<div id="progress"></div>
<div id="result1"></div>
<div id="result2"></div>
<h3>SVG element</h3>
<svg id="mySVG" width="500" height="500" style="border:1px solid #d3d3d3;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>
<h3>Canvas element</h3>
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #d3d3d3;"></canvas>
</div>

Related

If noseX is on cnvSection[i], draw img[i] on the canvas

I'm making a sketch with the p5.js library and ml5's poseNet. I have the variable noseX — which indicates the x-coordinate position of the nose on the canvas — and a preloaded array of 60 images. I would like to vertically divide the canvas into 60 sections. For each cnvSection noseX is on, I want the image img[i] corresponding to that cnvSection[i] to to be drawn on the canvas.
So basically if noseX is on cnvSection[5], draw img[5] on the canvas etc.
Here's my function, at the moment I have only been able to draw the vertical lines indicating the canvas sections.
let cnvWidth = 1440;
let cnvHeight = 900;
function nosePosition() {
let sectionWidth = cnvWidth / 60;
let cnvSection = [];
for (let i = 0; i < cnvWidth; i = i + sectionWidth) {
line(i, 0, i, cnvHeight);
}
}
Many thanks!
Try the following:
noseX = 50;
function setup() {
createCanvas(700, 400);
}
function draw() {
background(220);
nosePosition();
fill('red');
ellipse(noseX, height / 2, 6);
}
function nosePosition() {
let sectionWidth = width / 60;
for (let i = 0; i < width; i += sectionWidth) {
line(i, 0, i, height);
}
const noseSection = floor(noseX / sectionWidth);
rect(noseSection * sectionWidth, 0, sectionWidth, height);
}
To simplify I use a hardcoded x value for the nose. In your case that would come from ml5 posenet. You don't need the sections in an array. You can simlpy calculate in which section the nose currently is and then draw the image accordingly. I am drawing a rect - again, to simplify the example.
Maybe copy and paste this into the p5 web editor and play around with the noseX value and see if that works for you.

how to see each pixel being changed on the canvas html5

I've created a filter in javascript which inverts the colors of image i.e creates a negative image, now when I run it in the browser it takes time to process and then returns the final negative image. How can I see each pixel of the image being inverted and not just the final inverted image? Instead of waiting for the the code to be implemented on the whole pixel array and then see its effects, I want to see each pixel being changed by the code till the last pixel.
var imgData = ctx.getImageData(0,0,x.width,x.height);
var d = imgData.data;
for (var i=0; i< d.length; i+=4) {
d[i] = 255 - d[i];
d[i+1] = 255 - d[i+1];
d[i+2] = 255 - d[i+2];
}
ctx.putImageData(imgData,0,0);
NEW CODE
invert(d,0);
function invert(d,i){
if(i < d.length){
d[i] = 255 - d[i];
d[i+1] = 255 - d[i+1];
d[i+2] = 255 - d[i+2];
d[i+3] = d[i+3];
//alert(i);
var n=i/4;
var h=parseInt(n/x.width);
var w = n - h*x.width;
ctx.fillStyle='rgba('+d[i]+','+d[i+1]+','+d[i+2]+','+d[i+3]/255+')';
ctx.fillRect(w,h,1,1);
//if(i>91000){alert(i);}
setTimeout(invert(d,i+4),50);
}
else{return ;}
}
You need to use an asynchronous "loop" so you can update some pixels, let the display update the result, then continue.
JavaScript is single threaded so nothing will be updated until the current loop finishes as the thread is occupied with that.
Watching the paint dry
Here is one approach you can use. You define a "batch" size in number of pixels you want to invert (1 is valid if you want to see each pixel, but this can take a long time:
ie. 16.67ms x total number of pixels.
So if you want to display each single pixel update with an image of 640x400 (as in the demo below) then it would take:
640 x 400 x 16.67ms = 4,267,520 ms, or more than an hour
Something to have in mind (the display cannot update faster than per 16.67ms = 60 fps). Below we use 128 pixels per batch.
Live example
Note that the batch value must match the width of the image. F.ex. if your image is 640 pixels wide you can use 1, 5, 10, 20, .. 64, 128 etc.
If you want widths that do not necessarily divide on anything but 1 or fractional values, you have to do a simple calculation to limit the last batch of one line as getImageData() require the arguments to define the area inside an image. Or, just do line by line...
You can also use a "batch" value for vertical tiles (box is probably a better term in that case).
var img = new Image();
img.crossOrigin = "";
img.onload = painter;
img.src = "http://i.imgur.com/Hl3I0cx.jpg";
function painter() {
// setup canvas and image
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d");
// set canvas size = image size
canvas.width = this.naturalWidth; canvas.height = this.naturalHeight;
// draw in image
ctx.drawImage(this, 0, 0);
// prepare loop
var batch = 128,
x = 0, y = 0,
w = canvas.width - batch,
h = canvas.height;
(function asyncUpdate() {
// do one batch only
var idata = ctx.getImageData(x, y, batch, 1), // get a bacth of pixels
data = idata.data,
i = 0, len = data.length;
while(i < len) { // invert the batch
data[i] = 255 - data[i++];
data[i] = 255 - data[i++];
data[i] = 255 - data[i++];
i++
}
ctx.putImageData(idata, x, y); // update bitmap
x += batch;
if (x > w) { // check x pos
x = 0;
y++;
}
if (y < h) { // new batch?
requestAnimationFrame(asyncUpdate); // let display update before next
}
else {
// use a callback here...
console.log("Done");
}
})();
}
<canvas></canvas>
You could context.fillRect each newly changed pixel on the canvas as it's calculated.
Something like this (warning: untested code, may need tweeks!):
var n=i/4;
var y=parseInt(n/canvas.width);
var x=n-y*canvas.width;
context.fillStyle='rgba('+d[i]+','+d[i+1]+','+d[i+2]+','+d[i+3]/255')';
context.fillRect(x,y,1,1);
Here's demo code that first does the inverting and then shows the effect over time:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var nextTime=0;
// a new line of converted image will be displayed
// after this delay
var delay=1000/60*2;
var y=0;
var imgData,d;
var img=new Image();
img.crossOrigin='anonymous';
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/sun.png";
function start(){
cw=canvas.width=img.width;
ch=canvas.height=img.height;
ctx.drawImage(img,0,0);
imgData=ctx.getImageData(0,0,cw,ch);
d=imgData.data;
for (var i=0; i< d.length; i+=4) {
d[i] = 255 - d[i];
d[i+1] = 255 - d[i+1];
d[i+2] = 255 - d[i+2];
}
requestAnimationFrame(animate);
}
function animate(time){
if(time<nextTime){requestAnimationFrame(animate); return;}
nextTime=time+delay;
for(var x=0;x<cw;x++){
var i=(y*cw+x)*4;
ctx.fillStyle='rgba('+d[i]+','+d[i+1]+','+d[i+2]+','+d[i+3]/255+')';
ctx.fillRect(x,y,1,1);
}
if(++y<ch){
requestAnimationFrame(animate);
}else{
ctx.putImageData(imgData,0,0);
}
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

Smoothly predicting the location of the mouse cursor in javascript

I want to draw a rectangle on a canvas around the mouse cursor that smoothly follows the cursor. Unfortunately, the mousemoved event doesn't fire quickly enough, and the drawing constantly trails behind it. So I'm assuming I need to predict where the mouse is, and draw the rectangle at that point. I'm trying to write a simple library to abstract that away, but it's not tracking as closely as I'd like for fast movements (in fact, fast movements are jittery). For slow movements, it tracks fairly well, and better than the simple solution of using the raw mouse coordinates.
The basic idea is that mousemove updates a couple of external variables with the current position of the mouse. A requestAnimationFrame loop (the Watcher function) tracks these variables and their previous values over time to calculate the speed the mouse is moving at (in the x axis). When the PredictX function is called, it returns the current x position, plus the last change in x multiplied by the speed. A different reqeustAnimationFrame loop moves the rectangle based on the predicted x value.
var MouseLerp = (function () {
var MOUSELERP = {};
var current_x = 0;
var last_x = 0;
var dX = 0;
var last_time = 0;
var x_speed = 0;
var FPS = 60;
function Watcher(time) {
var dT = time - last_time
if (dT > (1000 / FPS)) {
dX = last_x - current_x;
last_x = current_x;
x_speed = dX / dT
last_time = time;
}
requestAnimationFrame(Watcher);
}
MOUSELERP.PredictX = function () {
return Math.floor((dX * x_speed) + current_x);
}
MOUSELERP.Test = function () {
var target_element = $(".container")
target_element.append('<canvas width="500" height="500" id="basecanvas"></canvas>');
var base_ctx = document.getElementById("basecanvas").getContext("2d");
var offset = target_element.offset()
var offset_x = offset.left;
var offset_y = offset.top;
var WIDTH = $(window).width();
var HEIGHT = $(window).height();
var FPS = 60;
var t1 = 0;
function updateRect(time) {
var dT = time - t1
if (dT > (1000 / FPS)) {
base_ctx.clearRect(0, 0, WIDTH, HEIGHT)
base_ctx.beginPath();
base_ctx.strokeStyle = "#FF0000";
base_ctx.lineWidth = 2;
base_ctx.rect(MOUSELERP.PredictX(), 100, 100, 100)
base_ctx.stroke();
t1 = time;
}
requestAnimationFrame(updateRect)
}
updateRect();
$(target_element).mousemove(function (event) {
current_x = event.pageX - offset_x;
});
requestAnimationFrame(Watcher);
}
MOUSELERP.Test()
return MOUSELERP;
}())
What am I doing wrong?
Here's a jsfiddle of the above: http://jsfiddle.net/p8Lr224p/
Thanks!
The mouse pointer will always be quicker than drawing, so your best bet is not to give the user's eye a reason to perceive latency. So, turn off the mouse cursor while the user is drawing. Draw a rectangle at the mouse position to visually act as the mouse cursor.
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var mouseX=0;
var mouseY=0;
canvas.style.cursor="none";
$("#canvas").mousemove(function(e){handleMouseMove(e);});
function handleMouseMove(e){
ctx.clearRect(mouseX-1,mouseY-1,9,9);
mouseX=e.clientX-offsetX;
mouseY=e.clientY-offsetY;
ctx.fillRect(mouseX,mouseY,8,8);
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Less 'lagging' when mouse is invisible & canvas draws cursor.</h4>
<canvas id="canvas" width=300 height=300></canvas>
,

html 5 canvas javascript rain animation(how to implement efficiently and easily!)

I tried both of these in canvas and nothing showed, also I doubt it is even efficient :/. I am trying to make rain that comes down the screen.. Wondering what is the most efficient way of doing this. I am a beginner at animation and would really appreciate help.
I suspect that creating a rain object would be best, each with the quality of coming down the screen then coming to the top and then an array with them...maybe with random x values withing the canvas width and y values of 0 but I don't know how to implement that. Please help!
xofRain = 20;
startY = 0;
ctx.beginPath();
ctx.moveTo(xofRain, startY);
ctx.lineTo(xofRain, startY + 20);
ctx.closePath();
ctx.fillStyle = "black";
ctx.fill();
function rain(xofRain){
startY = canvas.height();
ctx.moveTo(xofRain, startY);
ctx.beginPath();
ctx.lineTo(xofRain, startY + 3);
ctx.closePath();
ctx.fillStyle = "blue";
ctx.fill();
}
Here comes your answer, this snow rain is created using pure HTML5 Canvas, the technique used to achieve this animation is called "Double Buffer Animation". First it is good to know what is Double Buffer animation technique.
Double Buffer Technique: This is an advanced technique to make animation clear and with less flickers in it. In this technique 2 Canvas is used, one is displayed on webpage to show the result and second one is used to create animation screens in backed process.
How this will help full, suppose we have to create a animation with very high number of move, as in our Snow Fall example, there are number of Flakes are moving with there own speed, so keep them moving, we have to change position of each flake and update it on the canvas, this is quite heavy process to deal with.
So Now instead of updating each Flake directly on our page canvas, we will create a buffer Canvas, where all these changes take place and we just capture a Picture from Buffer canvas after 30ms and display it on our real canvas.
This way our animation will be clear and without flickers. So here is a live example of it.
http://aspspider.info/erishaan8/html5rain/
Here is the code of it:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>HTML5 Rain</title>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<style>
article, aside, figure, footer, header, hgroup,
menu, nav, section { display: block; }
</style>
<script type="text/javascript">
var canvas = null;
var context = null;
var bufferCanvas = null;
var bufferCanvasCtx = null;
var flakeArray = [];
var flakeTimer = null;
var maxFlakes = 200; // Here you may set max flackes to be created
function init() {
//Canvas on Page
canvas = document.getElementById('canvasRain');
context = canvas.getContext("2d");
//Buffer Canvas
bufferCanvas = document.createElement("canvas");
bufferCanvasCtx = bufferCanvas.getContext("2d");
bufferCanvasCtx.canvas.width = context.canvas.width;
bufferCanvasCtx.canvas.height = context.canvas.height;
flakeTimer = setInterval(addFlake, 200);
Draw();
setInterval(animate, 30);
}
function animate() {
Update();
Draw();
}
function addFlake() {
flakeArray[flakeArray.length] = new Flake();
if (flakeArray.length == maxFlakes)
clearInterval(flakeTimer);
}
function blank() {
bufferCanvasCtx.fillStyle = "rgba(0,0,0,0.8)";
bufferCanvasCtx.fillRect(0, 0, bufferCanvasCtx.canvas.width, bufferCanvasCtx.canvas.height);
}
function Update() {
for (var i = 0; i < flakeArray.length; i++) {
if (flakeArray[i].y < context.canvas.height) {
flakeArray[i].y += flakeArray[i].speed;
if (flakeArray[i].y > context.canvas.height)
flakeArray[i].y = -5;
flakeArray[i].x += flakeArray[i].drift;
if (flakeArray[i].x > context.canvas.width)
flakeArray[i].x = 0;
}
}
}
function Flake() {
this.x = Math.round(Math.random() * context.canvas.width);
this.y = -10;
this.drift = Math.random();
this.speed = Math.round(Math.random() * 5) + 1;
this.width = (Math.random() * 3) + 2;
this.height = this.width;
}
function Draw() {
context.save();
blank();
for (var i = 0; i < flakeArray.length; i++) {
bufferCanvasCtx.fillStyle = "white";
bufferCanvasCtx.fillRect(flakeArray[i].x, flakeArray[i].y, flakeArray[i].width, flakeArray[i].height);
}
context.drawImage(bufferCanvas, 0, 0, bufferCanvas.width, bufferCanvas.height);
context.restore();
}
</script>
</head>
<body onload="init()">
<canvas id="canvasRain" width="800px" height="800px">Canvas Not Supported</canvas>
</body>
</html>
Also if you find this help full, accept as Answer and make it up. o_O
Cheers!!!
I'm not sure what "most efficient" is. If it was me I'd do it in WebGL but whether or not that's efficient is not clear to me.
In either case I'd try to use a stateless formula. Creating and updating state for every raindrop is arguably slow.
const ctx = document.querySelector("canvas").getContext("2d");
const numRain = 200;
function render(time) {
time *= 0.001; // convert to seconds
resizeCanvasToDisplaySize(ctx.canvas);
const width = ctx.canvas.width;
const height = ctx.canvas.height;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, width, height);
resetPseudoRandom();
const speed = time * 500;
ctx.fillStyle = "#68F";
for (let i = 0; i < numRain; ++i) {
const x = pseudoRandomInt(width);
const y = (pseudoRandomInt(height) + speed) % height;
ctx.fillRect(x, y, 3, 8);
}
requestAnimationFrame(render);
}
requestAnimationFrame(render);
let randomSeed_ = 0;
const RANDOM_RANGE_ = Math.pow(2, 32);
function pseudoRandom() {
return (randomSeed_ =
(134775813 * randomSeed_ + 1) %
RANDOM_RANGE_) / RANDOM_RANGE_;
};
function resetPseudoRandom() {
randomSeed_ = 0;
};
function pseudoRandomInt(n) {
return pseudoRandom() * n | 0;
}
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
}
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas></canvas>
Note that I could have used ctx.moveTo(x, y); ctx.lineTo(x, y + 8); for each line and then at the end of the loop called ctx.stroke(). I didn't do that because I'm assuming it would be less efficient than using ctx.fillRect. In order for the canvas to draw lines it actually has to allocate a dynamic path (you call ctx.beginPath). It then has to record all the lines you add. Then it has to expand those lines into vertices of various kinds to rasterize the lines. You can basically see the various algorithms it uses here. Conversely none of that has to happen with ctx.fillRect. No allocations have to happen (not saying they don't happen, just saying they don't have to). The canvas can just use a single pre-allocated quad and draw it on the GPU by passing the correct matrix to draw whatever rectangle you ask of it. Of course they're might be more overhead calling ctx.fillRect 200 times rather than ctx.moveTo, ctx.lineTo 200s + ctx.stroke once but really that's up to the browser.
The rain above may or may not be a good enough rain effect. That wasn't my point in posting really. The point is efficiency. Pretty much all games that have some kind of rain effect do some kind of stateless formula for their rain. A different formula would generate different or less repetitive rain. The point is it being stateless.

HTML 5 canvas animation - objects blinking

I am learning ways of manipulating HTML 5 Canvas, and decided to write a simple game, scroller arcade, for better comprehension. It is still at very beginning of development, and rendering a background (a moving star field), I encountered little, yet annoying issue - some of the stars are blinking, while moving. Here's the code I used:
var c = document.getElementById('canv');
var width = c.width;
var height = c.height;
var ctx = c.getContext('2d');//context
var bgObjx = new Array;
var bgObjy = new Array;
var bgspeed = new Array;
function init(){
for (var i = 1; i < 50; i++){
bgObjx.push(Math.floor(Math.random()*height));
bgObjy.push(Math.floor(Math.random()*width));
bgspeed.push(Math.floor(Math.random()*4)+1);
}
setInterval('draw_bg();',50);
}
function draw_bg(){
var distance; //distace to star is displayed by color
ctx.fillStyle = "rgb(0,0,0)";
ctx.fillRect(0,0,width,height);
for (var i = 0; i < bgObjx.length; i++){
distance = Math.random() * 240;
if (distance < 100) distance = 100;//Don't let it be too dark
ctx.fillStyle = "rgb("+distance+","+distance+","+distance+")";
ctx.fillRect(bgObjx[i], bgObjy[i],1,1);
bgObjx[i] -=bgspeed[i];
if (bgObjx[i] < 0){//if star has passed the border of screen, redraw it as new
bgObjx[i] += width;
bgObjy[i] = Math.floor(Math.random() * height);
bgspeed[i] = Math.floor (Math.random() * 4) + 1;
}
}
}
As you can see, there are 3 arrays, one for stars (objects) x coordinate, one for y, and one for speed variable. Color of a star changes every frame, to make it flicker. I suspected that color change is the issue, and binded object's color to speed:
for (var i = 0; i < bgObjx.length; i++){
distance = bgspeed[i]*30;
Actually, that solved the issue, but I still don't get how. Would any graphics rendering guru bother to explain this, please?
Thank you in advance.
P.S. Just in case: yes, I've drawn some solutions from existing Canvas game, including the color bind to speed. I just want to figure out the reason behind it.
In this case, the 'Blinking' of the stars is caused by a logic error in determining the stars' distance (color) value.
distance = Math.random() * 240; // This is not guaranteed to return an integer
distance = (Math.random() * 240)>>0; // This rounds down the result to nearest integer
Double buffering is usually unnecessary for canvas, as browsers will not display the drawn canvas until the drawing functions have all been completed.
Used to see a similar effect when programming direct2d games. Found a double-buffer would fix the flickering.
Not sure how you would accomplish a double(or triple?)-buffer with the canvas tag, but thats the first thing I would look into.

Categories