Crazy Letter
written by reyrove
Setting the Basics
const ratio = 1 / 1;
const prefix = 'Crazy letter';
Here, we define the ratio for the canvas as 1/1
, ensuring it is a perfect square. The prefix is set as 'Crazy letter'
, and it will be used for naming any files when the artwork is downloaded.
Defining Global Variables
const features = {};
let resizeTmr = null;
let thumbnailTaken = false;
const urlSearchParams = new URLSearchParams(window.location.search);
const urlParams = Object.fromEntries(urlSearchParams.entries());
let forceDownloaded = false;
const animated = false;
let nextFrame = null;
- features : This object stores the various properties for the artwork, like background color and line color.
- resizeTmr : A timer to handle canvas resizing events.
- thumbnailTaken : A flag to make sure the thumbnail preview is only taken once.
- urlSearchParams & urlParams : These extract any URL parameters, which might include instructions like forcing the download of the artwork.
- forceDownloaded : A flag to prevent multiple downloads.
-
animated: Set to
false
because this artwork is static. - nextFrame : Stores the ID of the next frame in case of animations.
setup Function: Picking Random Colors
const setup = () => {
const backgroundColours = ['#FFC0CB', '#FFA07A', '#FFFFE0', '#E6E6FA', '#ADFF2F', '#66CDAA', '#E0FFFF', '#FFE4C4', '#F5F5DC', '#DCDCDC', '#FFF0F5', '#FFF8DC'];
const backgroundNames = ['Pink', 'LightSalmon', 'LightYellow', 'Lavender', 'GreenYellow', 'MediumAquamarine', 'LightCyan', 'Bisque', 'Beige', 'Gainsboro', 'LavenderBlush', 'Cornsilk'];
const backgroundIndex = Math.floor($fx.rand() * backgroundColours.length);
const foregroundColours = ['#f5a04e', '#931a1e', '#fad2db', '#f2e73d', '#14b9dc', '#d65a9c', '#f2f8ef', '#395370'];
const foregroundIndex = Math.floor($fx.rand() * foregroundColours.length);
features.backgroundColour = backgroundColours[backgroundIndex];
features.lineColours = foregroundColours[foregroundIndex];
const readableFeaturesObj = {};
readableFeaturesObj['Background Color'] = backgroundNames[backgroundIndex];
readableFeaturesObj['Lines Color'] = foregroundColours[foregroundIndex];
$fx.features(readableFeaturesObj);
console.table(readableFeaturesObj);
};
This function sets up the random selection of colors for the background and lines. The background colors are stored in the backgroundColour
s array, and the line colors in foregroundColours
. We randomly pick a color using $fx.rand()
and store the choices in the features
object.
The selected colors and their names are printed to the console for easy reference.
Drawing the Line Pattern
The function drawLine
generates a quirky line pattern across the canvas. Each line is drawn at a randomized Y-axis position, with varying colors and shadows.
function drawLine(ctx, lineY, margin) {
const canvas = document.getElementById('target');
const w = canvas.width;
const range = map(lineY, margin * 2, w - margin * 2, 0, w / ($fx.rand() * 20 + 30));
let prevX = margin * ($fx.rand() * 10 + 2);
let prevY = lineY;
const lineSpacing = w / ($fx.rand() * 100 + 20);
for (let x = prevX + lineSpacing * $fx.rand() * 10; x <= w - margin * $fx.rand() * 10; x += lineSpacing) {
const y = lineY + $fx.rand() * 2 * range - range;
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(x, y);
ctx.stroke();
prevX = x;
prevY = y;
}
}
-
drawLine(ctx, lineY, margin)
: This function draws a zigzagging line across the canvas using random values for the line’s Y-axis position and spacing between points. -
map(value, start1, stop1, start2, stop2)
: Maps a value from one range to another (for example, mapping the Y-coordinate from the canvas width to a smaller range). -
random(min, max)
: Generates a random number between the givenmin
andmax
values.
Drawing the Entire Canvas
Next, we draw the entire canvas by invoking the drawCanvas
function, which first fills the background and then applies the line pattern.
const drawCanvas = async () => {
window.cancelAnimationFrame(nextFrame);
const canvas = document.getElementById('target');
const ctx = canvas.getContext('2d');
const w = canvas.width;
const h = canvas.height;
const margin = w / ($fx.rand() * 20 + 50);
ctx.fillStyle = features.backgroundColour;
ctx.fillRect(0, 0, w, h);
ctx.strokeStyle = features.lineColours;
ctx.lineWidth = w / ($fx.rand() * 300 + 200);
ctx.shadowColor = features.lineColours;
ctx.shadowBlur = w / ($fx.rand() * 300 + 200);
ctx.beginPath();
ctx.rect(margin, margin, w - margin * 2, h - margin * 2);
ctx.stroke();
for (let y = margin * 2; y < h - margin * 2; y += w / ($fx.rand() * 50 + 50)) {
drawLine(ctx, y, margin);
}
if (!thumbnailTaken) {
$fx.preview();
thumbnailTaken = true;
}
if ('forceDownload' in urlParams && forceDownloaded === false) {
forceDownloaded = true;
await autoDownloadCanvas();
window.parent.postMessage('forceDownloaded', '*');
}
if (animated) {
nextFrame = window.requestAnimationFrame(drawCanvas);
}
};
- Filling the Background : The canvas is filled with the background color selected earlier.
- Stroke Properties : We set the stroke color and line width for the drawn lines. Shadows are added for a cool 3D effect.
- Drawing the Frame : A rectangle is drawn inside the canvas to create a margin.
-
Drawing Lines
: Lines are drawn across the canvas using the
drawLine
function in a loop, creating the artistic effect.
Handling Resizing and Canvas Layout
const layoutCanvas = async (windowObj = window, urlParamsObj = urlParams) => {
windowObj.cancelAnimationFrame(nextFrame);
const { innerWidth: wWidth, innerHeight: wHeight, devicePixelRatio = 1 } = windowObj;
let dpr = devicePixelRatio;
let cWidth = wWidth;
let cHeight = cWidth / ratio;
if (cHeight > wHeight) {
cHeight = wHeight;
cWidth = wHeight * ratio;
}
const canvases = document.getElementsByTagName('canvas');
Array.from(canvases).forEach(canvas => canvas.remove());
let targetHeight = cHeight;
let targetWidth = targetHeight * ratio;
if ('forceWidth' in urlParams) {
targetWidth = parseInt(urlParams.forceWidth);
targetHeight = Math.floor(targetWidth / ratio);
dpr = 1;
}
targetWidth *= dpr;
targetHeight *= dpr;
const canvas = document.createElement('canvas');
canvas.id = 'target';
canvas.width = targetWidth;
canvas.height = targetHeight;
document.body.appendChild(canvas);
canvas.style.position = 'absolute';
canvas.style.width = `${cWidth}px`;
canvas.style.height = `${cHeight}px`;
canvas.style.left = `${(wWidth - cWidth) / 2}px`;
canvas.style.top = `${(wHeight - cHeight) / 2}px`;
drawCanvas();
};
This function ensures that the canvas adjusts its size responsively. It calculates the optimal size for the canvas based on the window size while maintaining the square aspect ratio.
Initiating the Canvas Drawing
document.addEventListener('DOMContentLoaded', init);
The init
function is called once the page is fully loaded. It sets up event listeners for resizing the window and pressing keys (like 's' for saving the canvas).
To view the full code and customize it, click here to start your journey with the "Crazy Letter" canvas art.
Crafted with sparkle, style, and passion by Frostbond Coders 💖✨