You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, disks = "3", borderColor = "#333333", borderWidth = "4") {
// Dimensions from original image
let w = originalImg.width;
let h = originalImg.height;
// Scale down proportionally if image is too large, to keep it fit as an icon
const MAX_SIZE = 800;
if (w > MAX_SIZE || h > MAX_SIZE) {
let scale = Math.min(MAX_SIZE / w, MAX_SIZE / h);
w *= scale;
h *= scale;
}
// Parse formatting parameters
let numDisks = parseInt(disks);
if (isNaN(numDisks) || numDisks < 1) numDisks = 3;
numDisks = Math.max(1, Math.min(20, numDisks)); // Constrain to safe visually appealing boundaries
const bw = parseFloat(borderWidth) >= 0 ? parseFloat(borderWidth) : 4;
const pad = bw + 2; // Allow enough layout padding for stroke overflows
// Create the destination canvas
const canvas = document.createElement('canvas');
canvas.width = w + pad * 2;
canvas.height = h + pad * 2;
const ctx = canvas.getContext('2d');
// Determine the Y-radius (perspective depth) of the ellipses that form the 3D cylinder
// Cap at 15% of width/height for aesthetic uniformity
let ry = w * 0.15;
ry = Math.min(ry, h * 0.15);
const cx = canvas.width / 2;
const topY = pad + ry;
const bottomY = canvas.height - pad - ry;
const cylX = pad;
const cylW = w;
const rightX = cylX + cylW;
// Helper function to trace the external silhouette of the database cylinder
function traceCylinder() {
ctx.beginPath();
// Left side
ctx.moveTo(cylX, topY);
ctx.lineTo(cylX, bottomY);
// Bottom rounded curve
ctx.ellipse(cx, bottomY, cylW / 2, ry, 0, Math.PI, 0, true);
// Right side
ctx.lineTo(rightX, topY);
// Top curved back edge
ctx.ellipse(cx, topY, cylW / 2, ry, 0, 0, -Math.PI, true);
ctx.closePath();
}
// Step 1: Clip and embed the original image into the cylinder silhouette
ctx.save();
traceCylinder();
ctx.clip();
ctx.drawImage(originalImg, pad, pad, w, h);
// Step 2: Overlay dynamic 3D Shading to establish lighting/volume
const gradient = ctx.createLinearGradient(cylX, 0, rightX, 0);
gradient.addColorStop(0, "rgba(0, 0, 0, 0.4)");
gradient.addColorStop(0.15, "rgba(255, 255, 255, 0.25)");
gradient.addColorStop(0.5, "rgba(0, 0, 0, 0)");
gradient.addColorStop(0.85, "rgba(0, 0, 0, 0.05)");
gradient.addColorStop(1, "rgba(0, 0, 0, 0.45)");
ctx.fillStyle = gradient;
ctx.fillRect(cylX, pad, cylW, h);
// Add specular brighten on the topmost flat lid surface indicator
ctx.beginPath();
ctx.ellipse(cx, topY, cylW / 2, ry, 0, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(255, 255, 255, 0.15)";
ctx.fill();
ctx.restore();
// Step 3: Imprint strokes, container outlines, and disk separator grooves
if (bw > 0) {
ctx.lineWidth = bw;
ctx.strokeStyle = borderColor;
ctx.lineCap = "round";
ctx.lineJoin = "round";
// Outer body container border
traceCylinder();
ctx.stroke();
// Full border line mapping the topmost disk
ctx.beginPath();
ctx.ellipse(cx, topY, cylW / 2, ry, 0, 0, 2 * Math.PI);
ctx.stroke();
// Inner database disk partitions (only curving across the front)
if (numDisks > 1) {
const spacing = (bottomY - topY) / numDisks;
for (let i = 1; i < numDisks; i++) {
let y = topY + i * spacing;
ctx.beginPath();
// Sweep the bottom half-ellipse arcs
ctx.ellipse(cx, y, cylW / 2, ry, 0, 0, Math.PI, false);
ctx.stroke();
}
}
}
return canvas;
}
Apply Changes