If I create a canvas element via:
var canvas = document.createElement('canvas');
And then draw to it. If I then keep a reference to that canvas will that use more memory than converting the canvas content to a data url and creating an image element with that data and releasing the reference to the canvas?
Which is less memory consuming? A canvas element or an image element, both the same dimensions with the same image data?
Using this html test page:
<html>
<head>
<script type="text/javascript">
window.onload = function () {
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var img = new Image;
img.src = "http://blog.buzzbuzzhome.com/wp-content/uploads/2012/06/Canadian-Flag-canada-729711_1280_1024.jpg";
ctx.drawImage(img, 0, 0);
}
</script>
</head>
<body>
<canvas id="canvas" width="1280" height="1024"></canvas>
<img src="http://blog.buzzbuzzhome.com/wp-content/uploads/2012/06/Canadian-Flag-canada-729711_1280_1024.jpg">
</body>
</html>
The memory output from a Google Chrome profile snapshot is as follows:
Google Chrome -> Developer Tools -> Profiles -> Take Head Snapshot -> Class Filter:HTML
The canvas has a smaller retained size (132 to 152 of the img), but a look into what's left over from rendering the image reveals more:
Class Filter:canvas
IMHO you're going to pay an overhead for rendering to the canvas in most major browsers.
Whether the mess gets cleaned up when your reference is released and your final memory usage is lower is anyone's guess.
I realize I didn't do it strictly the way you intended, but I felt a side by side comparison would give you some idea what's involved.
I suppose if loading the image via canvas is the only way to go, perhaps you're performing some manipulation before outputting the final result, then leaving it in the canvas and attempting to nullify all references for garbage collection will be slightly less expensive for the client.
This test was only done in Google Chrome and I cannot verify anything for other browsers.
Try it yourself!
Related
I am using ESRI basemaps with Mapbox-GL-JS. I am trying to capture a screenshot of the map using the following code:
this.map.getCanvas().toBlob(function (blob) {
canvasContext.strokeStyle = '#CCCCCC';
canvasContext.strokeRect(leftPosition, topPosition, width, height);
var img = new Image();
img.setAttribute("crossOrigin", "anonymous");
var srcURL = URL.createObjectURL(blob);
img.onload = function () {
canvasContext.drawImage(img, leftPosition, topPosition, width, height);
URL.revokeObjectURL(srcURL);
};
img.src = srcURL;
});
I am not able to figure out why the attribution on the Map is not getting captured in the screenshot. I understand that here I am just trying to get the canvas of the map. I even tried adding text elements to the map canvas and that doesn't work either. I have markers & routes, which get in the image correctly. I also tried using the Mapbox basemap and try the same, but faced the same issue.
Any help is highly appreciated!
map.getCanvas() will only return the Map's canvas not any of the HTML Elements which sit over the map like the controls, Mapbox logo or attribution text. Sam Murphy has been working on an example showing how to capture the Map including the Logo and Attribution text to an image which you can see at https://github.com/mapbox/mapbox-gl-js/pull/6518/files.
Since we can't easily capture an HTML Element to an image in JavaScript the attribution text is re-created in a canvas drawn into the Image.
Can any one tell me how to place my html content on a canvas.And if we can do that, will the properties and events of those elements works or not, and also I have animations drawn on that canvas.
From this article on MDN:
You can't just draw HTML into a canvas. Instead, you need to use an
SVG image containing the content you want to render. To draw HTML
content, you'd use a element containing the HTML, then
draw that SVG image into your canvas.
It than suggest you follow these steps:
The only really tricky thing here—and that's probably an
overstatement—is creating the SVG for your image. All you need to do
is create a string containing the XML for the SVG and construct a Blob
with the following parts.
The MIME media type of the blob should be "image/svg+xml".
The element.
Inside that, the element.
The (well-formed) HTML itself, nested inside the .
By using a object URL as described above, we can inline our HTML
instead of having to load it from an external source. You can, of
course, use an external source if you prefer, as long as the origin is
the same as the originating document.
The following example is provided (you can see more information about this in this blog by Robert O'Callahan):
DEMO
const ctx = document.getElementById("canvas").getContext("2d");
const data = `
<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'>
<foreignObject width='100%' height='100%'>
<div xmlns='http://www.w3.org/1999/xhtml' style='font-size:40px'>
<em>I</em> like <span style='color:white; text-shadow:0 0 2px blue;'>CANVAS</span>
</div>
</foreignObject>
</svg>
`;
const img = new Image();
const svg = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
const url = URL.createObjectURL(svg);
img.onload = function() {
ctx.drawImage(img, 0, 0);
URL.revokeObjectURL(url);
};
img.src = url;
<canvas id="canvas" style="border:2px solid black;" width="200" height="200"></canvas>
This example results in this HTML being rendered to canvas as this:
Will the properties and events of those elements works or not ?
No, everything drawn to a canvas is forgotten as passive pixels - they becomes simply an image.
You will need to provide custom logic that you provide yourselves in order to to handle any such things as clicks, objects, events etc. The logic need to define the areas, objects and anything else.
I would like to make a good performance hit test for png images and other shapes. I don't really care what shapes they are because with this technique there is no performance issues at checking (not setup).
I intent to collect all the images on the screen in a secondary canvas just for hit test. For each image drawn I will create a new color which is attached to that particular image. Then I draw all of them in the canvas, each image will have a different fill color.
When I click on a pixel (x, y) it will get the color (r, g, b). Every color is mapped to a image, so I get the image clicked with no error (I don't waste with finding what was hit with that click).
I know it will be limited to 256*256*256=16 777 216 items because those are all the colors but I don't think it will be a problem for now...
So what I really need is to know how to put those fill colors on the secondary canvas which is based only on the visible pixels for each image.
UPDATE
As you can see to the right it's the hit test map. So if I click on the black shade (c) I instantly know I've clicked on the blue box without any other calculation.
One improvement it would be to cache the alpha data. Also reuse the same alpha data for each image instance (we must take care about scaling and rotation...).
thanks
Here’s how you would color-mask the non-transparent pixels of a canvas image.
Be sure you replace "PutYourImageHere.png" with your own image url.
<!doctype html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid blue;}
</style>
<script>
$(function(){
var img=new Image();
img.onload=function(){
var red=255;
var blue=0;
var green=0;
var canvasCopy=document.getElementById("canvasCopy");
var ctxCopy=canvasCopy.getContext("2d");
var c=document.getElementById("canvas");
var ctx=c.getContext("2d");
ctx.drawImage(this,0,0);
var imgData=ctx.getImageData(0,0,c.width,c.height);
for (var i=0;i<imgData.data.length;i+=4)
{
if(imgData.data[i+3]>0){
imgData.data[i]=red;
imgData.data[i+1]=green;
imgData.data[i+2]=blue;
imgData.data[i+3]=255;
}
}
ctxCopy.putImageData(imgData,0,0);
}
img.src = "PutYourImageHere.png";
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width="300" height="300"></canvas>
<canvas id="canvasCopy" width="300" height="300"></canvas>
</body>
</html>
I just basically want to get the "http://... .png" or "http://... .jpg" location of my canvas as an image.
I have already tried toDataURL() but it is not working. Especially if I loaded an image within the canvas.
Here is my code: (btw, I'm using jQuery here)
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script>
$(document).ready(function(){
var canvas = $("#canvas");
var ctx = canvas.get(0).getContext("2d");
var image1 = new Image();
image1.src = "http://www.helpinghomelesscats.com/images/cat1.jpg"
$(image1).load(function(){
ctx.drawImage(image1,0,0,200,200);
});
});
</script>
with my html/body having only this:
<canvas id="canvas" width="500" height="400"></canvas>
now, if you try that, that works fine. it shows that cat.
but when i add that toDataURL into my script, it just doesn't happen.
var dataImg = canvas.get(0).toDataURL();
i load this dataImg variable into another click-redirect function to test it, hoping it would redirect to the page using the base64 url it contains, but it just doesn't work:
$("#canvas").click(function(){
document.location = dataImg;
});
it brings me to a blank page? what am i missing here?
thank you very much!
Do you own http://www.helpinghomelesscats.com or is your code hosted directly on that site? If not you won't be able to do this due to cross site origin policies. The best way would be to have some server side code grab the image and then serve it locally on your domain.
If you do own helpinghomelesscats.com this should work, as tested here
Live Demo
Click the canvas and view the log in order to see the response.
I am a beginner programmer in javascript. I don't use jQuery! And I want to make a simple game.
I am loading multiple images into canvas using
imageObj.onload = function(){}
I am using a keylistener for multiple keypresses so that the images could move on the diagonal while pressing both up and left keys by using smth like this:
function keydown_handler(e){my_key[String.fromCharCode(e.keyCode)] =
true; Move();}
My problem is that when I press the keys and move the images on the canvas the image flickers. I suppose this is because it loads the image every time I press a key. If this is true how can I load an image ONCE into memory and then RECALL that image from memory and change it's coordinates?
Thank you!
Well, what you really need to do is to create a render loop with javascript using requestAnimationFrame(), and render with the canvas element. Here's a really basic example of rendering with HTML5:
<!DOCTYPE html>
<html>
<body>
<canvas id="gameCanvas" width="800px" height="600px"></canvas>
<script type="text/javascript">
var canvas = document.getElementById('gameCanvas');
var context = canvas.getContext('2d');
var myImage = new Image();
var myImage.onload=function(){init();};
var myImage.src='location/of/image.png';
var imageX = 0, imageY = 0;
function render()
{
window.requestAnimationFrame(render);
// clear canvas
canvas.width = canvas.width;
context.drawImage(myImage, imageX, imageY);
imageX++;
imageY++;
}
function init()
{
window.requestAnimationFrame(render);
}
</script>
</body>
</html>
There will never be flicker when you're rendering through a canvas since the browser is already double buffering that rendering surface; and manually double buffering the canvas will actually produce a significant drop in framerate. What you're probably encountering (if you're rendering through a canvas) is tearing of the frame. Using requestAnimationFrame will resolve the tearing problem by essentially v-syncing the render (since it waits until the end of code execution to render).
Hopefully this will help you get started on the right path for rendering with HTML5.
What you are referring to is a very common problem when dealing with animations. The issue has less to do with what is stored in memory and more to do with the way an animation must be redrawn each time something changes. The most common method for avoiding this flickering issue is known as double buffering.
I have never done this using HTML5 specifically but after a quick search I found this article that may help you.
https://stackoverflow.com/a/2864533/594558