How to render custom font in HTML5 canvas? [duplicate] - javascript

This question already has an answer here:
How to render icon fonts on HTML canvas and Material Design icon fonts in particular?
(1 answer)
Closed 2 years ago.
I saw a code pen here for rendering Microsoft Fabric UI Icons in a canvas in code pen. This is the working link
https://codepen.io/joshmcrty/pen/GOBWeV
There is a lot of extra stuff here to customize it. So I attempted to minimize this to barebones. What I did is completely remove the css, change the HTML to just the canvas tag, and reduced the JS code. The result looks like this:
HTML
<canvas id="canvas" width="92" height="92"></canvas>
CSS
/*none*/
JS
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
function getFontIconCharacter(className, pseudo = ':before') {
var testI = document.createElement('i');
var char;
testI.className = `ms-Icon ms-Icon--${className}`;
document.body.appendChild(testI);
char = window.getComputedStyle(testI, pseudo).getPropertyValue('content').replace(/'|"/g, '');
testI.remove();
return char;
}
function drawRect() {
context.fillStyle = "#777777";
context.fillRect(0, 0, canvas.width, canvas.height);
}
function drawIcon() {
context.clearRect(0, 0, canvas.width, canvas.height);
drawRect();
context.fillStyle = "#FFFFFF";
let fontClass = "Contact",
fontSize = 56,
topOffset = 46,
leftOffset = 46;
context.font = fontSize + "px FabricMDL2Icons";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText(getFontIconCharacter(fontClass), parseInt(leftOffset, 10), parseInt(topOffset, 10));
}
window.addEventListener('load', function() {
drawIcon();
});
The weird thing is if I replace the code from the working example into the above, the custom font in the canvas still renders correctly. However if I recreate it on a new file, it does not render the font properly. I saved my attempt here
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Fabric UI Icons</title>
<link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/11.0.0/css/fabric.min.css">
</head>
<body>
<canvas id="canvas" width="92" height="92"></canvas>
<script>
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
function getFontIconCharacter(className, pseudo = ':before') {
var testI = document.createElement('i');
var char;
testI.className = `ms-Icon ms-Icon--${className}`;
document.body.appendChild(testI);
char = window.getComputedStyle(testI, pseudo).getPropertyValue('content').replace(/'|"/g, '');
testI.remove();
return char;
}
function drawCircle() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = canvas.width / 2;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = "#777777";
context.fill();
}
function drawRect() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.fillStyle = "#777777";
context.fillRect(0, 0, canvas.width, canvas.height);
}
function drawIcon() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.clearRect(0, 0, canvas.width, canvas.height);
drawRect();
context.fillStyle = "#ffffff"; // icon color
let fontClass = "Contact",
fontSize = 56,
topOffset = 46,
leftOffset = 46;
context.font = fontSize + "px FabricMDL2Icons";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText(getFontIconCharacter(fontClass), parseInt(leftOffset, 10), parseInt(topOffset, 10));
}
window.addEventListener('load', function() {
drawIcon();
});
</script>
</body>
</html>
I noticed they include a css file to the font, so I have that as well, but it still doesn't work.
Does anyone see what is wrong?
Thanks

I figured it out, I had to start the font loading process with this. Just because it's defined in a <link> tag doesn't mean it will start loading. Something needs to reference it before its used on the canvas for the browser to load it.
<style>
body {
font-family: FabricMDL2Icons;
}
</style>

Related

Why is JS canvas showing up, but not updating?

I want to create a rectangle canvas that simply shows a few randomy generated particles. I have been messing around with canvas a bit off my project so I understand the basics. However, it seems when I try to integrate a canvas to my project, nothing is working.
The canvas HTML element is properly added to the DOM (I can see it thanks to the dashed border) and it is properly positioned.
I can also see the console message "Refreshed" which means the interval works.
BUT the canvas just remains blank, whatever I do. I've tried filling it, drawing circles, strokes and everything, and absolutely NOTHING shows up. It just remains a transparent rectangle.
Below is the (typscript) code that handles the canvas, and the method that calls the canvas:
// particles canvas
public static getTooltipParticlesCanvas(rarity: Data.Rarity, id: number) {
let posY = 25, posX = 25;
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 232;
canvas.height = 53;
canvas.style.border = "1px dashed grey";
canvas.style.position = "absolute";
canvas.style.left = "0";
clearInterval(this.particlesTooltipCanvasInterval);
this.particlesTooltipCanvasInterval = setInterval(() => {
// erase canvas
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
posY -= 0.25;
ctx.fillStyle = "green";
ctx.fillRect(posX, posY, 5, 5);
console.log("Refreshed");
}, 30);
return canvas;
}
public static getWeaponTooltip(weapon: Weapon) {
let str: string = '';
// ...
str += '<div class="canvasWrapper">' + this.getTooltipParticlesCanvas(weapon.rarity).outerHTML + '</div>
// ...
return str;
}
The getWeaponTooltip returns a HTML string that is then appended via .innerHTML on a div.
Your code works fine in isolation (with references to this and the unused rarity and id removed), so the issue is in something you're not showing us.
function getTooltipParticlesCanvas() {
let posY = 25, posX = 25;
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 232;
canvas.height = 53;
canvas.style.border = "1px dashed grey";
canvas.style.position = "absolute";
canvas.style.left = "0";
setInterval(() => {
// erase canvas
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
posY -= 0.25;
ctx.fillStyle = "green";
ctx.fillRect(posX, posY, 5, 5);
}, 30);
return canvas;
}
canvas = getTooltipParticlesCanvas();
document.body.appendChild(canvas);

Why canvas is not working locally but working on TRYIT

I Have build some code related to canvas but code is working on TRYIT but code is not working locally when i have copied all code to file and tried to run it .
This is what this code is doing , it takes an image and set the width and height of canvas with respect to that image and draw a filled circle with text in it on that image(canvas).
Here is code
<head>
<meta charset=utf-8 />
<title>Draw a circle</title>
</head>
<body onload="draw();">
<canvas id="circle"></canvas>
</body>
<script>
var canvas = document.getElementById('circle'),
context = canvas.getContext('2d');
function draw()
{
base_image = new Image();
base_image.src = 'http://sst-system.com/old/Planos/C21E34.JPG';
var canvas = document.getElementById('circle');
if (canvas.getContext)
{
base_image = new Image();
base_image.src = 'http://sst-system.com/old/Planos/C21E34.JPG';
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.canvas.width = base_image.width;
ctx.canvas.height = base_image.height;
var X = 500;
var Y = 229;
var R = 6.4;
ctx.font = "15px Arial bold";
ctx.beginPath();
ctx.arc(X, Y, R, 0, 2 * Math.PI, false);
ctx.lineWidth = 12;
ctx.strokeStyle = '#FF0000';
ctx.drawImage(base_image, 0, 0)
ctx.stroke();
ctx.fillText("TT", X-9, Y+5);
}
}
</script>
There are no errors on console , but it shows these warnings in console :
As #DBS mentioned, it was a speed thing. You weren't waiting for the image to load before working with it.
The fix is to attach a listener to the load event of the image, either using image.addEventListener('load', () => { or the deprecated-but-still-works style of image.onload = () => {, which I have used below.
The reason that it works in the TryIt example is that the image is cached by the browser from the second load onwards, so it is available immediately and you don't need to wait for it to load.
I suspect that when you run it locally, if you have Devtools open, the cache is disabled due to an option in Devtools settings called "Disable cache (while DevTools is open)". So it will never be pulled from the cache, and thus never work.
The following code works:
<html>
<head>
<meta charset=utf-8 />
<title>Draw a circle</title>
</head>
<body onload="draw();">
<canvas id="circle"></canvas>
</body>
<script>
var canvas = document.getElementById('circle'),
context = canvas.getContext('2d');
function draw() {
base_image = new Image();
base_image.src = 'http://sst-system.com/old/Planos/C21E34.JPG';
var canvas = document.getElementById('circle');
if (canvas.getContext) {
base_image = new Image();
base_image.src = 'http://sst-system.com/old/Planos/C21E34.JPG';
// The key change: put the rest of the code inside the onload callback
// to wait for the image to load before using it.
base_image.onload = function() {
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.canvas.width = base_image.width;
ctx.canvas.height = base_image.height;
var X = 500;
var Y = 229;
var R = 6.4;
ctx.font = "15px Arial bold";
ctx.beginPath();
ctx.arc(X, Y, R, 0, 2 * Math.PI, false);
ctx.lineWidth = 12;
ctx.strokeStyle = '#FF0000';
ctx.drawImage(base_image, 0, 0)
ctx.stroke();
ctx.fillText("TT", X - 9, Y + 5);
};
}
}
</script>
</html>

How to rotate dynamic text from input in Canvas and JS?

I'm working on a box styling project with Canvas and Javascript, and I can't rotate the text the way I want (written from bottom to the top).
(source: hostpic.xyz)
I followed a tutorial (https://newspaint.wordpress.com/2014/05/22/writing-rotated-text-on-a-javascript-canvas/) and tried to adapt it in my code, but I couldn't make it.
You can find the code on the JSFiddle link below, there's an input where you can type your text and it should be written as the "brand" word in the JSFiddle example.
https://jsfiddle.net/ParkerIndustries/mgne9x5u/5/
Everything is in the init() function:
function init() {
var elem = document.getElementById('elem');
var circle_canvas = document.createElement("canvas");
var context = circle_canvas.getContext("2d");
var img = new Image();
img.src = "https://unblast.com/wp-content/uploads/2018/11/Vertical-Product-Box-Mockup-1.jpg";
context.drawImage(img, 0, 0, 500, 650);
circle_canvas.width = 500;
circle_canvas.height = 650;
context.fillStyle = "#000000";
//context.textAlign = 'center';
var UserInput = document.getElementById("UserInput");
context.save();
context.translate( circle_canvas.width - 1, 0 );
UserInput.oninput = function() {
context.clearRect(0, 0, 500, 650);
context.drawImage(img, 0, 0, 500, 650);
var text = UserInput.value;
console.log(text);
if (text.length < 12) {
context.rotate(3*Math.PI/2);
console.log("-12");
context.font = "50px Righteous";
context.fillText(text, -350, -170);
context.restore();
} else {
context.rotate(3*Math.PI/2);
context.font = "25px Righteous";
context.fillText(text, -350, -170);
context.restore();
}
}
elem.appendChild(circle_canvas);
}
init();
I tried a lot a values in the context.rotate() function but in any way my text is upside down.
(source: hostpic.xyz)
You're pretty close here. I suggest performing the canvas translation to the middle of the screen (width / 2, height / 2) and then rotating by 270 degrees:
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate(270 * Math.PI / 180);
Beyond that, I recommend a round of code cleanup to avoid inconsistent variable names and hardcoded numbers (try to make everything proportional to img.width and img.height, then avoid literal values. This makes it easier to adjust your code dynamically without having to re-type all of the values. You can access img.width and img.height after the img.onload function fires.
Another useful function is context.measureText(text), which makes it simpler to proportionally scale text size.
Full example:
function init() {
var userInput = document.getElementById("user-input");
var elem = document.getElementById("elem");
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var img = new Image();
img.onload = function () {
canvas.width = img.width / 3;
canvas.height = img.height / 3;
context.drawImage(img, 0, 0, canvas.width, canvas.height);
context.fillStyle = "#000000";
context.textAlign = "center";
elem.appendChild(canvas);
userInput.oninput = function () {
var text = userInput.value;
var textSizePx = 50 - context.measureText(text).width / 10;
context.font = textSizePx + "px Righteous";
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(img, 0, 0, canvas.width, canvas.height);
context.save();
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate(270 * Math.PI / 180);
context.fillText(text, 0, -canvas.width / 20);
context.restore();
}
};
img.src = "https://unblast.com/wp-content/uploads/2018/11/Vertical-Product-Box-Mockup-1.jpg";
}
init();
<div id="elem">
<input type="text" id="user-input" maxlength="15">
</div>
With that proof-of-concept working, one problem is that scaling the image on canvas causes visual artifacts. This problem can be overcome with JS or by scaling down the source image itself, but at this point, it's best to make the image as an element and circumvent the entire problem.
Similarly, there's no obvious reason to render text on canvas either; we can make an element for this as well. Moving text to HTML/CSS gives more power over how it looks (I didn't do much with style here, so it's an exercise to make it blend more naturally into the image).
Here's a rewrite without canvas that looks much cleaner and should be more maintainable:
function init() {
var userInput = document.querySelector("#user-input");
var img = document.querySelector("#product-img");
var imgRect = img.getBoundingClientRect();
var overlayText = document.querySelector("#overlay-text");
var overlay = document.querySelector("#overlay");
overlay.style.width = (imgRect.width * 0.86) + "px";
overlay.style.height = imgRect.height + "px";
userInput.addEventListener("input", function () {
overlayText.innerText = userInput.value;
overlayText.style.fontSize = (50 - userInput.value.length * 2) + "px";
});
}
init();
#product-img {
position: absolute;
}
#overlay {
position: absolute;
display: flex;
}
#overlay-text {
font-family: Verdana, sans-serif;
color: #333;
transform: rotate(270deg);
margin: auto;
cursor: default;
}
<div>
<input type="text" id="user-input" maxlength="15">
</div>
<div id="product-container">
<img id="product-img" src="https://unblast.com/wp-content/uploads/2018/11/Vertical-Product-Box-Mockup-1.jpg" width=666 height=500 />
<div id="overlay">
<div id="overlay-text"></div>
</div>
</div>

"var canvas = document.getElementById("canvas")" can't be a global variable?

I made the following script. When I push the button, the word "blood" moves, and push the button and it stops.
This script worked in Chrome but when I move the following scripts to the TOP line. (between var flag; and window.setInterval), an error happens saying
uncaught typeerror cannot call method 'getcontext' of null"
would you explain why it happens please?
var canvas = document.getElementById("canvas"), ctx = canvas.getContext("2d");
var y = 100;
var flag = false;
window.onload = setInterval(function(){
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "red";
ctx.font = "50px Helvetica";
ctx.fillText("blood", 200, y);
if(flag){
y++;
}
}, 30);
function start(){
flag = true;
}
function stop(){
flag = false;
}
If you run your code in a script element at the end of the html body, you can ensure that it doesn't try to get the canvas context before the DOM is ready. Also, I made an optimization to your code to cache the DOM lookup:
<html>
<head>
<title>Canvas Scrolling Text</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="text/javascript">
var y = 100;
var flag = false;
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
setInterval(function() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "red";
ctx.font = "50px Helvetica";
ctx.fillText("blood", 100, y);
if(flag)
y++;
}, 30);
function start()
{
flag = true;
}
function stop()
{
flag = false;
}
start();
</script>
</body>
Here's a working jsFiddle.
If you move it out of the onload function, then the DOM elements (specifically your canvas) don't exist yet. See https://stackoverflow.com/a/15564394/1370556 for more information on DOM content loaded events.

Drawing on canvas after resize

I have a canvas element:
<canvas id="canvas" width="100" height="100"></canvas>
And some JavaScript to make canvas full screen:
var canvas, ctx, w, h;
function start() {
canvas = $("#canvas")[0];
ctx = canvas.getContext("2d");
w = $('body').innerWidth();
$("#canvas").width(w);
$("#canvas").height(w);
w = $("#canvas").width();
h = $("#canvas").height();
if(typeof game_loop != "undefined") clearInterval(game_loop);
game_loop = setInterval(paint, 60);
}
function paint() {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, w, h);
ctx.strokeStyle = "blue";
ctx.strokeRect(0, 0, 50, 50);
}
I don't know why I get one big square not 50x50. Can you help?
To actually resize the canvas (as in have more pixles) you'll need to set the width/height attributes. jQuerys width()/height() sets the css values. Resulting in a stretched canvas element consisting of the same number of pixels as before. Instead use:
$('#canvas').prop({
width: 400, // the 400 is just arbitrary
height: 400
});
Or, as per Alnitaks comment, you could of course just as well assign the values directly:
canvas.width = 400;
canvas.height = 400;
Here is a simple example of drawing a square on resize using width/height.
Example: http://jsfiddle.net/Ubv7X/
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var paint = function() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Draw background
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw rect
var rect_size=50;
ctx.fillStyle = 'rgb(0, 0, 255)';
ctx.fillRect(canvas.width/2-(rect_size/2), canvas.height/2-(rect_size/2),
rect_size, rect_size);
}
setInterval(paint, 60);

Categories

Resources