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
}
Related
I'm using p5.js to make a GIF animation but I have an issue when I want to export it.
I did a pixelased effect on my animation by adding these css properties to the Canva (140*140) :
image-rendering: pixelated;
width:500px;
height:500px;
My problem is that I can't export this Canva with the properties I added. (I'm using CCapture)
My gif is in 140x140 without the pixelated effect.
How can I get the rendering I need?
There is a difference between the width & height you can set for a HTML element e.g. <canvas width='140' height='140'> and the CSS width & height properties.
The former defines the actual size of the canvas - 140x 140in this case.
If we now set the CSS width & height to something deviating from the HTML element's width & height e.g. <canvas width='140' height='140' style='width: 500px; height:500px;'> the actual size in pixels of the canvas does not change - it stays 140 x 140 pixels. The CSS properties just control the displayed size of the element inside the browser, meaning the 140 x 140 are simply stretched to 500 x 500.
So if you get actual pixel data of the canvas - for exporting to gif/png - the final image will have the original dimensions of the canvas - not the rendered.
The fix is quite simple though. Instead of directly using the source canvas for exporting, draw it's content on a second canvas, the size of your desired resolution.
To force the 'export' canvas to not use any filtering/smoothing, you need to set the imageSmoothingEnabled property of it's context to false.
Here's an example (you can right-click and save both images to see the difference):
var ctx = document.getElementById('canvas').getContext('2d');
var image = new Image();
image.onload = function() {
ctx.drawImage(image, 0, 0);
let sourceCanvas = document.getElementById("canvas");
let virtualWidth = parseInt(getComputedStyle(sourceCanvas).width);
let virtualHeight = parseInt(getComputedStyle(sourceCanvas).height);
var canvas = document.getElementById("exportCanvas");
canvas.width = virtualWidth;
canvas.height = virtualHeight;
canvas.getContext('2d').imageSmoothingEnabled = false;
canvas.getContext('2d').drawImage(sourceCanvas, 0, 0, virtualWidth, virtualHeight);
}
image.src = 'https://mdn.mozillademos.org/files/12640/cat.png';
canvas {
width: 500px;
height: 500px;
}
<span>Rendered canvas</span><br>
<canvas id="canvas" width="140" height="140"></canvas><br>
<span>Export canvas</span><br>
<canvas id="exportCanvas"></canvas>
I'm using Raphael to draw an object, then transferring it to an HTML canvas element with canvg so that I can use toDataURL to save it as a PNG. But when I use canvg, the resulting image is blurry. The code below, for example, produces this (raphael on top, canvg on bottom):
<html>
<head>
<script src="lib/raphael-min.js"></script>
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/rgbcolor.js"></script>
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/StackBlur.js"></script>
<script type="text/javascript" src="http://canvg.googlecode.com/svn/trunk/canvg.js"></script>
<script src="lib/raphael.export.js"></script>
</head>
<body>
<div id="raph_canvas"></div><br>
<canvas id="html_canvas" width="50px" height="50px"></canvas>
<script language="JavaScript">
var test=Raphael("raph_canvas",50,50);
var rect=test.rect(0,0,50,50);
rect.attr({fill: '#fff000', 'fill-opacity':1, 'stroke-width':1})
window.onload = function() {
var canvas_svg = test.toSVG();
canvg('html_canvas',canvas_svg);
var canvas_html = document.getElementById("html_canvas");
}
</script>
</body>
</html>
The blurriness is evident in the png created by toDataURL as well. Any idea what is going on here? I don't think this has anything to do with re-sizing. I've tried setting ignoreDimensions: True and some other things.
Another datapoint. If I use raphael to output some text and then use canvg, it is not only blurry but the wrong font!
And here is the test.rect(0.5,0.5,50,50) suggested. Still blurry:
So it took me a while, but then it dawned on me. All your example images are twice the size the code claims they should be. So you're most likely on some sort of HDPI device (Retina MacBook Pro ect...) SVG is great because its resolution independent, canvas on the other hand is not. The issue you're seeing has to do with how canvas renders. To fix this, you need to prep the canvas so that your drawing will be done at the resolution of your screen.
http://jsbin.com/liquxiyi/3/edit?html,js,output
This jsbin example should look great on any screen.
The trick:
var cv = document.getElementById('box');
var ctx = cv.getContext("2d");
// SVG is resolution independent. Canvas is not. We need to make our canvas
// High Resolution.
// lets get the resolution of our device.
var pixelRatio = window.devicePixelRatio || 1;
// lets scale the canvas and change its CSS width/height to make it high res.
cv.style.width = cv.width +'px';
cv.style.height = cv.height +'px';
cv.width *= pixelRatio;
cv.height *= pixelRatio;
// Now that its high res we need to compensate so our images can be drawn as
//normal, by scaling everything up by the pixelRatio.
ctx.setTransform(pixelRatio,0,0,pixelRatio,0,0);
// lets draw a box
// or in your case some parsed SVG
ctx.strokeRect(20.5,20.5,80,80);
// lets convert that into a dataURL
var ur = cv.toDataURL();
// result should look exactly like the canvas when using PNG (default)
var result = document.getElementById('result');
result.src=ur;
// we need our image to match the resolution of the canvas
result.style.width = cv.style.width;
result.style.height = cv.style.height;
This should explain the issue you're having, and hopefully point you in a good direction to fix it.
Another solution described in this article, similar to the one posted here, except it's using scale() and it's taking into account the pixel ratio of the backing store (browser underlying storage of the canvas):
var devicePixelRatio = window.devicePixelRatio || 1,
backingStoreRatio = context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1,
ratio = devicePixelRatio / backingStoreRatio;
// upscale the canvas if the two ratios don't match
if(devicePixelRatio !== backingStoreRatio){
// adjust the original width and height of the canvas
canvas.width = originalWidth * ratio;
canvas.height = originalHeight * ratio;
// scale the context to reflect the changes above
context.scale(ratio, ratio);
}
// ...do the drawing here...
// use CSS to bring the entire thing back to the original size
canvas.style.width = originalWidth + 'px';
canvas.style.height = originalHeight + 'px';
I am trying to write a image magnifier with canvas, It works fine but the problem is when I mouseover the image the image drawn on the canvas is not positioned correctly based on the mouse position on the image.
You can see the problem here
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Magnifier</title>
<style type="text/css">
canvas
{
border:1px solid #000;
width:150px;
height:150px;
border-radius:80px;
position:absolute;
}
</style>
</head>
<body>
<img src="natural.jpg" height="400" id="image" width="600" onMouseMove="move(event);">
<canvas id="magnifier"></canvas>
<input type="button" name="magnify" id="magnify" value="magnify" onClick="magnify()" />
</body>
</html>
<script type="text/javascript">
var flag = 0;
var lens = document.getElementById('magnifier');
var img = document.getElementById('image');
var ctx=lens.getContext("2d");
function magnify()
{
flag = 1;
}
function move(e)
{
if(flag == 1)
{
var co_ord = getImageCoords(e,img);
var x = co_ord['x']+img.offsetLeft;
var y = co_ord['y']+img.offsetTop;
draw(x,y);
/*lens.style.top = y+"px";
lens.style.left = x+"px";*/
}
}
function getImageCoords(event,img) {
var cords = new Array;
cords['x'] = event.offsetX?(event.offsetX):event.pageX-img.offsetLeft;
cords['y'] = event.offsetY?(event.offsetY):event.pageY-img.offsetTop;
return cords;
}
function draw(a,b)
{
ctx.clearRect(0,0,lens.width,lens.height);
ctx.drawImage(img,a,b,150,150,0,0,300,150);
}
</script>
The main problem is that you are scaling the original image by forcing it into a 640 by 400 img element. The actual image is quite a bit larger. The width and height are just presentation settings, so when you draw the image ctx.drawImage(img,a,b,150,150,0,0,300,150); it is drawing the original unsized image. This means that your mouse coordinates do not match the location on the original image.
I can see two options:
1. Resize original by drawing to canvas
See update here. I haven't used cssdeck before, so here is a fiddle in case I didn't save it properly. Basically, it resizes the image to a canvas (resizeCanvas) and then uses this canvas for the drawing:
Relevant HTML:
<canvas id='resizeCanvas' height='400' width='600' onMouseMove="move(event);"></canvas>
Relevant JavaScript:
var ctxR=resizeCanvas.getContext("2d");
ctxR.drawImage(theImg,0,0,600,400);
There were a few other tweaks I made. First, you should specify the width and height attributes directly on the magnifier canvas. Otherwise, if this is different from the css then this will cause scaling. Then you can do the scaling to double the size by:
ctx.drawImage(img,a,b,150,150,0,0,300,300);
The only drawback of that approach is that you have a high res image that you are uploading and then losing some quality when you maginify which seems a pity. So, a better approach might be to load the original image without adding to the dom and then translate the x,y coords appropriately for the original image. Which is the second approach:
2. Scale x,y coordinates (better quality)
See the update here (fiddle here as well). As you can see, the quality is much better.
Here, we load the original image:
var origImage = document.createElement('img');
var origImage.src = '<image source>'
Then, just scale accordingly:
scaleX = origImage.width/img.width;
scaleY = origImage.height/img.height;
ctx.clearRect(0,0,lens.width,lens.height);
ctx.drawImage(img,a*scaleX,b*scaleY,150,150,0,0,150,150);
Note that we are not actually doing any resizing of the original image at all when we draw it to the canvas (width and height are 150 in all cases), instead we are just showing it at its larger native size. For smaller images, you may want to resize according to some fudge factor.
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..
I have seen some similar questions here but not actually what I need to know!
I am using Flash CS6 and outputting a CreateJS framework animation instead of regular .swf files. When you publish from within the API (Flash) it generates an html5 doc and an external .js file with the actual javascript that defines the animation.
Here's what I need: I want my animation to either be able to go full screen and maintain it's aspect ratio -OR- be set at say 1024x768 and be centered in the browser window but if viewed on a mobile device, dynamically resize to fit the device screen size or viewport size and centered.
A perfect example of what I need is here: http://gopherwoodstudios.com/sandtrap/ but I don't see which code is doing the dynamic resizing in this example.
Any help would be greatly appreciated. In addition, I am supplying the html5/js output of the Flash API since it seems to be very, very different than the example code given in other canvas-related posts.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CreateJS export from index</title>
<script src="http://code.createjs.com/easeljs-0.5.0.min.js"></script>
<script src="http://code.createjs.com/tweenjs-0.3.0.min.js"></script>
<script src="http://code.createjs.com/movieclip-0.5.0.min.js"></script>
<script src="http://code.createjs.com/preloadjs-0.2.0.min.js"></script>
<script src="index.js"></script>
<script>
var canvas, stage, exportRoot;
function init() {
canvas = document.getElementById("canvas");
images = images||{};
var manifest = [
{src:"images/Mesh.png", id:"Mesh"},
{src:"images/Path_0.png", id:"Path_0"}
];
var loader = new createjs.PreloadJS(false);
loader.onFileLoad = handleFileLoad;
loader.onComplete = handleComplete;
loader.loadManifest(manifest);
}
function handleFileLoad(o) {
if (o.type == "image") { images[o.id] = o.result; }
}
function handleComplete() {
exportRoot = new lib.index();
stage = new createjs.Stage(canvas);
stage.addChild(exportRoot);
stage.update();
createjs.Ticker.setFPS(24);
createjs.Ticker.addListener(stage);
}
</script>
<style type="text/css">
body {text-align:center;}
#container { display:block;}
</style>
</head>
<body onload="init();" style="background-color:#D4D4D4">
<div id="container">
<canvas id="canvas" width="1024" height="768" style="background-color:#ffffff; margin: 20px auto 0px auto;"></canvas>
</div>
</body>
</html>
Thanks again!
don't know if you worked this one out in the end, but I recently had the same issue - I just needed to resize the whole createJs object to the viewport.
I added a listener to viewport resizing (I used jQuery), then resized the canvas stage to match the viewport, then using the height of the original flash stage height, or width depending on what you want (mine was 500), you can scale up the createJs movie object (exportRoot).
(function($){
$(window).resize(function(){
windowResize();
});
})(jQuery);
function windowResize(){
stage.canvas.width = window.innerWidth;
stage.canvas.height = window.innerHeight;
var test = (window.innerHeight/500)*1;
exportRoot.scaleX = exportRoot.scaleY = test;
}
Hope that helps someone!
function resizeGame()
{
widthToHeight = 600 / 350;
newWidth = window.innerWidth;
newHeight = window.innerHeight;
newWidthToHeight = newWidth / newHeight;
if (newWidthToHeight > widthToHeight)
{
newWidth = newHeight * widthToHeight;
gameArea.style.height = newHeight + 'px';
gameArea.style.width = newWidth + 'px';
} else
{
newHeight = newWidth / widthToHeight;
gameArea.style.height = newHeight + 'px';
gameArea.style.width = newWidth + 'px';
}
scale = newWidthToHeight / widthToHeight;
stage.width = newWidth;
stage.height = newHeight;
gameArea.style.marginTop = ((window.innerHeight - newHeight) / 2) + 'px';
gameArea.style.marginLeft = ((window.innerWidth - newWidth) / 2) + 'px';
}
widthToHeight is your game canvas scaling ratio. gameArea is your div id
make it sure your html tag must contain
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
The inline width and the height of the canvas define "resolution" - setting a CSS style of width defines "scale". Think of it like canvas size and image size in photoshop.
Because width and height of elements isn't defined when you export, I'm struggling to come up with a good way to scale up. But, you can always scale down. Use CSS to set the max-width and max-height to be 1024x768 (or whatever your original is) and then set the regular width and height to be whatever you need (proportionately).
Then just use CSS to center - margin:auto and all that.
I find it useful to allow my canvas to be fluid based on width, and then adjust the height based on the aspect ratio on resize. There are a couple things to keep in mind.
You must assign a width & height attribute to the canvas element whether you're creating with javascript or with html
You have to adjust the object's style.height property, not the canvas height property directly.
If you follow this sort of example you can even use media queries to give even more flexibility. jsFiddle, if you please.
var el = document.getElementById('mycanvas');
var aspectRatio = el.height / el.width;
resizeCanv = function resize (){
var style = getComputedStyle(el);
var w = parseInt(style.width);
el.style.height = (w * this._aspectRatio) + 'px';
};
// TODO: add some logic to only apply to IE
window.addEventListener('resize', resizeCanv);
EDIT: I haven't tested this with any interactivity within the canvas, only layout and animations.