You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, amplitude = 10, period = 50, direction = 'vertical') {
let w = originalImg.naturalWidth || originalImg.width;
let h = originalImg.naturalHeight || originalImg.height;
// Ensure w and h are valid numbers, defaulting to a small canvas size if not.
// This helps prevent errors if the image object is faulty or not fully loaded.
w = Number(w) || 100; // Default width if w is NaN, undefined, or 0
h = Number(h) || 100; // Default height if h is NaN, undefined, or 0
const outputCanvas = document.createElement('canvas');
outputCanvas.width = w;
outputCanvas.height = h;
const outputCtx = outputCanvas.getContext('2d');
// Create a temporary canvas to get image data
const tempCanvas = document.createElement('canvas');
tempCanvas.width = w;
tempCanvas.height = h;
// Use { willReadFrequently: true } for potential performance optimization when using getImageData repeatedly.
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
try {
tempCtx.drawImage(originalImg, 0, 0, w, h);
} catch (e) {
console.error("Error drawing original image to temporary canvas:", e);
// Display an error message on the canvas
outputCtx.fillStyle = 'rgba(200,200,200,1)'; // Light gray background
outputCtx.fillRect(0,0,w,h);
outputCtx.fillStyle = 'red';
outputCtx.font = '14px Arial';
outputCtx.textAlign = 'center';
outputCtx.textBaseline = 'middle';
outputCtx.fillText('Error: Could not load image.', w/2, h/2);
return outputCanvas; // Return canvas with error message
}
let originalImgData;
try {
originalImgData = tempCtx.getImageData(0, 0, w, h);
} catch (e) {
console.error("Error getting image data (possibly cross-origin):", e);
// If getImageData fails (e.g. cross-origin), draw the original image as a fallback
// and display a warning message on top.
outputCtx.drawImage(originalImg, 0, 0, w, h);
outputCtx.fillStyle = 'rgba(255, 0, 0, 0.6)'; // Semi-transparent red overlay
outputCtx.fillRect(0, 0, w, h);
outputCtx.fillStyle = 'white';
outputCtx.font = 'bold 16px Arial';
outputCtx.textAlign = 'center';
outputCtx.textBaseline = 'middle';
const messages = ['Kinetic effect cannot be applied.', '(Cross-origin restrictions or image error)'];
const lineHeight = 20;
const startY = h/2 - (messages.length - 1) * lineHeight / 2;
for(let i = 0; i < messages.length; i++) {
outputCtx.fillText(messages[i], w/2, startY + i * lineHeight);
}
return outputCanvas;
}
const outputImgData = outputCtx.createImageData(w, h);
const origPixels = originalImgData.data;
const outPixels = outputImgData.data;
const numAmplitude = Number(amplitude);
const numPeriod = Number(period);
if (w === 0 || h === 0) {
// If image has zero dimension, there's nothing to process.
// The output canvas will be blank and correctly sized.
console.warn("Image has zero width or height. Returning blank canvas.");
return outputCanvas;
}
if (direction === 'vertical') {
for (let x = 0; x < w; x++) { // Iterate through destination columns
const angle = (numPeriod === 0) ? 0 : (x * 2 * Math.PI / numPeriod);
const yShift = (numPeriod === 0) ? 0 : Math.sin(angle) * numAmplitude;
for (let y = 0; y < h; y++) { // Iterate through destination rows
// For canvas pixel (x,y), find source pixel (x, srcY)
const srcY = Math.round(y - yShift);
// Clamp srcY to be within image boundaries
let clampedSrcY = srcY;
if (clampedSrcY < 0) clampedSrcY = 0;
if (clampedSrcY >= h) clampedSrcY = h - 1;
const destIdx = (y * w + x) * 4;
const srcIdx = (clampedSrcY * w + x) * 4;
outPixels[destIdx] = origPixels[srcIdx]; // R
outPixels[destIdx + 1] = origPixels[srcIdx + 1]; // G
outPixels[destIdx + 2] = origPixels[srcIdx + 2]; // B
outPixels[destIdx + 3] = origPixels[srcIdx + 3]; // A
}
}
} else if (direction === 'horizontal') {
for (let y = 0; y < h; y++) { // Iterate through destination rows
const angle = (numPeriod === 0) ? 0 : (y * 2 * Math.PI / numPeriod);
const xShift = (numPeriod === 0) ? 0 : Math.sin(angle) * numAmplitude;
for (let x = 0; x < w; x++) { // Iterate through destination columns
// For canvas pixel (x,y), find source pixel (srcX, y)
const srcX = Math.round(x - xShift);
// Clamp srcX to be within image boundaries
let clampedSrcX = srcX;
if (clampedSrcX < 0) clampedSrcX = 0;
if (clampedSrcX >= w) clampedSrcX = w - 1;
const destIdx = (y * w + x) * 4;
const srcIdx = (y * w + clampedSrcX) * 4;
outPixels[destIdx] = origPixels[srcIdx]; // R
outPixels[destIdx + 1] = origPixels[srcIdx + 1]; // G
outPixels[destIdx + 2] = origPixels[srcIdx + 2]; // B
outPixels[destIdx + 3] = origPixels[srcIdx + 3]; // A
}
}
} else {
// Invalid direction, just copy image to output canvas
console.warn(`Invalid direction: "${direction}". Copying original image.`);
outputCtx.drawImage(originalImg, 0, 0, w, h);
return outputCanvas;
}
outputCtx.putImageData(outputImgData, 0, 0);
return outputCanvas;
}
Apply Changes