A member from stackoferflow helped fix my script and he obtained a good result, but I still need some help.
I have a DIV that contains some other divs and in the end, an image.
This image is bigger in size, but has a smaller width/height set directly from CSS (3x times smaller, the number is irrelevant).
The image has CSS transformations applied to it (rotate/scale, translate).
The script creates a canvas, of the same size as the div, and draws the image in the canvas, CROPPING the outside part of it (filling it with white). Finally it draws the image in it’s normal position (with no rotation applied to it), but with the outside part, in white (so cropped).
It works perfect. The problem is that I would like to scale this canvas 3 times (or 5 times) to make it better quality, since my image is bigger (natural size), but it’s displayed smaller due to the CSS exact width/height set.
I tried rescaling (resizing) the canvas but it simply produces bad results, as it’s rescales a small image (losing a lot of quality).
I would like to modify the script, so it applied scaling on all steps based on a scale factor (let’s say SCALE_FACTOR=3).
In my example, the div size is 400×400 pixels, so the final canvas would be 1200×1200 pixels and the image would be drawn 3 times bigger, but in EXACTLY the same position (so the crop result is identical, just in higher quality).
I tried applying .scale on the matrix and inverse matrix, but it does not give the result I am looking for. Of course I increased containerWidth with *SCALE_FACTOR etc…
I would like to obtain this (but high quality draw, not rescale like I did now) .
Here is the code I am using:
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.object-render--canvas {
width: 400px;
/* You can change this to any size */
height: 400px;
/* You can change this to any size */
border: 2px solid #000;
position: relative;
overflow: hidden;
margin-bottom: 20px;
}
#dd6f9f50-780e-11ef-b763-11ebd40cd3a6 {
width: 326px; /* Scaled-down width */
height: 290.3px; /* Scaled-down height */
transform: translate(230px, -130px) rotate(145deg) scale(1);
transform-origin: center center;
position: absolute;
top: 0;
left: 0;
}
#buttons {
margin-bottom: 20px;
}
#tempCanvases {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
#tempCanvases canvas {
border: 1px solid #ccc;
}
</style>
</head>
<body>
<h1>Dynamic Image Transformation and Cropping</h1>
<div class="object-render--canvas">
<img id="dd6f9f50-780e-11ef-b763-11ebd40cd3a6" src="https://i.sstatic.net/oJs20ysA.png" alt="Source Image">
</div>
<div id="buttons">
<button id="processButton">Process</button>
<button id="downloadButton" disabled>Download</button>
</div>
<h2>Temporary Canvases (For Debugging)</h2>
<script type='text/javascript'>
// Wait for the DOM to load
document.addEventListener('DOMContentLoaded', () => {
const processButton = document.getElementById('processButton');
const downloadButton = document.getElementById('downloadButton');
let finalCanvas = null;
let finalImageCanvas=null;
processButton.addEventListener('click', () => {
// Clear previous temporary canvases
downloadButton.disabled = true;
processCanvas();
// Enable the download button
downloadButton.disabled = false;
});
downloadButton.addEventListener('click', () => {
if (!finalCanvas) return;
// Convert the final canvas to a data URL
const dataURL = finalCanvas.toDataURL('image/png');
// Create a temporary link to trigger download
const link = document.createElement('a');
link.href = dataURL;
link.download = 'processed_image.png';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
/**
* Utility function to append a canvas with a label for debugging
* @param {HTMLCanvasElement} canvas
* @param {string} label
*/
function processCanvas(){
const sourceImage = document.getElementById('dd6f9f50-780e-11ef-b763-11ebd40cd3a6');
// Step 1: Get container and image dimensions
const container = document.getElementsByClassName("object-render--canvas")[0];
const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight;
const imageRenderedWidth = sourceImage.width;
const imageRenderedHeight = sourceImage.height;
// Step 2: Get computed styles of the image
const style = window.getComputedStyle(sourceImage);
const transform = style.transform;
// Calculate the center of the image
const centerX = imageRenderedWidth / 2;
const centerY = imageRenderedHeight / 2;
// If no transform is applied, set it to identity matrix
const matrix = transform === 'none' ?
new DOMMatrix() :
new DOMMatrix(transform)
// Apply the transform-origin
.preMultiplySelf({ e: centerX, f: centerY })
.multiply({ e: -centerX, f: -centerY })
//this is the canvas where we revert transformations and crop image
finalCanvas = document.createElement('canvas');
finalCanvas.width = containerWidth;
finalCanvas.height = containerHeight;
finalCanvas.style.border="1px solid red";
const ctx1 = finalCanvas.getContext('2d');
ctx1.setTransform(matrix);
// Draw the image transformed
ctx1.drawImage(sourceImage, 0, 0, imageRenderedWidth, imageRenderedHeight);
//above code draws the image, identical as in the div, after this line, we reset transformations and crop everything that is outside the canvas with white, and display the image in normal (non rotated, not css scaled) position.
// Draw again, using the inverse transform
ctx1.setTransform(matrix.inverse());
// Replace the previous content by the new content
ctx1.globalCompositeOperation = "copy";
ctx1.drawImage(ctx1.canvas, 0, 0);
// Fill with white below the current drawing
ctx1.fillStyle = '#FFFFFF';
ctx1.globalCompositeOperation = "destination-over"; // Draw below
ctx1.resetTransform(); // No transform
ctx1.fillRect(0, 0, finalCanvas.width, finalCanvas.height);
// Append the canvas for debugging
appendCanvas(finalCanvas, 'Result');
}
function appendCanvas(canvas, label) {
const wrapper = document.createElement('div');
const caption = document.createElement('p');
caption.textContent = label;
wrapper.appendChild(caption);
wrapper.appendChild(canvas);
document.body.appendChild(wrapper);
}
});
</script>
</body>
</html>