We're building a one page website and something we have always had problems with is when the one page site has lots of images. The scroll seems very jittery especially on chrome and we want to look at a much better way of getting it done. Any suggestions of libraries of techniques to use?
Thanks in advance.
Since each image is likely getting resized with CSS, then with lots of images, the browser has to resize the image when you scroll, which adds a lot of computation.
This will add to the loading/rendering time initially, but should help with laggy scrolling.
$('.body').find('.img').each(function() {
var img = this;
setTimeout(function() { //use setTimeout so as to not totally block the thread.
renderImageAsRightSize(img);
}, 0);
});
//remove loader image.
$(".site-loader").delay(100).fadeOut("slow");
function renderImageAsRightSize (imageObject)
{
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.height = canvas.width * (imageObject.height / imageObject.width);
var oc = document.createElement('canvas'),
octx = oc.getContext('2d');
oc.width = imageObject.width;
oc.height = imageObject.height;
octx.drawImage(imageObject, 0, 0, oc.width, oc.height);
octx.drawImage(oc, 0, 0, oc.width, oc.height);
//resize final image
ctx.drawImage(oc, 0, 0, oc.width, oc.height, 0, 0, canvas.width, canvas.height);
}
Essentialliy, converting full-size images being resized with CSS, to the appropriate size (whatever the size of the resized image is on the page) using the canvas.
Related
Just as an introduction, I'm a beginner with JS so maybe the answer to the following problem is simple but I'm just not seeing it.
I have the following code:
let canvas = document.getElementById('canvas')
let ctx = canvas.getContext('2d');
canvas.width = window.innerWidth
canvas.height = window.innerHeight
let img = new Image();
img.onload = function() { ctx.drawImage(img, 0, 0); };
img.src = './tree.jpg';
Which works, so no problem here.
Then I have:
ctx.clearRect(0, 0, 300, 300);
img.src = './tree.jpg';
ctx.drawImage(img, 730, 720);
And for some reason that eludes me, the clearRect doesn't want to work in that sequence.
Any suggestions are kindly appreciated.
Thanks
clearRect() does not work as intended here since the image hasn't been drawn on the canvas yet. What has happened here is the canvas gets cleared and then the image loads.
As Mozilla says here,
If you try to call drawImage() before the image has finished loading, it won't do anything (or, in older browsers, may even throw an exception). So you need to be sure to use the load event so you don't try this before the image has loaded:
Similiar to how you're using drawImage() in the onload(), clear your Rectangle after your image has loaded and been drawn.
img.onload = function() {
ctx.drawImage(img, 0, 0);
ctx.clearRect(0, 0, 300, 300);
};
I need to reduce the height/width of an image before adding to a Canvas. We are using fabric.js.
When the image is resized using the image.set() function shown below, does it degrade the image quality?
imgAdd.onload = function () {
var img = new fabric.Image(imgAdd);
var imgSizeObj = img.getOriginalSize();
var newImgSize = calculateAspectRatioFit(imgSizeObj.width, imgSizeObj.height, canvas.width, canvas.height);
if (imgSizeObj.width > canvas.width || imgSizeObj.height > canvas.height) {
//Does img.set() preserve image resolution?
img.set({
width: newImgSize.width,
height: newImgSize.height
});
}
canvas.add(img);
}
The goal is to maintain image quality but automatically scale a large image to fit within the canvas.
Thank you,
No it doesnt.
The image is stored in a property of the object._element and does not change unless you apply some filter over it.
Even in that situation a copy is stored in object._originalElement.
Please try the following:
var scale = bef;
img.set({
scaleX: scale,
scaleY: scale
});
You can find more information on this here: https://github.com/kangax/fabric.js/issues/176
I am trying to draw a movie frame to the canvas element using ctx.DrawImage and I am getting what I think are very weird scaling / cropping behaviors.
First, here is my code:
HTML
<body>
<video autoplay width="125" height="125"></video>
<img src="">
<canvas></canvas>
</body>
Javascript:
var video = document.querySelector('video');
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var localMediaStream = null;
function snapshot() {
if (localMediaStream) {
ctx.drawImage(video, 0, 0, 125, 125);
document.querySelector('img').src = canvas.toDataURL('image/webp');
}
}
video.addEventListener('click', snapshot, false);
navigator.getUserMedia(
{
video: true
}, function(stream) {
video.src = window.URL.createObjectURL(stream);
localMediaStream = stream;
});
What this does is essentially take the still from the video and squish it so that it is about 1/3 the original width and about 70% of the height. This is frustrating.
Leaving off the 125 and just using ctx.drawImage(video, 0, 0) fills the canvas with the still, however, it is only about the top left corner (25%) of the image. This is also frustrating.
How can I make the canvas draw the exact same sized image as the video element is playing?
This is coupled with the fact the video element itself is indeed 125px x 125px but my because of the aspect ratio, there is letter boxing (white space) around the video element. Is there a way to force the video element to fill itself with the incoming video stream?
I too was facing the same problem when I tried...
var sw = video.offsetWidth, // source width
sh = video.offsetHeight, // source height
dw = canvas.width, // destination width
dh = canvas.height; // destination height
ctx.drawImage(video, 0, 0, sw, sh, 0, 0, dw, dh);
it got solved when I changed to :
ctx.drawImage(video, 0, 0, dw, dh);
I believe in your case the problem is, 125 is height and width of video element, but what you should be supplying is the canvas height and width, for example, setting the canvas width and height:
canvas.width = 125;
canvas.height = 125;
ctx.drawImage(video, 0, 0, 125, 125);
I was using a webcam (with the navigator.getUserMedia function). The webcam resolution was significantly higher and so, even when the sizes of the video element and canvas were the same, the image being captured was much larger than could fit.
The solution was to add a constrain property to make the media stream also 125px x 125px
This seems to be a very old question, but I was faced with a similar challenge a few days back.
Making use of CSS viewport units to set height & width of the canvas and also adjust size of the tags saved my day.
Viewport units allow you to size elements and font sizes as a percentage of the total width or height of the user’s screen (the viewport).
This is how i implemented in my code
The video tags
<video id="video" style="height: calc(25vh - 15px); width: calc(25vh - 15px); border: 1px solid gray;" autoplay playsinline></video>
You can read more about them here https://alligator.io/css/viewport-units/
I am working on an ebook application, I draw each page on canvas using PDF.js , the problem is , when I click on the button and turn to other page, I tried simply render on the same canvas again , but the canvas seems move to a wrong location or wrong size .
function renderPage(url) {
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
//clearCanvasGrid('canvas');
PDFJS.getDocument(url).then(function (pdf) {
// Using promise to fetch the page
pdf.getPage(1).then(function(page) {
var viewport = page.getViewport(5); //scale 5
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
page.render(renderContext).then(function() {
initialZoomPage(viewport.height,viewport.width);
});
});
});
}
So, are there any necessary step I need to do before redraw the page? Also , how can I destroy it if I would like to close the page? Thanks
Update:
function clearCanvasGrid(canvasID){
canvas = document.getElementById(canvasID); //because we are looping //each location has its own canvas ID
context = canvas.getContext('2d');
//context.beginPath();
// Store the current transformation matrix
context.save();
// Use the identity matrix while clearing the canvas
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);
// Restore the transform
context.restore(); //CLEARS THE SPECIFIC CANVAS COMPLETELY FOR NEW DRAWING
}
I found a function to clear the canvas but it has .save , .setTransform and .restore besides clearRect, are they necessary? thanks
One way is to clear out the canvas using context.clearRect(0,0, width, height) (Reference).
Alternatively, you can append a new canvas element (and possibly remove the old one, depending on whether you will want to display it again) each time you want a new page. Something like this should do it:
var oldcanv = document.getElementById('canvas');
document.removeChild(oldcanv)
var canv = document.createElement('canvas');
canv.id = 'canvas';
document.body.appendChild(canv);
Just note that if you plan to keep more than one, each one must have a unique id instead of just id="canvas" (perhaps based on the page number - something like canvas-1).
Answer to updated question:
The save, setTransform, and restore are only necessary if you are doing (or somehow allowing users to do) transformation. I don't know if the PDF.js library does any transformation behind the scenes, so it may be best to leave it there.
try using clearRect(), like:
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
In Lightning Aura components, a different approach is required:
The chart object, once created, is stored in an attribute.
<aura:attribute name="chartObject" type="Object"/>
<canvas id="chartId" aura:id="chartCanvas" </canvas>
let theChart = component.get("v.chartObject");
if (theChart != undefined) {
//alternative
//let chartStatus = Chart.getChart("chartId");
//if (chartStatus != undefined) {
//update data if necessary
//theChart.data.datasets[0].data[i] = etc
theChart.update();
}
else{
var ctx = component.find("chartCanvas").getElement();
var chart = new Chart(ctx, {
type: 'line',
data: myData,
options: {
tension: 0.25,
responsive: false,
maintainAspectRatio :false,
fill: true,
}
});
component.set("v.chartObject", chart);
}//endif chart exists
I'm loading png and jpeg images in a canvas with drawImage, but it doesn't always work, even with the same image. There's no exception, no error, it just doesn't work.
{
debug("lbase starting");
var canvas = document.getElementById("base");
debug(canvas);
ctx = canvas.getContext("2d");
debug(ctx);
ctx.clearRect(0, 0, canvas.width, canvas.height);
var baseimg = new Image();
debug(baseimg);
baseimg.src = "baseimage.png";
debug(baseimg.src);
ctx.drawImage(baseimg, 0, 0);
debug("base loading done");
}
My debug function just write a message in a div at the bottom of the page. I also tried with:
retcode ctx.drawImage(baseimg, 0, 0);
debug(retcode)
but it returns undefined.
Everything gets executed (I get all the right debug messages), but sometimes it works, sometimes it does not. It seems to fail more in ff then chromium.
Is there anyway to verify that the drawImage has worked?
Any idea what's going on?
Thanks.
You can't add an image to the canvas until it has finished loading.
Something like this should work:
var baseimg = new Image();
baseimg.src = "baseimage.png";
baseimg.onload = function() {
ctx.drawImage(baseimg, 0, 0);
};