Canvas drawimage() not displaying image - javascript

I am trying to make a full screen, responsive image on Canvas. I believe I have the sizing figured out, but it isn't drawing the image and is instead leaving the canvas blank.
This is the HTML and JavaScript I am currently using:
<canvas id="MainCanvas"></canvas>
<script>
var canvas = document.getElementById("MainCanvas");
var context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var wRatio = canvas.width / imageObj.width;
var hRatio = canvas.height / imageObj.height;
var ratio = Math.min (wRatio, hRatio);
var imageObj = new Image();
imageObj.onload= function() {
context.drawImage(imageObj, 0, 0, canvas.width, canvas.height,imageObj.width*ratio, imageObj.height*ratio);
};
imageObj.src="Local File.jpg";
</script>
CSS:
canvas{
border: 5pt solid black;
position: relative;
height: 100%;
width: 100%;
}
Any help is greatly appreciated.

First off, good job on the minimal example. When I ran it in jsFiddle, here's what I got in my javascript console.
Uncaught TypeError: Cannot read property 'width' of undefined
Looking at your code, i noticed that it was trying to reference the width and height of the image before it had loaded. I put that code inside your onLoad function and that error was corrected. Next I got this error:
Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': Valid arities are: [3, 5, 9], but 7 arguments provided.
Seeing that, I looked up CanvasRenderingContext2D on MDN. That showed me what it required, and I added a 0,0 for the dx, dy values. This is completely wrong of course, but it at least gets the image to render (jsfiddle link if the below example doesn't work correctly):
var canvas = document.getElementById("MainCanvas");
console.log(canvas);
var context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var imageObj = new Image();
imageObj.onload= function() {
var wRatio = canvas.width / imageObj.width;
var hRatio = canvas.height / imageObj.height;
var ratio = Math.min (wRatio, hRatio);
context.drawImage(imageObj, 0, 0, canvas.width, canvas.height,0,0,imageObj.width*ratio, imageObj.height*ratio);
};
imageObj.src="//placehold.it/500";
html, body {
height: 100%;
}
canvas{
border: 5pt solid black;
position: relative;
height: 100%;
width: 100%;
box-sizing: border-box;
display:block;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.css" rel="stylesheet"/>
<canvas id="MainCanvas"></canvas>

Related

Canvas fill viewport and keep image ratio

I am trying to make the canvas area and image fill the viewport without the image stretching and retain its aspect ratio when you resize the page. Below is my current code.
var ctx = $("#demo")[0].getContext("2d"),
img = new Image(),
radius = 35,
blurryImageSrc = "https://s9.postimg.cc/u9nsmzlwf/image.jpg";
img.src = blurryImageSrc;
$(img).on("load", function() {
resizeCanvas();
$("#demo").on("mousemove", function(e) {
erase(getXY(e));
});
$("#reset").on("click", function() {
ctx.globalCompositeOperation = "source-over";
ctx.drawImage(img, 0, 0);
ctx.globalCompositeOperation = "destination-out";
});
ctx.drawImage(img, 0, 0, window.innerWidth, window.innerHeight);
ctx.globalCompositeOperation = "destination-out";
});
function getXY(e) {
var r = $("#demo")[0].getBoundingClientRect();
return { x: e.clientX - r.left, y: e.clientY - r.top };
}
function erase(pos) {
ctx.beginPath();
ctx.arc(pos.x, pos.y, radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
}
var can = document.getElementById('demo');
function resizeCanvas() {
can.style.width = window.innerWidth + 'px';
setTimeout(function() {
can.style.height = window.innerHeight + 'px';
}, 0);
}
window.onresize = resizeCanvas;
resizeCanvas();
https://jsfiddle.net/65fph0mn/8/
To retain the image's correct proportions, you need to query it's 'natural' width and height. The image object itself has two properties for this purpose: naturalWidth and naturalHeight.
As the image finished loading you must decide by which factor to scale the image based on the dimensions of the browser window and the longer side of your image.
Let's have a look at a simple example. Say your browser window's width is 1200 pixel and the image 250. If we now divide 1200 by 250 we get 4.8 - that's the factor we need to multiply the image's width and height to fill the current browser window in one direction while maintaining it's correct aspect ratio.
Here's an example:
var ctx = $("#demo")[0].getContext("2d"),
img = new Image(),
radius = 35,
blurryImageSrc = "https://s9.postimg.cc/u9nsmzlwf/image.jpg";
/// setup logic
img.src = blurryImageSrc;
$(img).on("load", function() {
resizeCanvas();
});
var can = document.getElementById('demo');
function resizeCanvas() {
can.width = window.innerWidth;
can.height = window.innerHeight;
if (img.width > 0) {
let factor = can.width / img.naturalWidth * img.naturalHeight > window.innerHeight ? can.height / img.naturalHeight : can.width / img.naturalWidth;
ctx.drawImage(img, 0, 0, img.naturalWidth * factor, img.naturalHeight * factor);
}
}
window.onresize = resizeCanvas;
resizeCanvas();
body {
background: lightgrey;
margin: 0;
}
.container {
position: relative;
background: blue;
width: 100vw;
height: 100vh;
}
#demo {
cursor: crosshair;
width: 100vw;
height: 100vh;
}
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<div class="container">
<canvas id="demo" width=640 height=640></canvas>
</div>
Here I'm waiting for the image to load. Then I can add the eventlistener for window resize and call the function resizeCanvas. This function will then resize the canvas and paint the image in the canvas.
The image must be repainted every time the window is resized.
To keep the aspect ratio the height of the image is calculated from the scaled width.
const canvas = document.getElementById('demo');
var ctx = canvas.getContext("2d"),
img = new Image(),
blurryImageSrc = "https://s9.postimg.cc/u9nsmzlwf/image.jpg";
img.src = blurryImageSrc;
img.addEventListener("load", e => {
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
});
function paint() {
var newHeight = Math.round(img.width/canvas.width*img.height);
ctx.drawImage(img, 0, 0, img.width, newHeight, 0, 0, canvas.width, canvas.height);
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
paint();
}
body {
background: lightgrey;
margin: 0;
}
.container {
position: relative;
background: blue;
width: 100vw;
height: 100vh;
}
#demo {
cursor: crosshair;
width: 100vw;
height: 100vh;
}
<div class="container">
<canvas id="demo" width="640" height="640"></canvas>
</div>

Keep canvas object in one place against changing size of the entire canvas

l was wondering about the logic required in-order to position a canvas object, in this instance it is a image, in the bottom right corner of the canvas persistently. l essentially need help with the canvas object staying in one place without it moving up,down,left,right due to the resizable canvas.
l also have tested using event listeners to constantly update the position of the canvas object depending on the window size of the canvas but l believe it would end up becoming an inefficient solution.
To see it working, full screen/expand the code snippet and change your window size by double clicking your browser's tab or simply resize it, then you should be able to see it in action.
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
var the_button = document.getElementById("the_button");
var the_background = document.getElementById("the_background");
var button_imageX = 115;
var button_imageY = 85;
window.onload = function() {
ctx.drawImage(the_background, 0, 0, canvas.width, canvas.height);
ctx.drawImage(the_button, button_imageX, button_imageY, 130, 75);
}
initialize();
function initialize() {
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
}
function redraw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(the_background, 0, 0, canvas.width, canvas.height);
ctx.drawImage(the_button, button_imageX, button_imageY, 130, 75);
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
redraw();
}
html,
body {
width: 100%;
height: 100%;
margin: 0px;
border: 0;
overflow: hidden;
display: block;
}
<html>
<canvas id="c"></canvas>
<img style="display: none;" id="the_button" src="https://i.imgur.com/wO7Wc2w.png" />
<img style="display: none;" id="the_background" src="https://img.freepik.com/free-photo/hand-painted-watercolor-background-with-sky-clouds-shape_24972-1095.jpg?size=626&ext=jpg" />
</html>
This is all about rectangle geometry.
Basically the culprit is this line that is present at 2 different places.
ctx.drawImage(the_button, button_imageX, button_imageY, 130, 75);
When you want the button to be a certain fixed position when you resize, you should provide fixed values as the 2nd and 3rd params for the drawImage function.
Change it to this,
ctx.drawImage(the_button, button_imageX, canvas.height - button_imageY, 130, 75);
The 2nd param button_imageX is already fixed.
But the 3rd param is the value of y from the top of the canvas where your button should be. Not from the bottom. You must be confused with cartesian coordinate system.
So if you want your button to be always a fixed position from bottom. you should canvas.height.
So canvas.height - button_imageY means that the top left point of your button will always be button_imageY pixels above the bottom of the canvas.
You might also want to consider the height of the button, so the new 3rd parameter will become canvas.height - button_imageY - button_height
Check the snippet for full code.
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
var the_button = document.getElementById("the_button");
var the_background = document.getElementById("the_background");
var button_imageX = 25;
var button_imageY = 25;
window.onload = function() {
ctx.drawImage(the_background, 0, 0, canvas.width, canvas.height);
drawButton();
}
initialize();
function initialize() {
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
}
function drawButton() {
ctx.drawImage(the_button, button_imageX, canvas.height - button_imageY - 75, 130, 75);
}
function redraw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(the_background, 0, 0, canvas.width, canvas.height);
drawButton();
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
redraw();
}
html,
body {
width: 100%;
height: 100%;
margin: 0px;
border: 0;
overflow: hidden;
display: block;
}
<html>
<canvas id="c"></canvas>
<img style="display: none;" id="the_button" src="https://i.imgur.com/wO7Wc2w.png" />
<img style="display: none;" id="the_background" src="https://img.freepik.com/free-photo/hand-painted-watercolor-background-with-sky-clouds-shape_24972-1095.jpg?size=626&ext=jpg" />
</html>

How to get my image drawn on a canvas proportionally, yet not leave any borders when the window is resized

I am currently in the process of making my website. I would like it to be interactive and have a nice design.
What I am currently doing is just drawing an image onto my canvas, with a bit of JavaScript that will resize my image proportionally as the window is resized. However, when I resize it to the smallest browser size, or the largest browser size, since it is proportional, it leaves a white border at the side.
I have already tried using CSS to fill the webpage by using 'width: 100%' and 'height: 100%' or 'width / height: auto' but that doesn't seem to change anything. I have also tried other resize functions (to below). but they are all very similar, if not the same.
img.onload = function () {
ctx.canvas.width = img.width;
ctx.canvas.height = img.height;
ctx.drawImage(img, 0, 0);
resize();
};
function resize()
{
var ratio = canvas.width / canvas.height;
var canvas_height = window.innerHeight;
var canvas_width = canvas_height * ratio;
if(canvas_width>window.innerWidth)
{
canvas_width=window.innerWidth;
canvas_height=canvas_width/ratio;
}
canvas.style.width = canvas_width + 'px';
canvas.style.height = canvas_height + 'px';
}
Taken from: https://codepen.io/anon/pen/VNjwYE
What should happen is that the image takes up the whole canvas with no borders at the sides. However, as I said, there are white borders at the side of image.
This imgur album has two screenshots of the problem. https://imgur.com/a/CDbKGjL
The rest of the code (i.e. CSS and HTML) can be found on my Codepen: https://codepen.io/DreamingInsanity/pen/aPoJEK
Thanks,
Dream.
just add body{ margin:0px; } in your css,
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = "https://source.unsplash.com/2000x1000/?trees,nature,forest";
img.onload = function () {
ctx.canvas.width = img.width;
ctx.canvas.height = img.height;
ctx.drawImage(img, 0, 0);
resize();
};
function resize() {
var ratio = canvas.width / canvas.height;
var canvas_height = window.innerHeight;
var canvas_width = canvas_height * ratio;
if(canvas_width>window.innerWidth)
{
canvas_width=window.innerWidth;
canvas_height=canvas_width/ratio;
}
canvas.style.width = canvas_width + 'px';
canvas.style.height = canvas_height + 'px';
}
window.onresize = resize;
html, body {
background-color: #1A1919;
overflow-x: hidden;
overflow-y: hidden;
margin: 0px;// do it
}
#canvas {
width: auto;
height: auto;
filter: grayscale(0%);
}
#top_bar {
background-color: transparent;
position: absolute;
width: 100%;
height: 80px;
top: 0px;
}
<html>
<head runat="server">
<title>My Website</title>
<link rel="stylesheet" type="text/css" href="Home/Stylesheet.css">
<link rel="stylesheet" type="text/css" href="Home/Buttons.css" />
</head>
<body>
<canvas id="canvas"></canvas>
<script src="Scripts/Canvas.js"></script>
<div id="top_bar"></div>
</body>
</html>
I changed what code editor I was using and at the same time, neetened up my file structure. As you can see in the HTML I am looking for the CSS under /Home/ whereas now, I am looking for it under /CSS/.

How prevent blurry drawing on canvas when using putImageData?

I want to use putImageData to paint on canvas. But for some reason the painted pixels are blurry and I don't know why. Here is a minimal example:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<style>
html, body, canvas { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; }
</style>
</head>
<body oncontextmenu="return false;">
<canvas id='canvas'></canvas>
<script type='text/javascript'>
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
let uarr = new Uint8ClampedArray(4);
uarr[0] = 0;
uarr[1] = 0;
uarr[2] = 0;
uarr[3] = 255;
let imgData = new ImageData(uarr , 1, 1);
ctx.putImageData(imgData, 20, 20);
</script>
</body>
</html>
This should paint one black pixel at the 20,20 coordinates, but for some reason it look blurry.
How can I force to draw sharp edges?
You need to set the canvas width and height attributes, as your CSS is stretching a default size canvas to the size of the screen, like stretching a too small image. After that ctx.imageSmoothingEnabled should work fine.
var canvas = document.getElementById('canvas');
// This will stretch correctly
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
var uarr = new Uint8ClampedArray(4);
uarr[0] = 0;
uarr[1] = 0;
uarr[2] = 0;
uarr[3] = 255;
var imgData = new ImageData(uarr , 1, 1);
ctx.putImageData(imgData, 20, 20);
html, body, canvas { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; }
<canvas id='canvas'></canvas>
As #Kaido mentions in the comments below, imageSmoothingEnabled does not really work that way. Check his fiddle for a little bit more of an in-depth look at what it can be used for.

Cannot draw text in javascript

I am trying to display simple text on the screen via update() and setInterval() functions, but it's not working. Note that I create a canvas on which I want the text to be displayed. Please , check the code below.
var canvas, canvasContext;
window.onload = function() {
canvas = document.getElementById('theCanvas');
canvasContext = canvas.getContext('2d');
var fps = 30;
setInterval(update, 1000 / fps);
}
function update() {
drawText();
}
function drawText() {
canvasContext.fillStyle = 'black';
canvasContext.font = 'bold 40px Monospace';
canvasContext.fillText('POC ......', 150, 150);
}
#canvasHolder {
position: absolute;
left: 0px;
top: 0px;
}
#theCanvas {
background-color: lightgreen;
width: 100pc;
height: 100pc;
}
<div id="canvasHolder">
<canvas id="theCanvas"></canvas>
</div>
The issue is that the canvas is being stretched to fill the element. Both the canvas itself and the canvas element have a size. If you don't specify the size of the canvas (you haven't), it defaults to 300x150. When the size of the element and the canvas don't match, the canvas content is scaled to fit into the element.. So the text is being drawn, it's just really big; scroll to the right and down and you'll find it.
My guess is that you meant for the canvas and the element to be the same size. If so, you need to set the size of the canvas. You can do that with width and height attributes on the canvas element, but those values will be in pixels, not picas as in your CSS. So we do it dynamically by getting the computed width and height of the element (because they'll come back to us in pixels), and then setting those values for the width and height attributes of the canvas:
// Set the canvas size to match the element size
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
Updated snippet:
var canvas, canvasContext;
window.onload = function() {
canvas = document.getElementById('theCanvas');
// Set the canvas size to match the element size
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
canvasContext = canvas.getContext('2d');
canvasContext.canvas.width = canvas.width;
canvasContext.canvas.height = canvas.height;
var fps = 30;
setInterval(update, 1000 / fps);
};
function update() {
drawText();
}
function drawText() {
canvasContext.fillStyle = 'black';
canvasContext.font = 'bold 40px Monospace';
canvasContext.fillText('POC ......', 150, 150);
}
#canvasHolder {
position: absolute;
left: 0px;
top: 0px;
}
#theCanvas {
background-color: lightgreen;
width: 100pc;
height: 100pc;
}
<div id="canvasHolder">
<canvas id="theCanvas"></canvas>
</div>
Note that right now, you're just redrawing it at the same location every time, but I assume moving it was your next task...
The text is already rendered but if you want to animate your text then you have to change your draw function because each time you are drawing your text in the same position -
function drawText() {
canvasContext.fillStyle = 'black';
canvasContext.font = 'bold 40px Monospace';
canvasContext.fillText('POC ......', 150, 150); //here you have given the fixed position.
}
and you have to clear your canvas before rendering your text in new position-
please refer -
How to clear the canvas for redrawing

Categories