new funtions and fixes

This commit is contained in:
Mateusz Gruszczyński
2026-07-24 12:40:23 +02:00
parent 0ed852a249
commit 14f15e5d47
10 changed files with 301 additions and 71 deletions
+16 -6
View File
@@ -11,10 +11,10 @@ export async function prepareImageFile(file){
const dialog=document.createElement("dialog");
dialog.className="image-editor-dialog";
dialog.innerHTML=`<form method="dialog" class="image-editor-panel">
<div class="image-editor-head"><div><h2>Adjust image</h2><p>Drag to crop, use zoom and choose output size.</p></div><button class="icon-button" value="cancel" aria-label="Close">×</button></div>
<div class="image-editor-head"><div><h2>Adjust image</h2><p>Keep the whole image or choose a crop, then select output size.</p></div><button class="icon-button" value="cancel" aria-label="Close">×</button></div>
<div class="image-crop-stage"><canvas></canvas></div>
<div class="image-editor-controls">
<label>Crop<select data-aspect><option value="free">Free</option><option value="1">Square</option><option value="1.333333">4:3</option><option value="1.777778">16:9</option></select></label>
<label>Crop<select data-aspect><option value="original" selected>Whole image</option><option value="free">Free</option><option value="1">Square</option><option value="1.333333">4:3</option><option value="1.777778">16:9</option></select></label>
<label>Zoom<input data-zoom type="range" min="1" max="3" value="1" step="0.01"></label>
<label>Max size<select data-size><option value="320">320 px</option><option value="480">480 px</option><option value="640">640 px</option><option value="800">800 px</option><option value="1000">1000 px</option><option value="1200">1200 px</option><option value="1600" selected>1600 px</option><option value="2000">2000 px</option><option value="0">Original</option></select></label>
</div>
@@ -27,7 +27,7 @@ export async function prepareImageFile(file){
function cropBox(){
const rect=stage.getBoundingClientRect();
let width=Math.max(280,rect.width),height=Math.min(520,Math.max(260,rect.height));
const aspect=aspectSelect.value==="free"?width/height:Number(aspectSelect.value);
const aspect=aspectSelect.value==="original"?image.naturalWidth/image.naturalHeight:aspectSelect.value==="free"?width/height:Number(aspectSelect.value);
if(width/height>aspect)width=height*aspect;else height=width/aspect;
return {width:Math.round(width),height:Math.round(height)};
}
@@ -35,7 +35,9 @@ export async function prepareImageFile(file){
const box=cropBox(),dpr=Math.min(devicePixelRatio||1,2);
canvas.width=Math.round(box.width*dpr);canvas.height=Math.round(box.height*dpr);canvas.style.width=`${box.width}px`;canvas.style.height=`${box.height}px`;
ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,box.width,box.height);
const base=Math.max(box.width/image.naturalWidth,box.height/image.naturalHeight),scale=base*Number(zoomInput.value);
const wholeImage=aspectSelect.value==="original";
zoomInput.disabled=wholeImage;
const base=wholeImage?Math.min(box.width/image.naturalWidth,box.height/image.naturalHeight):Math.max(box.width/image.naturalWidth,box.height/image.naturalHeight),scale=base*(wholeImage?1:Number(zoomInput.value));
const drawW=image.naturalWidth*scale,drawH=image.naturalHeight*scale;
const maxX=Math.max(0,(drawW-box.width)/2),maxY=Math.max(0,(drawH-box.height)/2);
offsetX=clamp(offsetX,-maxX,maxX);offsetY=clamp(offsetY,-maxY,maxY);
@@ -50,8 +52,16 @@ export async function prepareImageFile(file){
const result=new Promise(resolve=>{
dialog.addEventListener("close",()=>{URL.revokeObjectURL(url);dialog.remove();resolve(accepted);},{once:true});
dialog.querySelector("[data-apply]").addEventListener("click",async()=>{
const box=cropBox(),maxSize=Number(sizeSelect.value),ratio=Math.min(1,maxSize?maxSize/Math.max(box.width,box.height):1),out=document.createElement("canvas");
out.width=Math.max(1,Math.round(box.width*ratio));out.height=Math.max(1,Math.round(box.height*ratio));out.getContext("2d").drawImage(canvas,0,0,out.width,out.height);
const box=cropBox(),maxSize=Number(sizeSelect.value),out=document.createElement("canvas");
if(aspectSelect.value==="original"){
const ratio=Math.min(1,maxSize?maxSize/Math.max(image.naturalWidth,image.naturalHeight):1);
out.width=Math.max(1,Math.round(image.naturalWidth*ratio));out.height=Math.max(1,Math.round(image.naturalHeight*ratio));
out.getContext("2d").drawImage(image,0,0,out.width,out.height);
}else{
const ratio=Math.min(1,maxSize?maxSize/Math.max(box.width,box.height):1);
out.width=Math.max(1,Math.round(box.width*ratio));out.height=Math.max(1,Math.round(box.height*ratio));
out.getContext("2d").drawImage(canvas,0,0,out.width,out.height);
}
const mime=file.type==="image/png"?"image/png":"image/jpeg";
const blob=await new Promise(r=>out.toBlob(r,mime,mime==="image/jpeg"?.88:undefined));
const ext=mime==="image/png"?"png":"jpg";