adjusting canvas to fit screen on scroll - javascript

I have a canvas element dynamically created which overlays every other element. On resize, the canvas fits to the screen perfectly without any trouble.
I figured that the same principle would work with scrolling, but it does not:
var a = document.createElement('canvas');
createCanvas();
function createCanvas(){
a.height = $(window).height();
a.width = $(window).width();
a.id = "some_canvas";
a.style.opacity = "0.8";
a.style.position = "absolute";
document.body.insertBefore(a,document.body.firstChild);
context = a.getContext("2d");
context.fillStyle = "#000000";
context.fillRect(0, 0, a.width, a.height);
}
$(window).resize(function(){
$("some_canvas").remove();
createCanvas();
});
$(window).scroll(function(){
$("#some_canvas").remove();
createCanvas();
});
https://jsfiddle.net/9mfxytoz/1/
the canvas remains at it's last size adjustment, but does not follow through with scrolling. I'm trying to make it so that the canvas can always be seen, even when scrolling.

Try this:
<canvas id="Canvas2D"></canvas>
CSS:
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#Canvas2D {
position:absolute;
top:0;
left:0;
}
And here is the JS required to make it all work:
// set the width and height to the full windows size
var SCREEN_WIDTH = window.innerWidth;
// Change to scroll height to make it the size of the document
var SCREEN_HEIGHT = document.documentElement.scrollHeight;
// Make sure to set the canvas to the screen height and width.
canvas.width = SCREEN_WIDTH;
canvas.height = SCREEN_HEIGHT;
// everytime we resize change the height and width to the window size, and reset the
// center point
window.onresize = function () {
SCREEN_HEIGHT = canvas.height = document.documentElement.scrollHeight;
SCREEN_WIDTH = canvas.width = document.body.offsetWidth;
HALF_WIDTH = SCREEN_WIDTH / 2;
HALF_HEIGHT = SCREEN_HEIGHT / 2;
};

Related

Canvas gets blurred on various coordinates (browser zoom level < 100%)

I am using a device that has devicePixelRatio of 1. On scroll, I am re-rendering the canvas by adding 1 or -1 to the y position. It affects the rendering quality and more importantly, the rendering position keeps changing on scroll. For instance, if I render with 2px space which is in between the line and the text it differs on scroll. This reproduces only when you scroll very slightly.
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
let width = 500;
let height = 500;
canvas.width = width * devicePixelRatio;
canvas.height = height * devicePixelRatio;
ctx.scale(devicePixelRatio,devicePixelRatio)
let startY = 0;
let lineHeight = 30;
let offSet = 0.5;
let padding = 2;
function draw(){
for(let i=0;i<30;i++){
let y = (i*lineHeight)+(startY%lineHeight);
ctx.beginPath()
ctx.moveTo(250+offSet,y+offSet);
ctx.lineTo(250+100+offSet,y+offSet);
ctx.stroke();
ctx.font = '20px Arial';
ctx.fillText('text',250+30+offSet,y+offSet-padding);
}
}
draw();
canvas.addEventListener('wheel',()=>{
let wheel = Math.round(event.wheelDeltaY/6);
ctx.clearRect(0,0,500,500);
startY += wheel;
draw();
});
canvas{
width : 500px;
height : 500px;
border : 1px solid;
}
<canvas></canvas>
How can I fix this?
You can see my project on codepen too.

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>

Image following a mouse on a canvas

I want to have an image follow the mouse around the canvas, which is fairly easy, but the catch is that I want my canvas to change with screen resolution (it is set using CSS to be 70vw).
When the resolution decreases and the window becomes smaller this means that using a normal method of using clientX doesn't work.
My code so far is this:
var mouseX = e.clientX/document.documentElement.clientWidth * 1920;
var mouseY = e.clientY/document.documentElement.clientHeight * 943;
This tries to convert the users clientX into the value it would be on a 1920x1080 monitor.
However, this isn't really accurate and doesn't work very well on even 1920x1080 monitors. Any help would be appreciated.
You can't scale the canvas using CSS in the way that you think. A canvas is basically a more advanced image. Scaling the canvas via CSS just stretches the canvas the same way an image would stretch. To change the canvas height and width, you need to change it's height and width attributes in the tag or via code. This will physically change the canvas to the size that you want without scaling and/or stretching.
That being said, we can use this to watch for window size changes and resize the canvas when the window changes.
window.addEventListener('resize', e => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
})
With some basic math, we can calculate what a 70% width would be, it would be done like this
window.addEventListener('resize', e => {
canvas.width = window.innerWidth * 0.7
canvas.height = window.innerHeight
})
The next thing we need to do is get the local position of the mouse on the canvas, which can be done using mousePosition - canvasOffset like this
let x = e.clientX - canvas.offsetLeft
let y = e.clientY - canvas.offsetTop
When all is said and done, we end up with something like this (To see it in action press run then click on Full Page and you will see the canvas resize):
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
// Set the inital height and width of the canvas
canvas.width = window.innerWidth
canvas.height = window.innerHeight
canvas.addEventListener('mousemove', e => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Get the local x/y coordinates of the mouse on the canvas
let x = e.clientX - canvas.offsetLeft
let y = e.clientY - canvas.offsetTop
// Draw a dot where the mouse is
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI, false);
ctx.fillStyle = 'white';
ctx.fill();
})
// Update the height and width when the window size changes
window.addEventListener('resize', e => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
})
body {
padding: 0;
margin: 0;
}
canvas {
background-color: black;
display: block;
}
<canvas></canvas>
In this example below, we use a canvas that is 70% the width and height of the screen and center it with CSS. However, we never touch the height/width with css because it will mess up the canvas' coordinate system. This part is done with JavaScript.
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
// Set the inital height and width of the canvas
canvas.width = window.innerWidth * 0.7
canvas.height = window.innerHeight * 0.7
canvas.addEventListener('mousemove', e => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Get the local x/y coordinates of the mouse on the canvas
let x = e.clientX - canvas.offsetLeft
let y = e.clientY - canvas.offsetTop
// Draw a dot where the mouse is
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI, false);
ctx.fillStyle = 'white';
ctx.fill();
})
// Update the height and width when the window size changes
window.addEventListener('resize', e => {
canvas.width = window.innerWidth * 0.7
canvas.height = window.innerHeight * 0.7
})
body {
padding: 0;
margin: 0;
}
canvas {
background-color: black;
display: block;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
}
<canvas></canvas>
I took my snippet from my answer to create a full screen canvas.
I added this for mouse movement:
let User = { x: 0, y: 0 };
//controles if the mouse is moving
window.addEventListener(
"mousemove",
e => {
User.x = e.clientX;
User.y = e.clientY;
},
false
);
Uncomment: cvs.ctx.drawImage(image, User.x, User.y); in the ShowImage() function to draw an image at the mouse x and y position.
Mind to replace the path of the image source: image.src = "Your/Path/To/Image.png";
/**
* #author RensvWalstijn. GitHub: https://github.com/RensvWalstijn
* Sets the canvas properties.
* #param {object} Cvs Give the html canvas Id.
* #param {boolean} Fullscreen Change the canvas fullscreen default false.
* #param {string} Dimension Change the canvas dimension default "2d".
* #return {object}
*/
function NewCanvas(cvs, fullscreen, dimension) {
if (!dimension) dimension = "2d";
var ctx = cvs.getContext(dimension);
if (fullscreen) {
cvs.style.position = "fixed";
cvs.style.left = cvs.x = 0;
cvs.style.top = cvs.y = 0;
} else {
var rect = cvs.getBoundingClientRect();
cvs.x = rect.left;
cvs.y = rect.top;
}
cvs.ctx = ctx;
cvs.dimension = dimension;
cvs.fullscreen = fullscreen;
return cvs;
}
/**
* #author RensvWalstijn. GitHub: https://github.com/RensvWalstijn
* Updates the canvas width and hight.
* #param {object} Cvs NewCanvas() object.
* #param {boolean} Clear Change the canvas clear default true.
*/
function UpdateCvs(cvs) {
if (cvs.fullscreen) {
//if the width is not the same resize the canvas width
if (window.innerWidth != cvs.width) {
cvs.width = window.innerWidth;
}
//if the height is not the same resize the canvas height
if (window.innerHeight != cvs.height) {
cvs.height = window.innerHeight;
}
} else {
let rect = cvs.getBoundingClientRect();
cvs.x = rect.left;
cvs.y = rect.top;
}
}
function ClearCvs(cvs) {
if (cvs.dimension == "2d")
// set fillRect to clearRect to clear all of the canvas
// fillRect is used here to show the full canvas
cvs.ctx.fillRect(0, 0, cvs.width, cvs.height);
}
/**
* #author RensvWalstijn. GitHub: https://github.com/RensvWalstijn
* get html element by id.
* #param {string} id give the html element id.
* #return {object} document.getElementById(id);
*/
function GetId(id) { return document.getElementById(id) }
// To create your canvas object.
var canvas = NewCanvas(GetId("yourCanvasId"), true);
// If you want to update your canvas size use this:
window.addEventListener("resize", function() {
UpdateCvs(canvas);
});
let User = { x: 0, y: 0 };
//controles if the mouse is moving
window.addEventListener(
"mousemove",
e => {
User.x = e.clientX;
User.y = e.clientY;
},
false
);
// Set it to current width
UpdateCvs(canvas);
ClearCvs(canvas);
// create an image
let image = new Image();
image.src = "Your/Path/To/Image.png";
function ShowImage(cvs) {
// Use this line to draw your image.
// cvs.ctx.drawImage(image, User.x, User.y);
// Shows where your image will be drawn.
cvs.ctx.clearRect(User.x, User.y, 100, 100);
}
function Update() {
ClearCvs(canvas);
ShowImage(canvas);
// keeps it looping
window.requestAnimationFrame(Update)
}
// Init the loop
Update();
<canvas id="yourCanvasId"></canvas>

Can I specify canvas dimensions using vh and vw?

My code is:
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.canvas.width = 40vw;
ctx.canvas.height = 40vh;
and it doesn't work. Is it possible to use vw and vh when setting canvas dimensions in JavaScript? If so, how?
I realised I could use document.documentElement.clientWidth and document.documentElement.clientHeight to work out the vw and vh respectively.
HTML:
<canvas class="canvas_hangman"></canvas>
JS:
function setUpCanvas() {
canvas = document.getElementsByClassName("canvas_hangman")[0];
ctx = canvas.getContext('2d');
ctx.translate(0.5, 0.5);
// Set display size (vw/vh).
var sizeWidth = 80 * window.innerWidth / 100,
sizeHeight = 100 * window.innerHeight / 100 || 766;
//Setting the canvas site and width to be responsive
canvas.width = sizeWidth;
canvas.height = sizeHeight;
canvas.style.width = sizeWidth;
canvas.style.height = sizeHeight;
}
window.onload = setUpCanvas();
This perfectly sets up your HTML canvas to draw on, in a responsive manner too.

Want to optimize code for captured image resize and rotate using canvas of html5, which now giving low memory warning on mobile

I have written code which will capture photo using camera and show it as snapshot. But before showing it as snapshot I am resizing image and rotate (if captured image is horizontal). This re-sized image is binary content which is less in size so uploading it on server becomes quick. Now I want to optimize code so that it will work better with low memory.
Problem facing:
On mobile device, sometimes chrome browser showing "unable to complete previous operation due to low memory".
Could be possible solution (i think but with question):
Processing image in chunks, will make think work better?
Thanks in advance for your help.
You may see working code here
or
Working sample code
<style>
#yourimage {
width:100%;
}
.imgWeightHght {
height: 290px !important;
width: 220px !important;
}
.captureInsuPanel img {
height: auto;
width: auto;
}
.newLoaderMask {
display: none;
background: #E5E5E5;
position: fixed;
left: 0;
top: 0;
z-index: 10;
width: 100%;
height: 100%;
opacity: 0.8;
z-index: 999;
}
</style>
<div id="mask" class="newLoaderMask">
<img src="http://jimpunk.net/Loading/wp-content/uploads/loading1.gif" width="200" id="loader" />
</div>
<input type="file" accept="image/*" capture="camera" id="takePictureField" />
<div class="captureInsuPanel">
<img id="yourimage" class="imgWeightHght" width="500" />
</div>
<script>
$(document).ready(function () {
$("#takePictureField").on("change", gotPic);
});
var max_width = 1000;
var max_height = 1000;
function gotPic(event) {
if (event.target.files.length == 1 && event.target.files[0].type.indexOf("image/") == 0) {
var image = event.target.files[0];
processCapturedImage(image, 90, max_width, max_height, $("#yourimage"));
}
}
function processCapturedImage(image, angle, max_width, max_height, thumbNailImage) {
$('#mask').show();
//create a hidden canvas object we can use to create new rotated image
var canvas = document.createElement('canvas');
canvas.id = "hidden_canvas_old";
canvas.style.display = "none";
document.body.appendChild(canvas);
//create a hidden canvas object we can use to create the new cropped &/or resized image data
var canvas_new = document.createElement('canvas');
canvas_new.id = "hidden_canvas_new";
canvas_new.style.display = "none";
document.body.appendChild(canvas_new);
var fileLoader = new FileReader(),
imageObj = new Image();
if (image.type.indexOf("image/") == 0) {
fileLoader.readAsDataURL(image);
} else {
alert('File is not an image');
}
fileLoader.onload = function () {
var data = this.result;
imageObj.src = data;
};
fileLoader.onabort = function () {
alert("The upload was aborted.");
};
fileLoader.onerror = function () {
alert("An error occured while reading the file.");
};
// set up the images onload function which clears the hidden canvas context,
// draws the new image then gets the blob data from it
imageObj.onload = function () {
var imgWidth = this.width;
var imgHeight = this.height;
if (imgWidth > imgHeight) {
//Rotate horizontal image to vertical and resizing is needed
rotateAndResize(this, imgWidth, imgHeight, angle, max_width, max_height, thumbNailImage);
} else {
//no need to rotate only resizing is needed
resize(this, imgWidth, imgHeight, max_width, max_height, thumbNailImage);
}
};
imageObj.onabort = function () {
alert("Image load was aborted.");
};
imageObj.onerror = function () {
alert("An error occured while loading image.");
};
}
function rotateAndResize(image, imgWidth, imgHeight, angle, max_width, max_height, thumbNailImage) {
var canvas = document.getElementById("hidden_canvas_old"),
ctx = canvas.getContext("2d");
var widthHalf = imgWidth / 2,
heightHalf = imgHeight / 2;
canvas.width = imgWidth;
canvas.height = imgWidth;
//clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
//set coordinate to rotate canvas
ctx.translate(canvas.width / 2, canvas.height / 2);
//rotate canvas with given angle
ctx.rotate(angle * Math.PI / 180);
//draw image on rotated canvas with given coordinate
ctx.drawImage(image, -widthHalf, -widthHalf);
ctx.restore();
var tempCanvas = document.getElementById("hidden_canvas_new"),
tCtx = tempCanvas.getContext("2d");
tempCanvas.width = imgHeight;
tempCanvas.height = imgWidth;
/*
* Crop rotated image from old canvas to remove white space
* So that canvas will have only image content without extra padding
*/
tCtx.drawImage(canvas, canvas.width - imgHeight, 0, imgHeight, imgWidth, 0, 0, imgHeight, imgWidth);
tCtx.restore();
//Delete unwanted canvas to reduce page size
canvas.remove();
/**
* Resizing Rotated image
*/
// calculate the width and height, constraining the proportions
if (imgWidth > imgHeight) {
if (imgWidth > max_width) {
imgHeight = Math.round(imgHeight *= max_width / imgWidth);
imgWidth = max_width;
}
} else {
if (imgWidth > max_height) {
imgWidth = Math.round(imgWidth *= max_height / imgHeight);
imgHeight = max_height;
}
}
var tempCanvasTemp = tempCanvas;
tempCanvas.remove();
var tempCanvas1 = document.createElement("canvas"),
tCtx1 = tempCanvas1.getContext("2d");
tempCanvas1.id = 'hidden_canvas_new';
tempCanvas1.style.display = 'none';
tempCanvas1.width = imgHeight;
tempCanvas1.height = imgWidth;
tCtx1.drawImage(tempCanvasTemp, 0, 0, imgHeight, imgWidth);
tCtx1.restore();
document.body.appendChild(tempCanvas1);
thumbNailImage.attr("src", tempCanvas1.toDataURL('image/jpeg'));
$('#mask').hide();
}
function resize(image, imgWidth, imgHeight, max_width, max_height, thumbNailImage) {
/**
* Resizing image
*/
// calculate the width and height, constraining the proportions
if (imgWidth > imgHeight) {
if (imgWidth > max_width) {
//height *= max_width / width;
imgHeight = Math.round(imgHeight *= max_width / imgWidth);
imgWidth = max_width;
}
} else {
if (imgWidth > max_height) {
//width *= max_height / height;
imgWidth = Math.round(imgWidth *= max_height / imgHeight);
imgHeight = max_height;
}
}
var tempCanvas = document.getElementById("hidden_canvas_new"),
tCtx = tempCanvas.getContext("2d");
tempCanvas.width = imgWidth;
tempCanvas.height = imgHeight;
tCtx.drawImage(image, 0, 0, imgWidth, imgHeight);
thumbNailImage.attr("src", tempCanvas.toDataURL('image/jpeg'));
$('#mask').hide();
}
</script>
Try using MediaDevices.getUserMedia() (documentation) for browsers which support it, you don't need to resize the photo as you can request the size of the photo.
Several examples:
http://www.html5rocks.com/en/tutorials/getusermedia/intro/
https://github.com/addyosmani/getUserMedia.js
as far as I know getUserMedia() supports the maximum resolution of the video
So if you have a 12M camera, but video is maximum 720p then you can set image quality to maximum 720p

Categories