HTML text inside Canvas - javascript

I need to render an image on which I have put some HTML text on it and then the user to be able to download the image with the text as an image or even better as a pdf.
The certText element is taken from a TinyMCE variable, where the user each time uses different HTML tags within the text.
So it could be
"Hello user, how are you"
or
"Hi there, great that you succeed on the certification".
Starting from the image option I have created below code but Canvas do no translate the HTML tags.
I have read that with Canvas it's not possible to render the HTML tags, but is there any other alternative to succeed this.
The code is used on a Laravel project that I have made.
var p = document.getElementById("certificate");
var ptx = p.getContext("2d");
var imgp = document.getElementById("certificateEmpty");
var txtp = document.getElementById("certText").innerHTML;
imgp.onload = function(){
ptx.drawImage(imgp, 0, 0);
ptx.font = "48px Nunito";
ptx.fillText(txtp, 150, 1000);
};
imgp.src = "{{$attendees[0]->certBLocation}}";

What are you trying to achive when you say "render html tags"?
Do you mean you want to style the text that is drawn on the canvas?
If so, you have to use canvas functions to set what the text will look like.
In the below example using your code, I use "fillStyle" to set the drawing fill colour in which the text will be drawn.
var p = document.getElementById("certificate");
var ptx = p.getContext("2d");
var imgp = document.getElementById("certificateEmpty");
var txt = document.getElementById("certText");
var txtp = txt.innerHTML;
imgp.onload = function(){
ptx.drawImage(imgp, 0, 0, p.width, p.height);
var styles = window.getComputedStyle(txt);
ptx.fillStyle = styles.color;
//ptx.font = styles.font; // Doesn't seem to work in firefox.
ptx.font = styles['font-weight']+" "+styles['font-size']+" "+styles.fontFamily; // Access properties array style because of hyphens.
ptx.fillText(txtp, 0, 100);
};
imgp.src = "https://www.gravatar.com/avatar/7b67c827ee1671ba3b43f4aebf6794fb?s=200";
img, canvas {
width: 200px;
height: 200px;
}
#certText {
color: #ff0000;
font-weight: bold;
font-size: 40px;
}
<img id="certificateEmpty"/>
<canvas id="certificate"></canvas>
<div id="certText">
some text
</div>
Text drawn on the canvas looks squashed on my screen (Ubuntu/Chrome), not sure why.

Related

compare 2 canvas elements for similarity and return result

I'm new to js and html5 so here's what I'm doing : I'm working on a game that helps in teaching illustrator shortcuts, the firts level consist of 2 canvas one with an already existing image and the other blank and ready for user to draw on, on ctrl + s press(sure I disabled it's default action using jquery) I want to compare the content of those 2 canvas elements. I've found Image similarity api from deepai.org very useful and accurate, but it only accepts url or input="file" content, so I'm trying to find a way to upload (maybe) the drawn canvas as an image to a server and get the url like this : https://server.com/myaccount/images/img1.png and since i only upload one image I can pass that static url to the api in addition to the original image which will also have a static url so hopefully it compares.
I made a solution that works without a server. But I couldn't make it work in an online code repo like jsfiddle. So I put it on my own server for you to check. http://paulyro.com/paul/deepai/
For convenience I put everything in one file. Of course it would make sense to save the JS in a separate file. But I let that up to you.
For explanation: the red square in a black frame is the canvas. I generate two images and add them to the page. Then I send those images to the deepAI server when you press the button. You will only need one generated img, but for testing purpose I made 2.
Let me know if this is what you were looking for. Of course I expect you to adapt this solution to your exact needs ;)
This is the code:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>DeepAiDemo</title>
<script src="https://cdnjs.deepai.org/deepai.min.js"></script>
<style rel="stylesheet">
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="canvas1" width="200" height="200"></canvas>
<button name="button" onClick="load()">Press me</button>
<div style="position:absolute;left:400px; top:30px; height: 354px;" id="messages">Result will get here</div>
<script type="text/javascript">
(async function() {
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
// create green canvas and make it an image
ctx.fillStyle = "green";
ctx.fillRect(75, 75, 50, 50);
var img = new Image();
img.src = can.toDataURL();
document.body.appendChild(img);
// create red canvas and make it an image
ctx.fillStyle = "red";
ctx.fillRect(75, 75, 50, 50);
var img2 = new Image();
img2.src = can.toDataURL();
document.body.appendChild(img2);
})()
const load = async () => {
document.getElementById('messages').innerHTML = "Waitng for response...";
deepai.setApiKey('xxxxxxxxxxxxxx');
var images = document.getElementsByTagName('img');
console.log("amount images: "+images.length);
console.log(images[0]);
console.log(images[1]);
var resp = await deepai.callStandardApi("image-similarity", {
image1: images[0],
image2: images[1],
});
console.log("response: ");
console.log(resp);
document.getElementById('messages').innerHTML = "Distance: " + resp.output.distance; //resp.output.distance contains the number from the server.
};
</script>
</body>
</html>
Cheers,
Paul

How to "toggle" between IText objects (paragraphs of text) on selection with fabricjs canvas?

I want to be able to select from a dropdown to "toggle" between styled IText paragraphs onto my fabricjs canvas but am having trouble making it happen. I tried borrowing from this, which was to do similarly with images, with no luck:
//oImgObj bread and butter, kudos #grunt
function replaceImage(oImgObj, imgUrl) {
if (!isImageLoaded) return; //return if initial image not loaded
var imgElem = oImgObj._element; //reference to actual image element
imgElem.src = imgUrl; //set image source
imgElem.onload = () => canvas.renderAll(); //render on image load
}
How might I accomplish this with editable blocks of text?
You can set your text to a text object with the attribute text.
var canvas = new fabric.Canvas('c');
var text = new fabric.IText('FabricJs is Awsome',{
left:50,top:50
});
canvas.add(text);
function replace(option){
var val = document.getElementsByTagName('textarea')[0].value;
text.set('text',val);
canvas.renderAll();
}
canvas {
border: blue dotted 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.min.js"></script>
<canvas id="c" width="400" height="400"></canvas>
<br>
<textarea></textarea><button onclick='replace()'>Replace</button>

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>

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