Trying to refresh specific canvas element - javascript

I'm simply trying to refresh a canvas element.
I've tried doing this
function reload(){
var container = document.querySelectorAll('[canvas]');
var content = container.innerHTML;
container.innerHTML= content;
console.log("Refreshed"); //Watches console.
}
But it simply doesn't refresh the canvas element? I can use Jquery on the site if it's easier.
Here is the HTML of the canvas element.
<canvas width="800" height="500" style="position: absolute; top: 39px; left: 136px; background-color: transparent; z-index: 10;"></canvas>
If you can help me find a solution please.

You should get the container using querySelector
var container = document.querySelector('canvas');
or querySelectorAll
var container = document.querySelectorAll('canvas')[0];
Now, you attempt to refresh the canvas by changing its inner HTML to the formerly known HTML value:
var content = container.innerHTML;
container.innerHTML= content;
console.log("Refreshed"); //Watches console.
if changing the inner HTML of the canvas would refresh it, then it is a hacky approach and it should only work according to my opinion if the HTML actually changes. Instead, it would be better to just store the content of the canvas somewhere, then clear it:
ctx.clearRect(0, 0, canvas.width, canvas.height);
and draw it again.

Currently theres a few issues. First, [canvas] should be just canvas. Second, you are using querySelectorAll which returns a NodeList of all canvas objects. So either, use that but then loop over it to update all canvases, or change to querySelector which returns just one. And I would recommend using an ID in that case.
Also, this might seem silly but are you calling the function? Or just defining it?
Here it is with querySelectorAll and canvas:
function reload(){
var canvases = document.querySelectorAll('canvas');
for(let canvas of canvases) {
var ctx = canvas.getContext("2d");
var content = ctx.getImageData(0, 0, canvas.width, canvas.height);
ctx.putImageData(ctx,0,0)
console.log("Refreshed"); //Watches console.
}
}
<canvas width="800" height="500" style="position: absolute; top: 39px; left: 136px; background-color: transparent; z-index: 10;"></canvas>

You are wrong in using document.querySelectorAll(). Since you are selecting a single target, you should use document.querySelector(). Try this.
function reload(){
var container = document.querySelector('canvas');
var content = container.innerHTML;
container.innerHTML = content;
console.log("Refreshed");
}

Related

2D Canvas (behind a 3D Canvas) not drawing images

I'm currently exploring with Three.js, and working on a small project of mine.
The project consists on having a canvas focused on 3D models and animations, and another one behind, which handles the simpler 2D work.
I've set up the 3D canvas properly, so it's background is transparent, and I can see boxes I draw manually on the 2D canvas, which leads me to assume the setup is right.
The issue I'm having is when it comes to images. I simply cannot get an image to display on the 2D canvas. I've experimented on a separate project, and could draw Images there, no problem. The code is pretty basic, and I actually found it here, but is as follows:
window.onload = function() {
var canvas = document.getElementById('bgcanvas');
var context = canvas.getContext('2d');
var logoText = new Image();
logoText.onload = function() {
context.drawImage(logoText, 69, 50);
};
logoText.src = 'images/logotext.png';
}
#canvas {
position: fixed;
z-index: 0;
}
#bgcanvas {
z-index: -10;
position: fixed;
width: 100vw;
height: 100vh;
}
<div id="fixedContainer">
<canvas id="bgcanvas"></canvas>
<canvas id="canvas"></canvas>
</div>
What's going on that I'm unaware of?
Massive thanks in advance!
UPDATE EDIT: The issue was that I had an image on which the top left corner was transparent, and didn't know the image would stretch. user3412847's comment helped me figure it out
Specifying image width and height is a good habit to get into. Use this syntax: context.drawImage(image, x, y, width, height).
Hope this helps.
I'm guessing you don't have an image at that path; It works fine for me with a valid image (eg: http://lorempixel.com/100/100):
window.onload = function() {
var canvas = document.getElementById('bgcanvas');
var context = canvas.getContext('2d');
var logoText = new Image();
logoText.onload = function() {
context.drawImage(logoText, 69, 50);
};
logoText.src = 'http://lorempixel.com/100/100';
}
#canvas {
position: fixed;
z-index: 0;
}
#bgcanvas {
z-index: -10;
position: fixed;
width: 100vw;
height: 100vh;
}
<div id="fixedContainer">
<canvas id="bgcanvas"></canvas>
<canvas id="canvas"></canvas>
</div>

Duplicating a canvas many times: clone the canvas or copy the image data?

One of my interface elements is being rendered using the HTML5 <canvas> element and associated JavaScript API. This element is used in several places on the same screen and on multiple screens throughout the app. What is the most efficient way to display this everywhere it's required?
My first idea is to draw to a master canvas, which I then clone and insert where needed in the page. The master canvas might be something like:
var master = $('<canvas>').attr({
width: 100,
height: 100
}),
c = master[0],
ctx = c.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 150, 75);
Let's say I want to duplicate the canvas in these div containers:
<div class="square-container" id="square_header"></div>
...
<div class="square-container" id="square_dataTable"></div>
...
<div class="square-container" id="square_gallery"></div>
....
When the page loads, I'll do this to insert a duplicate canvas element into each container:
$(document).ready(function() {
$('.square-container').each(function() {
master.clone().appendTo($(this));
});
});
The content being rendered on the canvas is going to be more complex than the simple square used in this example but will still end up being just a static image. It is possible, though, that there could be dozens of different images each cloned dozens of times per page.
The other approach I had in mind was to create an image using the toDataURL() method and set that as the appropriate images' sources:
var master = $('<canvas>').attr({
width: 100,
height: 100
}),
c = master[0],
ctx = c.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0,0,150,75);
var square = c.toDataURL('image/png');
I would add image tags where necessary:
<img src="" id="square_header" class="square" alt="" />
...
<img src="" id="square_dataTable1" class="square" alt="" />
...
<img src="" id="square_gallery" class="square" alt="" />
....
And then set all of their SRCs to that newly created image:
$(document).ready(function() {
$('img.square').attr('src', square);
});
To me, it pretty much looks like six of one, half dozen of the other. But I'm wondering if one way is considered better practice than the other? If the content being rendered on the <canvas> were more complex, would one way be more efficient than the other?
In that same spirit, when I need to use that element on subsequent pages, is it best to execute all the javascript (from whatever solution is deemed best above) on each page or would saving the value of CANVAS_ELEMENT.toDataURL() in a cookie and then using that on subsequent pages be any more efficient?
Cloning a canvas will duplicate its dimensions and styling, but not its image data. You can copy the image data by calling drawImage on the context. To paint the contents of originalCanvas onto duplicateCanvas, write:
duplicateCanvas.getContext('2d').drawImage(originalCanvas, 0, 0);
As a demonstration, the following snippet generates four canvases:
an original canvas with a small scene painted onto it
a copy made by calling cloneNode only
a copy made by calling cloneNode and drawImage
a copy made by creating a new image and setting its source to the data URI
function message(s) {
document.getElementById('message').innerHTML += s + '<br />';
}
function timeIt(action, description, initializer) {
var totalTime = 0,
initializer = initializer || function () {};
initializer();
var startTime = performance.now();
action();
var elapsed = performance.now() - startTime;
message('<span class="time"><span class="number">' +
Math.round(elapsed * 1000) + ' μs</span></span> ' + description);
}
function makeCanvas() {
var canvas = document.createElement('canvas'),
context = canvas.getContext('2d');
canvas.width = 100;
canvas.height = 100;
timeIt(function () {
context.fillStyle = '#a63d3d';
context.fillRect(10, 10, 80, 40); // Paint a small scene.
context.fillStyle = '#3b618c';
context.beginPath();
context.arc(60, 60, 25, 0, 2*Math.PI);
context.closePath();
context.fill();
}, '(millionths of a second) to draw original scene', function () {
context.clearRect(0, 0, canvas.width, canvas.height);
});
return canvas;
}
// copyCanvas returns a canvas containing the same image as the given canvas.
function copyCanvas(original) {
var copy;
timeIt(function () {
copy = original.cloneNode(); // Copy the canvas dimensions.
copy.getContext('2d').drawImage(original, 0, 0); // Copy the image.
}, 'to copy canvas with cloneNode and drawImage');
return copy;
}
// imageFromStorage extracts the image data from a canvas, stores the image data
// in a browser session, then retrieves the image data from the session and
// makes a new image element out of it. We measure the total time to retrieve
// the data and make the image.
function imageFromStorage(original) {
var image,
dataURI = original.toDataURL();
timeIt(function () {
image = document.createElement('img');
image.src = dataURI;
}, 'to make image from a dataURI');
return image;
}
function pageLoad() {
var target = document.getElementById('canvases'),
containers = {}, // We'll put the canvases inside divs.
names = ['original', 'cloneNode', 'drawImage', 'dataURI'];
for (var i = 0; i < names.length; ++i) {
var name = names[i], // Use the name as an ID and a visible header.
container = document.createElement('div'),
header = document.createElement('div');
container.className = 'container';
header.className = 'header';
header.innerHTML = container.id = name;
container.appendChild(header);
target.appendChild(container);
containers[name] = container; // The canvas container is ready.
}
var canvas = makeCanvas();
containers.original.appendChild(canvas); // Original canvas.
containers.cloneNode.appendChild(canvas.cloneNode()); // cloneNode
containers.drawImage.appendChild(copyCanvas(canvas)); // cloneNode + drawImage
containers.dataURI.appendChild(imageFromStorage(canvas)); // localStorage
}
pageLoad();
body {
font-family: sans-serif;
}
.header {
font-size: 18px;
}
.container {
margin: 10px;
display: inline-block;
}
canvas, img {
border: 1px solid #eee;
}
#message {
color: #666;
font-size: 16px;
line-height: 28px;
}
#message .time {
display: inline-block;
text-align: right;
width: 100px;
}
#message .number {
font-weight: bold;
padding: 1px 3px;
color: #222;
background: #efedd4;
}
<div id="canvases"></div>
<div id="message"></div>
If you call toDataURL to copy the image data into a string for use in other pages, don't put the string into a cookie. Cookies are meant to store small amounts of data. Instead, use the HTML5 Web Storage API to store the image data in the browser. Alternatively, if the image doesn't change between user sessions, you can render it to a PNG image on a server and use the Cache-Control header to encourage the browser to cache the image file for fast retrieval.
When it comes to the performance of client-side image rendering, it may be faster to draw the scene anew than to paint the stringified image data onto the canvas. Decoding the string and painting the pixels is a relatively expensive operation. To find out if it makes sense to redraw the scene on each page, you can time your drawing operations with performance.now, as demonstrated in the snippet.

How to draw a background image on an HTML5 canvas

I'm working on my first HTML5 Canvas game. I am trying to put in a background image, and I don't know how to do it. The game will change backgrounds throughout levels, but I think I understand how to make that happen. However, if I just try to draw an image, then it appears above my title text. Any ideas how to fix this? I don't think the CSS method will work, because the background will change. Here is my code:
<!DOCTYPE html>
<html>
<head>
<title>How Well Do You Know Your Swedish?</title>
</head>
<body>
<canvas width="640" height="480" id="game" style="display: block; margin-left: auto; margin-right: auto; border: 1px solid black;" Your Browser is not compatible with this game. We recommend Google Chrome, Mozilla Firefox, or Opera.></canvas>
<script>
var game_canvas = document.getElementById("game");
var game_context = game_canvas.getContext("2d");
var swedishflagbg = new Image();
swedishflagbg.src = "resources/images/swedishflagbg.png";
swedishflagbg.onload = function() {
game_context.drawImage(swedishflagbg, 0, 0);
}
game_context.fillStyle = "#000000";
game_context.font = "35px Ubuntu";
game_context.textAlign = "center";
game_context.textBaseline = "top";
game_context.fillText("How Well Do You Know Your Swedish?", 320, 0);
</script>
</body>
</html>
I am new to JavaScript, and even newer to the HTML5 Canvas.
Extending from comment:
The "why":
Because the background image is drawn in an onload event, which will happen "later", while text is drawn immediately.
So the text is drawn first, and then sometimes later the image is drawn, thus causing the text to be covered.
The "how":
Depending on how "dynamic" the background image is, you can consider:
use CSS background-image on cancas/canvas container, and use className/classList to switch between different backgrounds;
put an <img> tag underneath the canvas, and change its src property;
use an additional "background" canvas underneath the current one, so you can draw the game content in current one, and draw the (probably not frequently-updated) background in the new one.
Credit of idea 1 and 2 goes to #markE :)
First things:
Check the size of your image. It should be equivalent to the size of the canvas.
context.drawImage can also take width and height parameter for the image.
Syntax: context.drawImage(img, x, y, width, height);
After editing, it should look like this
let game_canvas = document.getElementById("game");
let game_context = game_canvas.getContext("2d");
let swedishflagbg = new Image();
swedishflagbg.src = "resources/images/swedishflagbg.png";
swedishflagbg.onload = function() {
game_context.drawImage(swedishflagbg, 0, 0, game_canvas.width, game_canvas.height);
}
game_context.fillStyle="#000000";
game_context.font="35px Ubuntu";
game_context.textAlign="center";
game_context.textBaseline="top";
game_context.fillText("How Well Do You Know Your Swedish?", 320, 0);
To anyone who is still facing the problem of background image overlaying text (Image above text), I thought "why not drawing text inside onload function after loading bg image!?"
And it worked!! The browser (Or whatever) loads image first and load my text above image.
<canvas width="534" height="104" id="game" style="display: block; margin-left: auto; margin-right: auto; border: 1px solid black;" Your Browser is not compatible with this game. We recommend Google Chrome, Mozilla Firefox, or Opera.></canvas>
<script>
let game_canvas = document.getElementById("game");
let game_context = game_canvas.getContext("2d");
let swedishflagbg = new Image();
swedishflagbg.src = "./img/logo1.png";
swedishflagbg.onload = function() {
game_context.drawImage(swedishflagbg, 0, 0, game_canvas.width, game_canvas.height);
game_context.fillStyle = "#000000";
game_context.font = "35px Ubuntu";
game_context.textAlign = "center";
game_context.textBaseline = "top";
game_context.fillText("How Well Do You Know Your Swedish?", 320, 0);
}
</script>

HTML5 Canvas - Grouping / Attaching an image TO a canvas

Using this JS Fiddle I am able to press a button to add new canvases to the screen...
var next = 4
function addCanvas() {
// create a new canvas element
var newCanvas = document.createElement("canvas");
newCanvas.id = "addedcanvases" + next; //added this to give each new canvas a unique id
next++;
newCanvas.width = canvasWidth;
newCanvas.height = canvasHeight;
// add a context for the new canvas to the contexts[] array
contexts.push(newCanvas.getContext("2d"));
// link the new canvas to its context in the contexts[] array
newCanvas.contextIndex = contexts.length;
// wire up the click handler
newCanvas.onclick = function (e) {
handleClick(e, this.contextIndex);
};
// wire up the mousemove handler
newCanvas.onmousemove = function (e) {
handleMousemove(e, this.contextIndex);
};
// add the new canvas to the page
document.body.appendChild(newCanvas);
}
The problem:
What is the best way to go about grouping / attaching a static image to the top of a canvas (as shown in the image below) so that whenever a new canvas is created in JS Fiddle an image is automatically created with it that is grouped / attached to the top of the new canvas.
This is so that where-ever a new canvas is dynamically created on the page an image is put above that canvas?
There may be an obvious way to do this that I am overlooking? but googling has not thrown up much as all 'image' and 'canvas' searches inevitably relate to actually adding an image to the canvas - which is not what I want to do in this instance.
Here's taking #KaliedaRik's answer and creating your groups using javascript:
http://jsfiddle.net/m1erickson/3EUnc/
The code to create a new group could be something like this:
function newGroup(){
// create a new wrapper div
var div=document.createElement("div");
div.className="wrapper";
// create an img and a canvas element
var img=document.createElement("img");
var br=document.createElement("br");
img.style.width="50px";
img.src="houseicon.png";
var canvas=document.createElement("canvas");
canvas.width=300;
canvas.height=55;
// add the img and canvas elements to the wrapper div
div.appendChild(img);
div.appendChild(br);
div.appendChild(canvas);
// add the wrapper div with its contained img + canvas to the page
document.body.appendChild(div);
}
One possible solution: when you create the canvas element, create a new div at the same time and put the canvas and img tags inside it. By making the div position style relative and the contained canvas and img position styles absolute, you'll be able to place the image wherever it needs to go.
<div style="position: relative">
<canvas style="position: absolute; top: 0px; left: 0px;"></canvas>
<img src="whatever" alt="whatever" style="position: absolute; top: -20px; left: 0px" />
</div>

Cannot save canvas with draggable object

I'm trying to save my HTML canvas to file which I can successfully do, but it's not saving any objects I've dragged into the canvas.
So, by using the Draggable JQuery I can happily move my object around screen and place it ontop of my canvas. When I save the canvas using the Canvas.ToDataURL() it does not save my dragged objects (and also does something strange to my canvas in the jsFiddle, it appears to change the colour of my canvas?).
To see a "working" example, please visit my jsFiddle http://jsfiddle.net/JVSFS/74/
Please simply drag the green box over the blue box and click the save button. The result will be shown underneath (just an orange box).
HTML
<canvas id="MyCanvas" class="canvas"></canvas>
<div class="popup_click">
<div id="popup_title">Drag</div>
</div>
<asp:HiddenField ID="hideMe" runat="server" />
<asp:Button runat="server" OnClick="ClickMe" Text="Click" OnClientClick="SaveMe()" />
<button onclick="SaveMe()">Try it</button>
<p>Results: </p>
<img id="myImage" />
JavaScript
$(document).ready(function () {
$('.popup_click').show(0).draggable();
});
function SaveMe() {
var canvas = document.getElementById("MyCanvas");
var context = canvas.getContext("2d");
context.fillStyle = "orange";
context.fillRect(0, 0, 100, 100);
var image = canvas.toDataURL("image/png");
document.getElementById("myImage").src = image;
document.getElementById("hideMe").value = image;
}
CSS
.popup_click {
background: #80FF80;
width: 50px; }
.canvas {
width: 100px;
height: 100px;
background-color: #0FC;
}
How can I get the dragged object to save? I assume I have to tell the Canvas that the object is part of it's context but no idea how and my own searches came up with nothing.
From https://developer.mozilla.org/en-US/docs/HTML/Canvas/Drawing_DOM_objects_into_a_canvas
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.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var 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;'>cheese</span>" +
"</div>" +
"</foreignObject>" +
"</svg>";
var DOMURL = self.URL || self.webkitURL || self;
var img = new Image();
var svg = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svg);
img.onload = function() {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
};
img.src = url;
That's because your draggable object isn't in the canves.
They are simple html elements.
It'll save only the objects whose created with canvas methods.
Any way to create html elements on canvas you have to use svg.
Mozilla show nice way to to this but you need to get all the css to inline css before.
mozilla explanation
Anyway with using svg on you canvas you won't be able to use toDataUrl because of security policy.

Categories

Resources