Is there a way to modify an Image with Javascript? - javascript

So I got a project where the user can generate a pdf based on their input. Using the jspdf library to generate the PDF files. The thing is though, the user can upload a profile picture, and I would like to display that image with rounded corners or fully rounded (with a border-radius of 50%). Since there isn't a native function that allows this in jspdf or any other library as far as I know (pfdkit, pdfmake), I am looking for a way to modify the image before generating the pdf.
I already tried using html2canvas, and that worked fine actually. The problem with html2canvas occurs when a user is on their mobile. Since the width and height of the image are adjusted to the screen size (both of which are around 35px), taking a snapshot with html2canvas and then displaying that in a pdf with a width & height of 100px, the image obviously gets way to blurry.
So ideally, I need something to edit the original image or something before generating a PDF file with jspdf.
Any other ideas that bring me closer to a solution are also very much appreciated.
Just for clarification, simply adding a CSS property to the image won't help. jspdf just uses the image in the img tag, without any css properties.

I would suggest you to add a class to the image before you generate the pdf and define the rule for that class in css:
.circle {
border-radius: 50%;
}
Or, even you may need to force in case there's already css with some border-radius value to img tag:
.circle {
border-radius: 50% !important;
}

It is possible to use rounded images on jspdf, you just need to apply the rounded-ness to the image before adding it to the PDF, if your already resizing you have the context.
roundedImage taken from: Canvas drawimage with round corners
For example (wont work on SO, so no demo):
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="UTF-8" />
<script src="https://unpkg.com/jspdf#latest/dist/jspdf.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
body {
background: #ccc;
}
#pdf {
display: block;
position: absolute;
bottom: 0;
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<embed id="pdf" src="#" type="application/pdf" width="100%" height="100%" />
<script>
function roundedImage(ctx, x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(
x + width,
y + height,
x + width - radius,
y + height
);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
}
var img = new Image();
img.src = "https://graph.facebook.com/649650002/picture?type=square";
img.setAttribute("crossorigin", "anonymous");
img.onload = function() {
//
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
roundedImage(ctx, 0, 0, 50, 50, 5);
ctx.clip();
ctx.drawImage(img, 0, 0, img.width, img.height);
ctx.restore();
// Default export is a4 paper, portrait, using milimeters for units
var doc = new jsPDF();
doc.text("woop!..rounded corners.", 10, 15);
doc.addImage(canvas.toDataURL("image/png"), "PNG", 15, 25, 30, 30);
document.getElementById("pdf").src = doc.output(
"dataurlstring",
"its-a.pdf"
);
};
</script>
</body>
</html>

If anyone for whatever reason stumbles across this post, I managed to actually achieve my desired result. As I said the user is able to upload an image and I would like to display that image with rounded corners or with a border-radius of 50%. Anyway, before previewing (and uploading) the image to my website, the user has to crop the image using cropperjs. They can then decide for themselves if they would like to display the image with rounded corners or with a border radius of 50%. I think that gives a tremendous boost to the UX and my end result.

Related

width and height in canvas are not accurate

I'm trying to draw a canvas with width x*100px and height y*100px
var x=6,y=5;
var canvas = document.getElementById('game');
var ctx = canvas.getContext('2d');
canvas.style.width=(100*x)+"px";
canvas.style.height=(100*y)+"px";
for(var i=0;i<x;i++)
for(var j=0;j<y;j++)
{
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (i*100, j*100, 100, 100);
ctx.fillStyle = "rgb(0,0,0)";
ctx.strokeRect(i*100, j*100, 100, 100);
}
jsfiddle
but it looks like the width and height of each tile in the grid are not 100 px, Why is that? and how can I set the width of each tile exactly to 100px?
Use this instead:
canvas.width=(100*x);
canvas.height=(100*y);
It seems a bit strange, but canvas is one element type that does not use CSS styling for its width and height. The number of available physical pixels is very relevant to the way that it draws, so it can't be left as a transient, derived CSS value (ie, width: calc(300vw - 100% - 20px);). That said, it's usually a good idea to have width/height change on a browser resize if you're making the web page responsive.

Using drawImage() to output fixed size images on a canvas?

How do I use drawImage() to output full size images on a 300px X 380px canvas regardless of the source image size?
Example:
1). If there is a image of 75px X 95px I want to be able to draw it to fit a 300px X 380px canvas.
2). If there is a image of 1500px X 1900px I want to be able to draw it to fit a 300px X 380px canvas.
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById("myPic");
ctx.drawImage(img,10,10);
What options are available to prevent any quality loss?
To scale the image to fit is not so hard, just use simple aspect ratio with the sizes:
var ratioX = canvas.width / image.naturalWidth;
var ratioY = canvas.height / image.naturalHeight;
var ratio = Math.min(ratioX, ratioY);
ctx.drawImage(image, 0, 0, image.naturalWidth * ratio, image.naturalHeight * ratio);
To maintain quality; a canvas of 300x380 will either appear very tiny on print, or very blurry.
It's important to keep the data from it in the target resolution. To do this, calculate the size using the target DPI (or rather, PPI). You will also need to know in advance what size the 300x380 area represents (e.g. in either inches, centimeters, millimeters etc.).
For example:
If the target PDF will have a PPI of 300, and the canvas represents 3 x 3.8 cm (just to keep it simple), then the width and height in pixel will be:
var w = (3 / 2.54) * 300; // cm -> inch x PPI
var h = (3.8 / 2.54) * 300;
Use this size on canvas' bitmap, then scale down the element using CSS:
canvas.width = w|0; // actual bitmap size, |0 cuts fractions
canvas.height = h|0;
canvas.style.width = "300px"; // size in pixel for screen use
canvas.style.height = "380px";
You can now use the canvas directly as an image source for the PDF while keeping print quality for the PDF (have in mind though, small images uploaded will not provide high print quality in any case).
And of course, set the canvas size first, then use the code at top to draw in the image.
You'll need to resize your source image. You will need to calculate the destination size of the image, then use a couple extra parameters during your draw.
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = 188;
var y = 30;
var width = 200;
var height = 137;
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, x, y, width, height);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
body {
margin: 0px;
padding: 0px;
}
<canvas id="myCanvas" width="578" height="200"></canvas>
Source: http://www.html5canvastutorials.com/tutorials/html5-canvas-image-size/

Locate mouse position relative to transformed plane

I've set up a jsfiddle illustrating my situation: http://jsfiddle.net/j5o0w5qc/1/
Basically, I've got three nested HTML elements: a viewport div on the outside, a stage div in the middle, and a canvas on the inside. The stage div provides a perspective setting for 3d transformations applied to the canvas. The viewport has overflow: hidden; so we don't see anything outside of the viewport. It also has a listener attached, listening for mousedown.
In my actual app that I'm building, the canvas might be transformed to any arbitrary 3d transformation, involving translation and rotation in 3d space. What I would like to happen is for the viewport div to intercept a click, and draw a spot on the canvas in the place you clicked. I'm intercepting the event with the viewport div, and I'm using offsetX and offsetY in Chrome. This works great for Chrome, but I know I can't rely on offsetX and offsetY in other browsers, so I'd like to use pageX and pageY, normalized via jQuery, but I'm not sure quite how to do that.
What I've currently got in the jsfiddle works great in Chrome, except when you click in the viewport NOT on the canvas. When you click on the canvas, it draws a dot there, regardless of the canvas's transformation. Chrome is doing all the hard work and giving me exactly what I want with offsetX and offsetY. However, when you click in the viewport NOT on the canvas, I guess it's giving me offsetX and offsetY values relative to the viewport, rather than the canvas, and then interpreting that and drawing a dot on the canvas. For example, if I transform the canvas and then click in the upper right corner of the viewport, a dot appears in the upper right corner of the canvas, regardless of where that corner actually appears on the page.
In Firefox, however, it works great as long as there is no transformation applied to the canvas, but as soon as the canvas is transformed, all of a sudden, the dot being drawn is displaced, and I can't figure out how to take my pageX and pageY values and figure out exactly where in the canvas I am clicking.
Does anyone have any brilliant solutions? I've been bashing my head against this problem for far too long. I'm pretty sure I need to manually calculate some 3d transformation matrices or something, and I've spent hours writing methods to return the inverse of a matrix, and to multiply a matrix by a vector, and all sorts of stuff, but none of it has actually solved the problem for me, and I'm not sure what I'm missing.
Stackoverflow says code is required with jsfiddle links, so here's all my code:
HTML:
<div id="viewport">
<div id="stage">
<canvas id="myCanvas" width="300" height="300"></canvas>
</div>
</div>
<div id="stuff">
<button onclick="transformMe()">Transform</button>
<input id="blah" type="text" size="45"></input>
</div>
CSS:
#viewport, #stage, #myCanvas {
width: 300px;
height: 300px;
position: absolute;
top: 0;
left: 0;
}
#viewport {
border: 1px solid #000;
overflow: hidden;
}
#stage {
perspective: 1000px;
transform-style: preserve-3d;
}
#myCanvas {
background-color: green;
transform-style: preserve-3d;
}
#stuff {
position: absolute;
top: 350px;
}
Javascript:
var counter = 0;
$('#viewport').mousedown(function _drawOnCanvas (e)
{
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var xpos, ypos;
if (typeof e.offsetX=='undefined')
{
xpos = e.pageX - $('#myCanvas').offset().left;
ypos = e.pageY - $('#myCanvas').offset().top;
}
else
{
xpos = e.offsetX;
ypos = e.offsetY;
}
ctx.fillRect(xpos-5, ypos-5, 10, 10);
});
function transformMe()
{
counter++;
var angle = (counter * 30) % 360;
$('#myCanvas').css('transform','perspective(1000px) rotate3d(5,6,7,' + angle + 'deg)');
$('input').val('counter: ' + counter + ', angle: ' + angle);
};
For Firefox, you can use event.layerX and event.layerY. Think of them as Firefox's versions of offsetX & offsetY.
DEMO: http://jsfiddle.net/dirtyd77/j5o0w5qc/3/
JAVASCRIPT:
var counter = 0;
$('#viewport').mousedown(function _drawOnCanvas (e)
{
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var xpos, ypos;
if (typeof e.offsetX=='undefined')
{
xpos = e.originalEvent.layerX;
ypos = e.originalEvent.layerY;
}
else
{
xpos = e.offsetX;
ypos = e.offsetY;
}
ctx.fillRect(xpos-5, ypos-5, 10, 10);
});
function transformMe()
{
counter++;
var angle = (counter * 30) % 360;
$('#myCanvas').css('transform','perspective(1000px) rotate3d(5,6,7,' + angle + 'deg)');
$('input').val('counter: ' + counter + ', angle: ' + angle);
};
If you change viewport to myCanvas in line 3 of the either Kyle S or Dom's jsfiddles:
$('#myCanvas').mousedown(function _drawOnCanvas (e)
it no longer places a dot when you "click in the viewport NOT on the canvas."
It seems there's a new issue with Firefox - if there's a transformation it only lets you paint on half ( the bottom left of diagonal - but depends on transformation ).

Bad quality for 100% both width and height of canvas

I have done a very tiny example with canvas, it's available on JsFiddle:
http://jsfiddle.net/yPtr5/
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
html, body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
}
#myCanvas {
width: 100%;
height: 100%;
display: block;
}
</style>
</head>
<body>
<canvas id="myCanvas">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var canvas = document.getElementById( "myCanvas" );
var context = canvas.getContext( "2d" );
context.id = "myContext";
context.beginPath();
context.arc( 95, 50, 40, 0, 2 * Math.PI );
context.stroke();
setTimeout( function() {
var rectWidth = 150;
var rectHeight = 75;
context.fillStyle = "blue";
context.fillRect( rectWidth / -2, rectHeight / -2, rectWidth, rectHeight );
}, 2000 );
</script>
</body>
</html>
As you are able to see, the rendering result has a very low quality:
So, I'm wondering, how can I draw various figures using Canvas in a good quality, I don't want to draw in small size, I want to draw in 100% size of page.
So, maybe I didn't define some anti aliasing filter or something else?
Thanks!
Problem
In most general cases we should avoid using CSS to set the canvas size.
The default size of canvas is 300 x 150 pixels (bitmap). If you set the size using CSS we'll just end up scaling those 300 x 150 pixels meaning the browser will start interpolating and smoothing the image, which is why you end up with a blurry result.
Solution
Remove these from the CSS-rule:
#myCanvas {
/*width: 100%;
height: 100%;*/
display: block;
}
and set the size in JavaScript like this:
var canvas = document.getElementById( "myCanvas" );
canvas.width = window.innerWidth; // equals window dimension
canvas.height = window.innerHeight;
You can of course set any other size you need (in pixels). You probably want to define position (i.e. fixed or absolute) for the canvas' CSS as well if your goal is full window size.
Hope this helps.
The height and width need to be set on the height and width attributes of the canvas tag and not in CSS. Any CSS sizing of the canvas element merely stretches the canvas and does not size it properly.
<canvas id="canvas" width="500px" height="500px">
Have a look at this project that I posted on my site: Creating an HTML5 Paint App
It includes a functionto resize the canvas when the browser window size changes (which you would have to modify):
this.onScreenSizeChanged = function (forceResize) {
if (forceResize || (this.canvas.width != window.innerWidth /*||
this.canvas.height != window.innerHeight*/)) {
var image = this.context.getImageData(0, 0,
this.canvas.width, this.canvas.height);
this.canvas.width = (window.innerWidth);
this.canvas.height = (window.innerHeight);
this.context.putImageData(image, 0, 0);
}
}
this.onScreenSizeChanged(true);
In my case, it was a problem with the screen pixel ratio.
To solve it, I created a canvas with a higher pixel ratio as follows:
function createHiPPICanvas(w, h) {
let ratio = window.devicePixelRatio;
let cv = document.createElement("canvas");
cv.width = w * ratio;
cv.height = h * ratio;
cv.style.width = w + "px";
cv.style.height = h + "px";
cv.getContext("2d").scale(ratio, ratio);
return cv;
}
Then I also increased accordingly the pixel ratio of the images used inside of the canvas.

Use <canvas> as a CSS background

Can I use the canvas element as a css background?
This has been possible in WebKit since 2008, see here.
<html>
<head>
<style>
div { background: -webkit-canvas(squares); width:600px; height:600px; border:2px solid black }
</style>
<script type="application/x-javascript">
function draw(w, h) {
var ctx = document.getCSSCanvasContext("2d", "squares", w, h);
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, 55, 50);
}
</script>
</head>
<body onload="draw(300, 300)">
<div></div>
</body>
</html>
Currently, Firefox 4 contains a feature, which allows you to use any element (including canvas) as a CSS background, in this fashion:
<p id="myBackground1" style="background: darkorange; color: white; width: 300px; height: 40px;">
This element will be used as a background.
</p>
<p style="background: -moz-element(#myBackground1); padding: 20px 10px; font-weight: bold;">
This box uses #myBackground1 as its background!
</p>
See Mozilla hacks for specifics.
Yes!!!! You can put a canvas in CSS background.
var Canvas = document.createElement("canvas");
... do your canvas drawing....
$('body').css({'background-image':"url(" + Canvas.toDataURL("image/png")+ ")" });
I know this is a pretty old question but I felt like posting my answer for people who'd visit this page because this is the correct answer, in just one line of code, using the .toDataURL function. It works in every browser that supports canvas.
I think the closest you could get is to render into a canvas, call toDataUrl() on it to retrieve the contents as an image, and assignment that result to the desired element's background-image property. This will only give a static background, though. If you want to be able to further update the canvas, however, then you'll need to instead position the canvas behind another element, as Johan has already suggested.
I've been triying to achieve this same feature past weeks, the best solution I've found its the same proposed by bcat:
Render canvas (visible or hidden)
Get canvas image with "canvas.toDataURL"
Asign this image-data as background image for the element (I use MooTools)
The bad news, for static images works great, but with animation in Chrome sometimes "blinks", and in Firefox blinks-a-lot. Maybe someone knows a workaround to get rid of this "nasty blinking".
Best regards.
P:.
<!DOCTYPE html>
<html>
<head>
<title>Asign canvas to element background</title>
<script type="text/javascript" src="/js/mootools.1.2.4.js"></script>
<style type="text/css">
* {
outline:0;
padding:0;
margin:0;
border:0;
}
body {
color:#fff;
background:#242424;
}
</style>
<script>
window.addEvent('domready',function() {
//GET BODY
var mibodi = $('mibodi');
var viewportSize = mibodi.getSize();
//GET CANVAS
var micanvas = $('micanvas');
var ctx = micanvas.getContext('2d');
var playAnimation = true;
//GET DIV
var midiv = $('midiv');
//VARIABLES
var rotate_angle = 0;
var rotate_angle_inc = 0.05;
//FUNCIÓN DE INICIALIZACIÓN
function init(){
ctx.clearRect (0, 0, 512, 512); //CLEAR CANVAS
ctx.fillStyle = 'rgba(128,128,128,1)';
ctx.strokeStyle = 'rgba(255,255,255,1)';
if (playAnimation) {
setInterval(draw,100);//
}
} //INIT
//FUNCIÓN DE DIBUJADO
function draw() {
//CLEAR BACKGROUND
ctx.clearRect (0, 0, 512, 512);
//DRAW ROTATING RECTANGLE
ctx.save();
ctx.translate( micanvas.width / 2, micanvas.height / 2 );
ctx.rotate( rotate_angle );
ctx.fillRect(0, 0, 100, 100);
ctx.restore();
//GET CANVAS IMAGE
var dataURL = micanvas.toDataURL("image/png");
//SET IMAGE AS BACKGROUND OF THE ELEMENTS
midiv.setStyle('background-image', 'url(' + dataURL + ')');
mibodi.setStyle('background-image', 'url(' + dataURL + ')');
//ANGLE INCREMENT
rotate_angle = rotate_angle + rotate_angle_inc;
} //DRAW
//BEGIN TO DRAW
init();
});//domeady
</script>
</head>
<body id="mibodi" >
<canvas id="micanvas" width="512" height="512" style="float:left;" style="display:none;">
Este texto se muestra para los navegadores no compatibles con canvas.
<br>
Por favor, utiliza Firefox, Chrome, Safari u Opera.
</canvas>
<div id="midiv" style="width:512px;height:512px;background:#f00;float:left;">
Sample
</div>
</body>
</html>
Try -moz-element(#id) for CSS background in Firefox.
And -webkit-canvas(name) for CSS background in WebKit based browsers.
You can use CSS Paint API
.elem {
backgound: paint(squares);
}
See more details here:
Blog posts:
https://vitaliy-bobrov.github.io/blog/exploring-the-css-paint-api/
https://vitaliy-bobrov.github.io/blog/css-paint-in-action-bar-chart/
Demos: https://vitaliy-bobrov.github.io/css-paint-demos/
You can emulate this behavior quickly without the performance drop of toDataURL() using z-index (granted, it's a workaround, since CSS images 4 / CSS Houdini hasn't implemented "background: element(#mycanvas)" as of 2017))
Working JSFiddle here. I didn't write this, all credit goes to Derek Leung:
http://jsfiddle.net/DerekL/uw5XU/
Unable to comment so I will create my own answer for this.
This answer is based off of #livedo, #Eric Rowell, and #shabunc
http://jsfiddle.net/MDooley47/yj26psdb/
window.i = 0;
function draw(w, h) {
window.i+=5;
if (window.webkitURL != null) {
var ctx = document.getCSSCanvasContext("2d", "squares", 100, 100);
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, w, h);
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, w, h);
}
else {
var ctxmozc = document.getElementById("squares");
var ctxmoz = ctxmozc.getContext("2d");
ctxmoz.fillStyle = "rgb(200,0,0)";
ctxmoz.fillRect (10, 10, w, h);
ctxmoz.fillStyle = "rgba(0, 0, 200, 0.5)";
ctxmoz.fillRect (30, 30, w, h);
}
}
setInterval(function(){draw(window.i, window.i);}, 500);
div {
background: -webkit-canvas(squares);
background: -moz-element(#squares) repeat-x;
width:575px;
height:475px;
border:2px solid black
}
<body>
<div></div>
<canvas id="squares" name="squaresmoz" style="display: none;" ></canvas>
</body>

Categories

Resources