Issue
I have the following HTML structure:
<div class="content">
<div class="wrapper">
<canvas id="myCanvas"></canvas>
</div>
<div class="images">
<img class="image" src="Test/0001.png" alt="Option">
<img class="image" src="Test/0002.png" alt="Option">
<img class="image" src="Test/0012.png" alt="Option">
<img class="image" src="Test/0014.png" alt="Option">
</div>
</div>
And the following JS function I use to resize a canvas using Fabric JS functions:
window.onresize = () => {
const wrapper = document.querySelector('.wrapper');
const ratio = canvas.getWidth() / canvas.getHeight();
const wrapperWidth = wrapper.clientWidth;
const scale = wrapperWidth / canvas.getWidth();
const zoom = canvas.getZoom() * scale;
canvas.setDimensions({width: wrapperWidth, height: wrapperWidth / ratio});
canvas.setViewportTransform([zoom, 0, 0, zoom, 0, 0]);
}
This HTML + JavaScript works and resizes my canvas properly, but the issue comes when I want to add this CSS so the content
and images
divs are centered on the screen:
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 5vh;
}
I've seen the issue is with the line align-items: center
, when I delete it, the canvas resizes accordingly to the JavaScript listener I declared but when it's present, it breaks the functionality and the canvas doesn't resize anymore because the wrapper
div doesn't change it's width with the window resize anymore.
Why's that? How can I align the items to the center without breaking the canvas resizing function?
Edit
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fabric.js Image Editor</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.5.0/fabric.min.js"></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<div class="wrapper">
<canvas id="myCanvas"></canvas>
</div>
<div class="images">
<img class="image" src="Test/0001.png" alt="Option">
<img class="image" src="Test/0002.png" alt="Option">
<img class="image" src="Test/0012.png" alt="Option">
<img class="image" src="Test/0014.png" alt="Option">
</div>
</div>
<script src="index.js"></script>
</body>
</html>
styles.css:
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 5vh;
}
.images {
display: flex;
width: 100%;
padding: 20px 0;
height: 1000px;
}
.image {
border: 1px solid black;
width: 25%;
height: 25%;
margin: 0 10px;
}
.image:hover {
cursor: pointer;
}
index.js:
var canvas = new fabric.Canvas('myCanvas');
fabric.Image.fromURL("Test/IMG_0008.jpg", function(img) {
var scaleFactor = Math.min(window.innerWidth / img.width, window.innerHeight / img.height);
scaleFactor *= 0.8;
canvas.setWidth(img.width * scaleFactor);
canvas.setHeight(img.height * scaleFactor);
img.scaleToWidth(canvas.width);
img.scaleToHeight(canvas.height);
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
});
document.querySelectorAll(".image").forEach(element => {
element.addEventListener("click", () => {
fabric.Image.fromURL(element.src, function(img) {
if (img.width > canvas.width || img.height > canvas.height) {
img.scaleToWidth(canvas.width / 2);
img.scaleToHeight(canvas.height / 2);
}
img.setControlsVisibility({ mt: false, mb: false, ml: false, mr: false });
canvas.add(img);
canvas.centerObject(img);
canvas.setActiveObject(img);
});
});
});
const resizeCanvas = () => {
const wrapper = document.querySelector('.wrapper');
const ratio = canvas.getWidth() / canvas.getHeight();
const wrapperWidth = wrapper.clientWidth;
const scale = wrapperWidth / canvas.getWidth();
const zoom = canvas.getZoom() * scale;
canvas.setDimensions({width: wrapperWidth, height: wrapperWidth / ratio});
canvas.setViewportTransform([zoom, 0, 0, zoom, 0, 0]);
}
window.onresize = resizeCanvas;
window.onload = resizeCanvas;
Solution
If I understood correctly and successfully reproduced your example, then the issue you're experiencing is when you shrink the page and try to zoom it back in. When zooming back in, you cannot achieve the original size.
If you observe in your code, the width of the wrapper always remains fixed to the size of the image. This is problematic because when I shrink the page, you scale down the image, and the wrapper size adjusts to this small size. If I enlarge the page, the small image remains small, and so does the wrapper.
Solution
When you load the main image into the canvas
, the canvas
captures the current page size and sets a specific size based on the image. You can save this as the fixed width of the wrapper
. In addition, you'll need to set the max-width
of the wrapper
to 100vw
in CSS to prevent overflow, and the browser will automatically shrink the wrapper
width, which your resize function would have already tracked.
.wrapper {
max-width: 100vw; /* HERE - Maximum 100vw (maximum screen width), so the photo won't overflow */
}
fabric.Image.fromURL("https://picsum.photos/1000/1000", function(img) {
// ...
const wrapper = document.querySelector('.wrapper');
wrapper.style.width = `${canvas.width}px`; // HERE - Setting a fixed width based on the original size (img.width * scaleFactor)
});
I suggested just one example for fixing the wrapper's width. You can fix the original size however you want. In my example, I simply wrote in the CSS, width: 500px;
which I later made a bit more flexible in JavaScript (what you can see this in the line wrapper.style.width
).
As @digitalniweb also suggested, using width: 100%
could be a solution, although I didn't choose this because I don't like distorting images (and in this case, there would be a realistic chance of that happening... **instead, I aligned with the canvas dimensions... (img.width * scaleFactor
).
Example
var canvas = new fabric.Canvas('myCanvas');
fabric.Image.fromURL("https://picsum.photos/500/500", function(img) {
var scaleFactor = Math.min(window.innerWidth / img.width, window.innerHeight / img.height);
scaleFactor *= 0.8;
canvas.setWidth(img.width * scaleFactor);
canvas.setHeight(img.height * scaleFactor);
img.scaleToWidth(canvas.width);
img.scaleToHeight(canvas.height);
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
const wrapper = document.querySelector('.wrapper');
wrapper.style.width = `${canvas.width}px`; // HERE - img.width * scaleFactor
});
document.querySelectorAll(".image").forEach(element => {
element.addEventListener("click", () => {
fabric.Image.fromURL(element.src, function(img) {
if (img.width > canvas.width || img.height > canvas.height) {
img.scaleToWidth(canvas.width / 2);
img.scaleToHeight(canvas.height / 2);
}
img.setControlsVisibility({ mt: false, mb: false, ml: false, mr: false });
canvas.add(img);
canvas.centerObject(img);
canvas.setActiveObject(img);
});
});
});
const resizeCanvas = () => {
const wrapper = document.querySelector('.wrapper');
const wrapperWidth = wrapper.clientWidth;
const canvasWidth = canvas.getWidth();
const canvasHeight = canvas.getHeight();
const canvasZoom = canvas.getZoom();
const ratio = canvasWidth / canvasHeight;
const scale = wrapperWidth / canvasWidth;
const zoom = canvasZoom * scale;
const newCanvasHeight = wrapperWidth / ratio;
canvas.setDimensions({ width: wrapperWidth, height: newCanvasHeight });
canvas.setViewportTransform([zoom, 0, 0, zoom, 0, 0]);
};
window.addEventListener('resize', () => {
resizeCanvas();
});
window.onload = () => {
resizeCanvas();
};
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 5vh;
}
.wrapper {
max-width: 100vw; /* HERE */
}
.images {
display: flex;
width: 100%;
padding: 20px 0;
height: 1000px;
}
.image {
border: 1px solid black;
width: 25%;
height: 25%;
margin: 0 10px;
}
.image:hover {
cursor: pointer;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<div class="container">
<div class="wrapper">
<canvas id="myCanvas"></canvas>
</div>
<div class="images">
<img class="image" src="https://picsum.photos/888/888" alt="Option">
<img class="image" src="https://picsum.photos/555/555" alt="Option">
<img class="image" src="https://picsum.photos/222/222" alt="Option">
<img class="image" src="https://picsum.photos/111/111" alt="Option">
</div>
</div>
Answered By - rozsazoltan
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.