I'm trying to recreate a Snapchat "snap" with HTML5 Canvas. The part that has me stumped is getting the font to look the same. The font Snapchat uses seems to be Helvetica, but I can't get my text to look the same as that of a real snap.
Here's my attempt of matching the font. I've drawn my text on top of an actual screenshot of a snap so it's easy to compare. You can edit the JavaScript variables to try to match the text (JSFiddle: http://jsfiddle.net/9RB88/1/).
HTML:
<img src='http://i.imgur.com/tivQ8xJ.jpg'>
<canvas width='640' height='1136'></canvas>
CSS:
img {display:none}
JavaScript:
//--- EDIT THESE ----------------
// text styles...
var fontFamily = 'Helvetica';
var fontSize = '35px';
var fontWeight = 'normal';
// http://css-tricks.com/almanac/properties/f/font-weight/
var onTop = false;
// set this to "true" to place our text on top of the real text (for more precise comparing)
var differentColours = false;
// draw our text/background different colours, making it easier to compare (use when "onTop" is "true")
// tweak where the text is drawn
var horOffset = 2;
var vertOffset = 0;
//-------------------------------
var can = document.getElementsByTagName('canvas')[0];
var c = can.getContext('2d');
c.fillStyle = 'white';
c.fillRect(0, 0, can.width, can.height);
var img = document.getElementsByTagName('img')[0];
c.drawImage(img, 0, 0);
var top;
if(onTop) top = 531;
else top = 450;
if(differentColours) c.fillStyle = 'orange';
else c.fillStyle = 'black';
c.globalAlpha = 0.6;
c.fillRect(0, top, can.width, 74);
c.globalAlpha = 1;
var text = 'Template example thingy $&#?! 123';
if(differentColours) c.fillStyle = 'red';
else c.fillStyle = 'white';
c.textAlign = 'center';
c.font = fontWeight + ' ' + fontSize + ' ' + fontFamily;
c.fillText(text, can.width/2 + horOffset, top + 50 + vertOffset);
JSFiddle: http://jsfiddle.net/9RB88/1/
It seems like one of the issues is letter spacing, but that's not possible to change in HTML5 Canvas.
What can I do?
Snapchat may embed a very specific variant or similar font to Helvetica. That doesn't mean the user loading the page (incl. on your own machine) will have that font installed on the system.
If the user doesn't have the font installed the browser will look for similar fonts, for example, it will use Arial on Windows and if that is not available use another sans-serif font. On Mac and Linux the same thing. This mean it will perhaps closely match but will not be the same font. If they don't use a web font the snap could have been taken on a different OS then you are using resulting in a different font being rendered.
Or in other words - when you specify "Helvetica" as font family the system will try to find a font in that family but it isn't given that Helvetica is the font you'll get. The browser will try to find the closest matching font available on the system in use.
You have to embed the very same font they are using as a web font otherwise there is no guarantee that the font will be the same.
To find out which font they are using (if any) open the stylesheets they're using.
My 2 cents..
Related
I want to be able to:
Get the Font Awesome 5 unicode character for any given Font Awesome class name (fas fa-check-circle, etc...)
Draw that unicode character to an html5 canvas
How would I go about doing this?
To get the correct unicode character and other important data for a Font Awesome 5 icon class and draw it onto an html5 canvas:
// create an icon with the Font Awesome class name you want
const i = document.createElement('i');
i.setAttribute('class', 'fas fa-check-circle');
document.body.appendChild(i);
// get the styles for the icon you just made
const iStyles = window.getComputedStyle(i);
const iBeforeStyles = window.getComputedStyle(i, ':before');
const fontFamily = iStyles.getPropertyValue('font-family');
const fontWeight = iStyles.getPropertyValue('font-weight');
const fontSize = '40px'; // just to make things a little bigger...
const canvasFont = `${fontWeight} ${fontSize} ${fontFamily}`; // should be something like: '900 40px "Font Awesome 5 Pro"'
const icon = String.fromCodePoint(iBeforeStyles.getPropertyValue('content').codePointAt(1)); // codePointAt(1) because the first character is a double quote
const ctx = myCanvas.getContext('2d');
ctx.font = canvasFont;
ctx.fillStyle = 'red';
ctx.textAlign = 'center';
ctx.fillText(icon, myCanvas.width / 2, myCanvas.height / 2);
When you do this, make sure the Font Awesome 5 font has actually loaded when you do the drawing. I made the mistake of only drawing once when I was testing and it resulted in only a box showing up. When the icon has loaded it should appear like this:
I'm making a whiteboard app just for fun and have tools like the brush tool, eraser, etc and I draw on an HTML5 canvas.
I'm trying to implement custom cursors for my tools (like brush, line, square) and was just going to use the CSS cursor property depending on which tool is selected but I'd like sizes to vary depending on the current line width.
For example, in MS Paint you'll notice with the brush and eraser tool, depending on the size of the line width, the cursor is differently sized when drawing on the canvas.
So I have two questions. The first is if I can use CSS still and somehow alternate the cursor size dynamically from JS.
The second is how you would implement this solution by just deleting the cursor and drawing your own on the canvas that follows the mouse movement. I'm a bit concerned that the latter would be quite glitchy and more complex so am hoping that the first one is possible.
Maintaining the position of your own cursor does not work very well on the browsers. By the time you have handled the mouse move, waited for the next animation frame you are one frame behind. (drawing immediately to canvas in the mouse event does help) The is very off putting, even if you set the cursor to "none" so the two do not overlap, the 1/60th of a second can make a big difference from where you see your rendered cursor to and where the hardware cursor (for want of a better name) is.
But it turns out that the browsers Chrome and Firefox allow you to have full control of the hardware cursor image. I have personally exploited it to the limits and it is a robust and tolerant system.
I started with pre made cursors as DataURLs. But now most of my cursors are dynamic. If a control uses the mouse wheel, I add the wheel indicator on the cursor, when using resize cursor, instead of 8 directions N, NE, E, SE, S, SW, W, NW It is created on demand to line up with whatever I happen to be resizing at what ever angle it is. When drawing the cursor is continuously reorienting to avoid covering pixels I may be working on.
To set a custom cursor, set the elements style.cursor to an image URL, followed by two numbers that are the hotspot (Where the click is focused), and a cursor name. I just use the same name "pointer" and generate the image URL on the fly with toDataURL.
Below is a quick example of changing the cursor dynamically. I am unsure what the limits are in terms of size, but so far I haven't wanted a cursor that it has not given me. Some of them, in games have been very big (lol as cursors go) (128*128 pixels +)
Note a slight bug in code. Will return to fix the first rendered word being clipped soon.
The only issue with this is IE does not support this what of doing cursors, so you will need a fallback
// create an image to use to create cursors
var image = document.createElement("canvas");
image.width = 200;
image.height = 14;
image.ctx = image.getContext("2d");
// some graphic cursors
var cursors = [
"url('data:image/png;base64,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') 0 0, cursor",
"url('data:image/png;base64,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') 0 0, cursor ",
"url('data:image/png;base64,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') 0 0, cursor "
];
// Do the stuff that does the stuff
var el = document.getElementById("customeCurs");
var over = false;
// set up canvas rendering
var c = image.ctx;
// some stuff to say and ABC are graphic flags
var stuff = "A C Hello pointy clicky things are great and easy to use A B C B".split(" ");
var tHandle;
var wordPos=0;
function createCursor(){ // creates cursors from the canvas
// get a word from the word list
var w = stuff[wordPos % stuff.length];
if(w === "A" || w === "B" || w === "C"){ // display graphics cursor
// just for fun. to much time on
// my hands. I really need a job.
var datURL;
switch(w){
case "A":
datURL = cursors[0];
break;
case "B":
datURL = cursors[1];
break;
case "C":
datURL = cursors[2];
}
el.style.cursor = datURL;
}else{ // create a dynamic cursor from canvas image
// get the size. Must do this
var size = c.measureText(w).width + 4;
// resize the canvas
image.width = size;
image.height = 36;
c.font = "28px arial black";
c.textAlign ="center";
c.textBaseline = "middle";
c.lineCap = "round";
c.lineJoin = "round";
// Please always give user a visual guide to the hotspot.
// following draws a little arrow to the hotspot.
// Always outline as single colours can get lost to the background
c.lineWidth = 3;
c.strokeStyle = "white";
c.moveTo(1,5);
c.lineTo(1,1);
c.lineTo(5,1);
c.moveTo(1,1);
c.lineTo(8,8);
c.stroke();
c.strokeStyle = "black";
c.lineWidth = 1;
c.moveTo(1,5);
c.lineTo(1,1);
c.lineTo(5,1);
c.moveTo(1,1);
c.lineTo(8,8);
c.stroke();
c.lineWidth = 5;
c.strokeStyle = "black";
c.fillStyle = "White";
// Draw the text outline
c.strokeText(w, image.width / 2, image.height / 2+2);
// and inside
c.fillText(w, image.width / 2, image.height / 2);
// create a cursor and add it to the element CSS curso property
el.style.cursor = "url('"+image.toDataURL("image/png")+"') 0 0 , pointer"; // last two
// numbers are the
// cursor hot spot.
}
// Next word
wordPos += 1;
// if the mouse still over then do it again in 700 ticks of the tocker.
if(over){
tHandle = setTimeout(createCursor,700);
}
}
// mouse over event to start cursor rendering
el.addEventListener("mouseover",function(){
over = true;
if(tHandle === undefined){
createCursor();
}
});
el.addEventListener("mouseout",function(){
over = false; // clean up if the mouse moves out. But leave the cursor
clearTimeout(tHandle);
tHandle = undefined;
});
<div id="customeCurs" >Move Mouse Over ME.</div>
<!-- Some say don't put style in HTML doc! The standards are indifferent. The author is lazy :P -->
<span style="font-size:small;color:#BBB;">Sorry IE again you miss out.</span>
I am using paper.js to create a (non-interactive) display. I have fixed area inside which I want to show some text. When the text is large, I want to clip the text so as to show only the part inside the fixed area is visible to the user. In addition, in that case, I want to scroll the text left-to-right repeatedly.
As far as I could read in the paperjs documentation, there is no strait forward way to achieve this (.
So, I am thinking of doing the following:
Determine if the text is larger than the container box. Could not find an easy way to do this. Thinking of having a simple heuristic function - which goes by the number of characters in the text.
Clip the text within the container box. I have not figured out yet but it appears possible.
Change the anchor point gradually to the left to create the scrolling effect. Again to determine how far to go, we would need the size of the text - but may need to depend on a heuristic function again.
May be the upcoming feature AreaText would make doing this easier. Please let me know if there is a better way to do this in the meantime. Thanks.
Please let me know if there are samples in plain canvas or in another js framework ...
If PaperJS works well for your app but you need scrolling text, you could make a hybrid app that uses both a PaperJS canvas and a native html5 canvas. The native html5 canvas could just do the scrolling text.
Since you ask for ideas...Here's an autoscrolling text script that I did a while back.
You're welcome to use the code as a starting point for your scrolling script. It word-wraps sentences into paragraphs and then displays them on an auto-scrolling html canvas.
When you need to display some text you could temporarily overlay your PaperJS canvas with this scrolling text html5 canvas and remove the html5 canvas when done displaying the text. Its would also be easy to give the user control of the scrolling using an html input-range control.
Example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var Paragraphs=( function(){
function Paragraphs(x,y,maxwidth,fontsize,fontface){
this.x=x;
this.y=y;
this.originalY=y;
this.maxwidth=maxwidth;
this.fontsize=fontsize;
this.fontface=fontface;
this.autoLineAdvance=true;
this.writeCount=0;
this.p=[];
this.nextTime=0;
this.duration=1000/60*1;
this.viewheight=150;
this.offsetY=0;
this.canvas=document.createElement('canvas');
this.ctx=this.canvas.getContext('2d');
this.totalHeight=0;
};
Paragraphs.prototype.addParagraph=function(text){ this.p.push(text); }
Paragraphs.prototype.autoscroll=function(viewportHeight){
this.viewheight=ch;
this.offsetY=ch;
requestAnimationFrame(this.animatescroll.bind(this));
};
Paragraphs.prototype.animatescroll=function(time){
var that=this;
if(this.offsetY>0){
requestAnimationFrame(that.animatescroll.bind(that));
}else{log('done');}
if(time<this.nextTime){return;}
this.nextTime=time+this.duration;
ctx.clearRect(0,0,cw,ch);
ctx.drawImage(this.canvas,this.x,this.offsetY);
this.offsetY-=0.50;
};
Paragraphs.prototype.lineAdvance=function(){this.y+=this.fontsize*1.286*1.5};
Paragraphs.prototype.drawOffCanvas=function(){
var y=0;
var lineCount=0;
var lineHeight = this.fontsize*1.286;
var lineAdvance=lineHeight*1.5;
this.canvas.width=this.maxwidth;
this.canvas.height=this.height();
this.ctx.textBaseline='top';
this.ctx.font = this.fontsize + "px " + this.fontface;
for(var i=0;i<this.p.length;i++){
var words=this.p[i].split(' ');
var line = '';
var space='';
for (var n=0; n<words.length; n++) {
var testLine = line + space + words[n];
space=' ';
if (this.ctx.measureText(testLine).width > this.maxwidth) {
this.ctx.fillText(line, 1, y);
line = words[n] + ' ';
y += lineHeight;
space='';
} else {
line = testLine;
}
}
this.ctx.fillText(line, 1,y);
y+=lineAdvance;
}
};
Paragraphs.prototype.height=function(){
ctx.save();
ctx.textBaseline='top';
ctx.font = this.fontsize + "px " + this.fontface;
var lineHeight = this.fontsize*1.286;
var writeCount=0;
var height=lineHeight*(this.p.length-1)*0.50;
var line,space;
for(var i=0;i<this.p.length;i++){
var words=this.p[i].split(' ');
line=space='';
if((writeCount++)>0){ height+=lineHeight; }
for (var n=0; n<words.length; n++) {
var testLine = line + space + words[n];
space=' ';
if (ctx.measureText(testLine).width > this.maxwidth) {
line = words[n] + ' ';
height+=lineHeight;
space='';
} else {
line = testLine;
}
}
}
height+=lineHeight;
ctx.restore();
this.totalHeight=height;
return(height);
}
return(Paragraphs);
})();
var d=new Paragraphs(35,20,200,14,'verdana');
d.addParagraph("I am using paper.js to create a (non-interactive) display. I have fixed area inside which I want to show some text. When the text is large, I want to clip the text so as to show only the part inside the fixed area is visible to the user. In addition, in that case, I want to scroll the text left-to-right repeatedly.");
d.addParagraph("Please let me know if there are samples in plain canvas or in another js framework ...");
d.drawOffCanvas();
d.autoscroll();
body{ background-color: ivory; padding:10px; }
canvas{border:1px solid red;}
<h4>Text scrolls in from the bottom<br>Be patient or click full page mode</h4>
<canvas id="canvas" width=300 height=300></canvas>
The canvas seems to default to a width of 300 and a height of 150. What I want it a much smaller canvas with text on it, a tooltip. As such I have three queries about this sample (on JSfiddle):
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
c.style.height="25px";
//2: This stretches the visible portion of the text. Not what I wanted.
//c.style.width="125px";
ctx.font="130px Georgia";
//3: If I don't want to set the font family this doesn't seem to set the font size.
//ctx.fontSize="130";
ctx.fillText("Hello World!",0,130);
</script>
</body>
</html>
I have reduced the size of the canvas but I need to increase the font size dramatically which seems an odd thing to have to do. Is this approach correct?
I can't get the whole 'Hello World' to display as increasing the size of the canvas stretches the visible portion of the text. How do I show the whole text?
How can I set the font size without setting the font family?
Thanks.
Use this instead:
c.width = 200; // just for example. Defined in pixels using integer values
c.height = 25;
CSS only affects the element but not the bitmap used for the context. Think of canvas as an edit-able image. An image would simply be stretched with CSS while the actual width and height would be the same as the original.
You can't set the font size without specifying font family.
You can extract current font and alter that:
var cFont = ctx.font,
parts = cFont.split(' ');
if (parts.length === 2) {
ctx.font = newSize + 'px ' + parts[1];
}
else if (parts.length === 3) {
ctx.font = parts[0] + ' ' + newSize + 'px ' + parts[2]; //bold/italic/.. used
}
Is there a way to only change the font size of a canvas context without having to know/write the font family.
var ctx = document.getElementById("canvas").getContext("2d");
ctx.font = '20px Arial'; //Need to speficy both size and family...
Note:
ctx.fontSize = '12px'; //doesn't exist so won't work...
ctx.style.fontSize = '20 px' //doesn't exist so won't work...
//we are changing the ctx, not the canvas itself
Other note: I could do something like: detect where 'px' is, remove what's before 'px' and replace it by my font size. But I'd like something easier than that if possible.
Here is an easier and cleaner way of changing the font size that will work regardless if you are using font-variant or font-weight or not.
First some RegEx to match and return the current font size
const match = /(?<value>\d+\.?\d*)/;
Set an absolute size, assuming your new font size is 12px
ctx.font = ctx.font.replace(match, 12);
Set a relative size, increase by 5:
const newSize = parseFloat(ctx.font.match(match).groups.value + 5);
ctx.font = ctx.font.replace(match, newSize);
Working example:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// Match on digit at least once, optional decimal, and optional digits
// Named group `value`
const match = /(?<value>\d+\.?\d*)/;
const setFontSize = (size) => ctx.font = ctx.font.replace(match, size);
const adjustFontSize = (amount) => {
const newSize = parseFloat(ctx.font.match(match).groups.value + amount);
return ctx.font = ctx.font.replace(match, newSize);
}
// Default font is 12px sans-serif
console.log( setFontSize(18) ); // 18px sans-serif
console.log( adjustFontSize(2) ); // 20px sans-serif
console.log( adjustFontSize(-5) ); // 15px sans-serif
<canvas id="canvas"></canvas>
Update: (from comments) There is no way around specifying font. The Canvas' font is modeled after the short-hand version of font in CSS.
However, there is always a font set on the canvas (or a font type) so what you can do is to first extract the current font by using it like this:
var cFont = ctx.font;
Then replace size arguments and set it back (note that there might be a style parameter there as well).
A simple split for the sake of example:
var fontArgs = ctx.font.split(' ');
var newSize = '12px';
ctx.font = newSize + ' ' + fontArgs[fontArgs.length - 1]; /// using the last part
You will need support for style if needed (IIRC it comes first if used).
Notice that font-size is fourth parameter, so this will not work if you will have/not have font-variant(bold,italic,oblique), font-variant(normal, small-caps) and font-weight(bold etc).
A cleaner way to not worry about scraping every other parameter:
canvas.style.font = canvas.getContext("2d").font;
canvas.style.fontSize = newVal + "px";
canvas.getContext("2d").font = canvas.style.font;
Explanation:
As mentioned in the first answer, the canvas context will always have a font. So canvas.getContext("2d").font might return something like "10px sans-serif". To not scrape this string we can use the Canvas DOM element (or any other element) to parse the full font spec and populate the other font fields. Which means that after setting:
canvas.style.font = "10px sans-serif";
canvas.style.fontSize = "26px";
the canvas.style.font will become "26px sans-serif". We can then pass this font spec back to the canvas context.
To correctly answer your question, there is no way to change the font size of a canvas context without having to know/write the font family.
try this (using jquery):
var span = $('<span/>').css('font', context.font).css('visibility', 'hidden');
$('body').append(span);
span[0].style.fontWeight = 'bold';
span[0].style.fontSize = '12px';
//add more style here.
var font = span[0].style.font;
span.remove();
return font;