You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, focusPosition = 0.5, focusRange = 0.1, blurAmount = 5, gradientType = "linear", direction = "vertical") {
// Normalize/clamp parameters
focusPosition = Math.max(0, Math.min(1, focusPosition));
focusRange = Math.max(0, Math.min(1, focusRange));
blurAmount = Math.max(0, blurAmount);
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
const canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// 1. Draw the blurred image as the base layer on the main canvas
// If blurAmount is 0, filter will be 'none', effectively drawing original image.
if (blurAmount > 0) {
ctx.filter = `blur(${blurAmount}px)`;
}
ctx.drawImage(originalImg, 0, 0, w, h);
if (blurAmount > 0) {
ctx.filter = 'none'; // Reset filter for any subsequent operations on ctx (though not strictly needed here)
}
// 2. Create a temporary canvas for the sharp layer and its mask
const sharpMaskedCanvas = document.createElement('canvas');
sharpMaskedCanvas.width = w;
sharpMaskedCanvas.height = h;
const smCtx = sharpMaskedCanvas.getContext('2d');
// 2a. Draw the original sharp image onto the temporary canvas
smCtx.drawImage(originalImg, 0, 0, w, h);
// 2b. Prepare for masking: subsequent drawing on smCtx will define the mask's alpha
smCtx.globalCompositeOperation = 'destination-in';
// 2c. Create and draw the gradient mask
let grad;
if (gradientType === "linear") {
const isVertical = direction === "vertical";
const dim = isVertical ? h : w; // Dimension along which the gradient runs
// Transition length from sharp to blur, related to blurAmount but at least 1px
const transitionLengthPx = Math.max(1, blurAmount * 1.5);
const focusCenterPx = dim * focusPosition;
const focusHalfRangePx = (dim * focusRange) / 2;
// Calculate gradient stop positions (normalized 0-1)
let s0 = (focusCenterPx - focusHalfRangePx - transitionLengthPx) / dim;
let s1 = (focusCenterPx - focusHalfRangePx) / dim;
let s2 = (focusCenterPx + focusHalfRangePx) / dim;
let s3 = (focusCenterPx + focusHalfRangePx + transitionLengthPx) / dim;
// Clamp all stops to [0,1] range. Canvas API handles ordered/coincident stops.
s0 = Math.max(0, Math.min(1, s0));
s1 = Math.max(0, Math.min(1, s1));
s2 = Math.max(0, Math.min(1, s2));
s3 = Math.max(0, Math.min(1, s3));
if (isVertical) { // Horizontal focus band, gradient runs vertically
grad = smCtx.createLinearGradient(0, 0, 0, h);
} else { // Vertical focus band, gradient runs horizontally
grad = smCtx.createLinearGradient(0, 0, w, 0);
}
grad.addColorStop(s0, 'rgba(0,0,0,0)'); // Transparent start of blur-in
grad.addColorStop(s1, 'rgba(0,0,0,1)'); // Opaque start of sharp area
grad.addColorStop(s2, 'rgba(0,0,0,1)'); // Opaque end of sharp area
grad.addColorStop(s3, 'rgba(0,0,0,0)'); // Transparent end of blur-out
smCtx.fillStyle = grad;
smCtx.fillRect(0, 0, w, h);
} else if (gradientType === "radial") {
const centerX = w / 2;
const centerY = h / 2;
const smallestDim = Math.min(w, h);
// focusPosition: (0-0.5 ideally) relative radius of the inner sharp circle (r0).
// focusRange: (0-1) relative width of the transition band from sharp to blur (r1-r0).
const r0_abs = smallestDim * focusPosition;
const transition_abs = smallestDim * focusRange;
const final_r0 = Math.max(0, r0_abs);
// Ensure transition is at least a few pixels or related to blurAmount
const min_transition_px = Math.max(1, blurAmount * 0.5);
const final_r1 = Math.max(final_r0 + min_transition_px, final_r0 + transition_abs);
grad = smCtx.createRadialGradient(centerX, centerY, final_r0, centerX, centerY, final_r1);
grad.addColorStop(0, 'rgba(0,0,0,1)'); // Sharp at r0 (inner radius of gradient)
grad.addColorStop(1, 'rgba(0,0,0,0)'); // Fades to transparent at r1 (outer radius of gradient)
smCtx.fillStyle = grad;
smCtx.fillRect(0, 0, w, h);
} else {
// Invalid gradientType: Make the sharp layer fully transparent.
// This ensures the output is the fully blurred image (or original if blurAmount=0).
smCtx.fillStyle = 'rgba(0,0,0,0)';
smCtx.fillRect(0, 0, w, h);
}
// 2d. Reset composite operation for smCtx (good practice though not strictly needed here)
smCtx.globalCompositeOperation = 'source-over';
// 3. Draw the masked sharp image onto the main canvas (which has the blurred base)
ctx.drawImage(sharpMaskedCanvas, 0, 0);
return canvas;
}
Apply Changes