mirror of
https://github.com/SamEyeBam/animate.git
synced 2026-02-04 09:20:25 +00:00
V1.1
Giant refactor. added layers. ui overhaul. added save/load and we now got presets
This commit is contained in:
99
docs/js/shapes/CircleExpand.js
Normal file
99
docs/js/shapes/CircleExpand.js
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* CircleExpand - Expanding concentric circles/hearts animation
|
||||
*/
|
||||
class CircleExpand extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 1, max: 70, defaultValue: 21, property: 'nCircles' },
|
||||
{ type: 'range', min: 50, max: 150, defaultValue: 150, property: 'gap' },
|
||||
{ type: 'range', min: 0, max: 1, defaultValue: 1, property: 'linear' },
|
||||
{ type: 'range', min: 0, max: 1, defaultValue: 1, property: 'heart' },
|
||||
{ type: 'color', defaultValue: '#fc03cf', property: 'colour1' },
|
||||
{ type: 'color', defaultValue: '#00fffb', property: 'colour2' },
|
||||
];
|
||||
|
||||
constructor(nCircles, gap, linear, heart, colour1, colour2) {
|
||||
super();
|
||||
this.nCircles = nCircles;
|
||||
this.gap = gap;
|
||||
this.linear = linear;
|
||||
this.heart = heart;
|
||||
this.colour1 = colour1;
|
||||
this.colour2 = colour2;
|
||||
}
|
||||
|
||||
lerpColor(a, b, amount) {
|
||||
const ah = +a.replace('#', '0x');
|
||||
const ar = ah >> 16, ag = ah >> 8 & 0xff, ab = ah & 0xff;
|
||||
const bh = +b.replace('#', '0x');
|
||||
const br = bh >> 16, bg = bh >> 8 & 0xff, bb = bh & 0xff;
|
||||
const rr = ar + amount * (br - ar);
|
||||
const rg = ag + amount * (bg - ag);
|
||||
const rb = ab + amount * (bb - ab);
|
||||
return '#' + ((1 << 24) + (rr << 16) + (rg << 8) + rb | 0).toString(16).slice(1);
|
||||
}
|
||||
|
||||
arraySort(x, y) {
|
||||
if (x.r > y.r) return 1;
|
||||
if (x.r < y.r) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
drawHeart(w, colour) {
|
||||
ctx.strokeStyle = "black";
|
||||
ctx.fillStyle = colour;
|
||||
ctx.lineWidth = 1;
|
||||
const x = centerX - w / 2;
|
||||
const y = centerY - w / 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y + w / 4);
|
||||
ctx.quadraticCurveTo(x, y, x + w / 4, y);
|
||||
ctx.quadraticCurveTo(x + w / 2, y, x + w / 2, y + w / 5);
|
||||
ctx.quadraticCurveTo(x + w / 2, y, x + w * 3 / 4, y);
|
||||
ctx.quadraticCurveTo(x + w, y, x + w, y + w / 4);
|
||||
ctx.quadraticCurveTo(x + w, y + w / 2, x + w * 3 / 4, y + w * 3 / 4);
|
||||
ctx.lineTo(x + w / 2, y + w);
|
||||
ctx.lineTo(x + w / 4, y + w * 3 / 4);
|
||||
ctx.quadraticCurveTo(x, y + w / 2, x, y + w / 4);
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
const rotation = elapsed * 0.9;
|
||||
ctx.strokeWeight = 1;
|
||||
ctx.lineWidth = 1;
|
||||
const arrOfWidths = [];
|
||||
let intRot;
|
||||
|
||||
if (this.linear) {
|
||||
intRot = Math.floor(rotation * 30) / 100;
|
||||
} else {
|
||||
intRot = Math.sin(rad(Math.floor(rotation * 30) / 4)) + rotation / 4;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.nCircles; i++) {
|
||||
const width = this.gap * ((intRot + i) % this.nCircles);
|
||||
const colour = (Math.sin(rad(i * (360 / this.nCircles) - 90)) + 1) / 2;
|
||||
arrOfWidths.push({ r: width, c: colour });
|
||||
}
|
||||
|
||||
const newArr = arrOfWidths.sort(this.arraySort);
|
||||
|
||||
for (let i = this.nCircles - 1; i >= 0; i--) {
|
||||
const newColour = this.lerpColor(this.colour1, this.colour2, newArr[i].c);
|
||||
|
||||
if (this.heart) {
|
||||
this.drawHeart(newArr[i].r, newColour);
|
||||
} else {
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, newArr[i].r, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = newColour;
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = "black";
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('CircleExpand', CircleExpand);
|
||||
78
docs/js/shapes/Countdown.js
Normal file
78
docs/js/shapes/Countdown.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Countdown - Countdown timer display with progress bar
|
||||
*/
|
||||
class Countdown extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 8000, max: 2000000, defaultValue: 2000000, property: 'milestone' },
|
||||
];
|
||||
|
||||
constructor(milestone) {
|
||||
super();
|
||||
this.milestone = milestone;
|
||||
}
|
||||
|
||||
secondsUntilDate(targetDate) {
|
||||
const now = new Date();
|
||||
const nzTimeString = targetDate.replace('T', 'T').concat('+12:00');
|
||||
const target = new Date(nzTimeString);
|
||||
const difference = target.getTime() - now.getTime();
|
||||
return Math.round(difference / 1000);
|
||||
}
|
||||
|
||||
drawProgressBar(progress) {
|
||||
const colourBackground = "#0c2f69";
|
||||
const colourProgress = "#4287f5";
|
||||
const barWidth = ctx.canvas.width;
|
||||
const barHeight = 60;
|
||||
const barX = 0;
|
||||
const barY = ctx.canvas.height - barHeight;
|
||||
|
||||
ctx.fillStyle = colourBackground;
|
||||
ctx.beginPath();
|
||||
ctx.rect(barX, barY, barWidth, barHeight);
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = colourProgress;
|
||||
ctx.beginPath();
|
||||
ctx.rect(barX, barY, (barWidth / 100) * progress, barHeight);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
let fontSize = 48;
|
||||
if (ctx.canvas.width < 1000) {
|
||||
fontSize = 24;
|
||||
}
|
||||
|
||||
ctx.font = fontSize + "px serif";
|
||||
ctx.fillStyle = "white";
|
||||
ctx.textAlign = "center";
|
||||
|
||||
const futureDate = '2025-06-01T04:30:00';
|
||||
const seconds = this.secondsUntilDate(futureDate);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const percentRounded = (((elapsed / 1000) / seconds) * 100).toFixed(8);
|
||||
|
||||
ctx.fillText(seconds + " Seconds", centerX, centerY - 200);
|
||||
ctx.fillText(minutes + " Minutes", centerX, centerY - 100);
|
||||
ctx.fillText(hours + " Hours", centerX, centerY);
|
||||
ctx.fillText(percentRounded + "% Closer", centerX, centerY + 300);
|
||||
|
||||
const milestoneSeconds = this.milestone;
|
||||
const target = new Date(futureDate + '+12:00');
|
||||
const milestoneDate = new Date(target.getTime() - milestoneSeconds * 1000).toLocaleString();
|
||||
ctx.fillText(milestoneDate, centerX, centerY + 100);
|
||||
ctx.fillText("^-- " + milestoneSeconds + " milestone", centerX, centerY + 200);
|
||||
|
||||
const canvasWidth = ctx.canvas.width;
|
||||
const secondsPerPixel = (seconds / canvasWidth);
|
||||
const secondsUntilFirstPixel = secondsPerPixel - (elapsed / 10);
|
||||
|
||||
ctx.fillText("Time until first pixel: " + Math.round(secondsUntilFirstPixel) + " seconds", centerX, centerY + 350);
|
||||
|
||||
this.drawProgressBar(percentRounded);
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('Countdown', Countdown);
|
||||
196
docs/js/shapes/EyePrototype.js
Normal file
196
docs/js/shapes/EyePrototype.js
Normal file
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* EyePrototype - Animated eye with blinking, spiral, and hypnotic effects
|
||||
*/
|
||||
class EyePrototype extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: -400, max: 400, defaultValue: 0, property: 'x' },
|
||||
{ type: 'range', min: -400, max: 400, defaultValue: 0, property: 'y' },
|
||||
{ type: 'range', min: -180, max: 180, defaultValue: 0, property: 'rotate' },
|
||||
{ type: 'range', min: 0, max: 1, defaultValue: 1, property: 'flip' },
|
||||
{ type: 'range', min: 1, max: 800, defaultValue: 400, property: 'width' },
|
||||
{ type: 'range', min: 1, max: 100, defaultValue: 5, property: 'blink_speed' },
|
||||
{ type: 'range', min: 0, max: 1, defaultValue: 0, property: 'draw_spiral' },
|
||||
{ type: 'range', min: 0, max: 1, defaultValue: 1, property: 'spiral_full' },
|
||||
{ type: 'range', min: 0, max: 1, defaultValue: 0, property: 'draw_pupil' },
|
||||
{ type: 'range', min: 0, max: 1, defaultValue: 0, property: 'draw_expand' },
|
||||
{ type: 'range', min: 0, max: 1, defaultValue: 1, property: 'draw_hypno' },
|
||||
{ type: 'range', min: 1, max: 10, defaultValue: 1, property: 'line_width' },
|
||||
{ type: 'color', defaultValue: '#00fffb', property: 'colourPupil' },
|
||||
{ type: 'color', defaultValue: '#ff0000', property: 'colourSpiral' },
|
||||
{ type: 'color', defaultValue: '#00fffb', property: 'colourExpand' },
|
||||
{ type: 'range', min: 0, max: 1, defaultValue: 1, property: 'draw_eyelid' },
|
||||
];
|
||||
|
||||
constructor(x, y, rotate, flip, width, blink_speed, draw_spiral, spiral_full, draw_pupil, draw_expand, draw_hypno, line_width, colourPupil, colourSpiral, colourExpand) {
|
||||
super();
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.rotate = rotate;
|
||||
this.flip = flip;
|
||||
this.width = width;
|
||||
this.blink_speed = blink_speed;
|
||||
this.line_width = line_width;
|
||||
this.step = 0;
|
||||
this.opening = true;
|
||||
this.counter = 0;
|
||||
this.cooldown = 0;
|
||||
this.draw_spiral = draw_spiral;
|
||||
this.spiral_full = spiral_full;
|
||||
this.draw_pupil = draw_pupil;
|
||||
this.draw_expand = draw_expand;
|
||||
this.draw_hypno = draw_hypno;
|
||||
this.colourPupil = colourPupil;
|
||||
this.colourSpiral = colourSpiral;
|
||||
this.colourExpand = colourExpand;
|
||||
this.centerPulse = new CircleExpand(10, 30, 1, 0, "#2D81FC", "#FC0362");
|
||||
}
|
||||
|
||||
drawEyelid(rotation) {
|
||||
ctx.strokeStyle = "orange";
|
||||
const relCenterX = centerX + this.x;
|
||||
const relCenterY = centerY + this.y;
|
||||
rotation *= (this.speedMultiplier / 100);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
let newPoint = 0;
|
||||
let newPoint1 = 0;
|
||||
const addedRotate = this.flip ? 90 : 0;
|
||||
|
||||
newPoint = rotatePoint(-this.width / 2, 0, this.rotate + addedRotate);
|
||||
ctx.moveTo(relCenterX + newPoint[0], relCenterY + newPoint[1]);
|
||||
newPoint = rotatePoint(0, -rotation / 400 * this.width, this.rotate + addedRotate);
|
||||
newPoint1 = rotatePoint(this.width / 2, 0, this.rotate + addedRotate);
|
||||
ctx.quadraticCurveTo(relCenterX + newPoint[0], relCenterY + newPoint[1], relCenterX + newPoint1[0], relCenterY + newPoint1[1]);
|
||||
|
||||
newPoint = rotatePoint(-this.width / 2, 0, this.rotate + addedRotate);
|
||||
ctx.moveTo(relCenterX + newPoint[0], relCenterY + newPoint[1]);
|
||||
newPoint = rotatePoint(0, +rotation / 400 * this.width, this.rotate + addedRotate);
|
||||
newPoint1 = rotatePoint(this.width / 2, 0, this.rotate + addedRotate);
|
||||
ctx.quadraticCurveTo(relCenterX + newPoint[0], relCenterY + newPoint[1], relCenterX + newPoint1[0], relCenterY + newPoint1[1]);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
eyelidCut(rotation) {
|
||||
const relCenterX = centerX + this.x;
|
||||
const relCenterY = centerY + this.y;
|
||||
let newPoint = 0;
|
||||
let newPoint1 = 0;
|
||||
const addedRotate = this.flip ? 90 : 0;
|
||||
|
||||
const squarePath = new Path2D();
|
||||
newPoint = rotatePoint(-this.width / 2, 0, this.rotate + addedRotate);
|
||||
squarePath.moveTo(relCenterX + newPoint[0], relCenterY + newPoint[1]);
|
||||
newPoint = rotatePoint(0, -rotation / 400 * this.width, this.rotate + addedRotate);
|
||||
newPoint1 = rotatePoint(this.width / 2, 0, this.rotate + addedRotate);
|
||||
squarePath.quadraticCurveTo(relCenterX + newPoint[0], relCenterY + newPoint[1], relCenterX + newPoint1[0], relCenterY + newPoint1[1]);
|
||||
|
||||
newPoint = rotatePoint(-this.width / 2, 0, this.rotate + addedRotate);
|
||||
squarePath.moveTo(relCenterX + newPoint[0], relCenterY + newPoint[1]);
|
||||
newPoint = rotatePoint(0, +rotation / 400 * this.width, this.rotate + addedRotate);
|
||||
newPoint1 = rotatePoint(this.width / 2, 0, this.rotate + addedRotate);
|
||||
squarePath.quadraticCurveTo(relCenterX + newPoint[0], relCenterY + newPoint[1], relCenterX + newPoint1[0], relCenterY + newPoint1[1]);
|
||||
|
||||
ctx.clip(squarePath);
|
||||
}
|
||||
|
||||
drawGrowEye(step) {
|
||||
ctx.strokeStyle = this.colourExpand;
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 5;
|
||||
ctx.arc(centerX + this.x, centerY + this.y, step, 0, 2 * Math.PI);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
drawCircle(step) {
|
||||
ctx.strokeStyle = this.colourPupil;
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 5;
|
||||
ctx.arc(centerX + this.x, centerY + this.y, step, 0, 2 * Math.PI);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
drawSpiral(step) {
|
||||
ctx.strokeStyle = this.colourSpiral;
|
||||
const a = 1;
|
||||
const b = 5;
|
||||
ctx.moveTo(centerX, centerY);
|
||||
ctx.beginPath();
|
||||
const max = this.spiral_full ? this.width : this.width / 2;
|
||||
|
||||
for (let i = 0; i < max; i++) {
|
||||
const angle = 0.1 * i;
|
||||
const x = centerX + (a + b * angle) * Math.cos(angle + step / 2);
|
||||
const y = centerY + (a + b * angle) * Math.sin(angle + step / 2);
|
||||
ctx.lineTo(x + this.x, y + this.y);
|
||||
}
|
||||
ctx.lineWidth = 3;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
stepFunc() {
|
||||
if (this.cooldown !== 0) {
|
||||
this.cooldown--;
|
||||
} else {
|
||||
if (this.opening === true) {
|
||||
if (this.step >= 200) {
|
||||
this.cooldown = 200;
|
||||
this.opening = false;
|
||||
this.step -= this.blink_speed;
|
||||
} else {
|
||||
this.step += this.blink_speed;
|
||||
}
|
||||
} else {
|
||||
if (this.step <= 0) {
|
||||
this.opening = true;
|
||||
this.step += this.blink_speed;
|
||||
} else {
|
||||
this.step -= this.blink_speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
const speedMult = 50;
|
||||
const waitTime = this.blink_speed;
|
||||
const cap = 200;
|
||||
const d = waitTime * speedMult * 10;
|
||||
const a = cap * 2 + d;
|
||||
const outputRotation = Math.min(Math.abs((Math.floor(elapsed * speedMult) % a) - a / 2 - d / 2), cap);
|
||||
|
||||
ctx.fillStyle = "black";
|
||||
ctx.save();
|
||||
this.drawEyelid(outputRotation);
|
||||
this.eyelidCut(outputRotation);
|
||||
|
||||
if (Math.floor(this.counter % (this.width / 4)) === 0) {
|
||||
this.counter = 0;
|
||||
}
|
||||
|
||||
ctx.fillStyle = "black";
|
||||
ctx.fillRect(this.x - this.width / 2 + centerX, 0, this.width, ctx.canvas.height);
|
||||
|
||||
if (this.draw_expand) {
|
||||
this.drawGrowEye(this.width / 4 + this.counter);
|
||||
}
|
||||
|
||||
if (this.draw_hypno) {
|
||||
this.centerPulse.draw(elapsed);
|
||||
}
|
||||
|
||||
if (this.draw_spiral) {
|
||||
this.drawSpiral(elapsed);
|
||||
}
|
||||
|
||||
if (this.draw_pupil) {
|
||||
this.drawCircle(this.width / 4);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
|
||||
this.stepFunc();
|
||||
this.counter++;
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('EyePrototype', EyePrototype);
|
||||
59
docs/js/shapes/FloralAccident.js
Normal file
59
docs/js/shapes/FloralAccident.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* FloralAccident - Accidental floral spiral pattern variant
|
||||
*/
|
||||
class FloralAccident extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 1, max: 50, defaultValue: 20, property: 'sides' },
|
||||
{ type: 'range', min: 1, max: 600, defaultValue: 240, property: 'width' },
|
||||
{ type: 'color', defaultValue: '#4287f5', property: 'colour' },
|
||||
];
|
||||
|
||||
constructor(sides, width, colour) {
|
||||
super();
|
||||
this.sides = sides;
|
||||
this.width = width;
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
this.updateFilters(elapsed);
|
||||
const rotation = elapsed * (this.speedMultiplier / 100);
|
||||
const rot = Math.round((this.sides - 2) * 180 / this.sides * 2);
|
||||
const piv = 360 / this.sides;
|
||||
let stt = 0.5 * Math.PI - rad(rot);
|
||||
let end = 0;
|
||||
const n = this.width / ((this.width / 10) * (this.width / 10));
|
||||
|
||||
for (let i = 1; i < this.sides + 1; i++) {
|
||||
end = stt + rad(rot);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(
|
||||
centerX + Math.cos(rad(90 + piv * i + rotation)) * this.width,
|
||||
centerY + Math.sin(rad(90 + piv * i + rotation)) * this.width,
|
||||
this.width,
|
||||
stt - (stt - end + rad(rotation)) / 2,
|
||||
end + rad(n),
|
||||
0
|
||||
);
|
||||
ctx.strokeStyle = this.colour;
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(
|
||||
centerX + Math.cos(rad(90 + piv * i - rotation)) * this.width,
|
||||
centerY + Math.sin(rad(90 + piv * i - rotation)) * this.width,
|
||||
this.width,
|
||||
stt,
|
||||
end - (end - stt - rad(rotation)) / 2 + rad(n),
|
||||
0
|
||||
);
|
||||
ctx.strokeStyle = this.colour;
|
||||
ctx.stroke();
|
||||
|
||||
stt = end + -(rad(rot - piv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('FloralAccident', FloralAccident);
|
||||
40
docs/js/shapes/FloralPhyllo.js
Normal file
40
docs/js/shapes/FloralPhyllo.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* FloralPhyllo - Phyllotaxis-based floral pattern with eyelid shapes
|
||||
*/
|
||||
class FloralPhyllo extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 1, max: 600, defaultValue: 300, property: 'width' },
|
||||
{ type: 'range', min: 1, max: 300, defaultValue: 150, property: 'depth' },
|
||||
{ type: 'range', min: 0, max: 3141, defaultValue: 0, property: 'start' },
|
||||
{ type: 'color', defaultValue: '#4287f5', property: 'colour1' },
|
||||
{ type: 'color', defaultValue: '#FC0362', property: 'colour2' },
|
||||
];
|
||||
|
||||
constructor(width, depth, start, colour1, colour2) {
|
||||
super();
|
||||
this.width = width;
|
||||
this.depth = depth;
|
||||
this.start = start;
|
||||
this.colour1 = colour1;
|
||||
this.colour2 = colour2;
|
||||
this.speedMultiplier = 500;
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
this.updateFilters(elapsed);
|
||||
const rotation = elapsed * (this.speedMultiplier / 500) + this.start;
|
||||
const c = 1;
|
||||
|
||||
for (let n = this.depth; n > 0; n -= 1) {
|
||||
const ncolour = LerpHex(this.colour1, this.colour2, n / this.depth);
|
||||
const a = n * rotation / 1000;
|
||||
const r = c * Math.sqrt(n);
|
||||
const x = r * Math.cos(a) + centerX;
|
||||
const y = r * Math.sin(a) + centerY;
|
||||
|
||||
drawEyelid(n * 2.4 + 40, x, y, ncolour);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('FloralPhyllo', FloralPhyllo);
|
||||
37
docs/js/shapes/FloralPhyllo_Accident.js
Normal file
37
docs/js/shapes/FloralPhyllo_Accident.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* FloralPhyllo_Accident - Phyllotaxis pattern with accidental eyelid variation
|
||||
*/
|
||||
class FloralPhyllo_Accident extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 1, max: 50, defaultValue: 20, property: 'sides' },
|
||||
{ type: 'range', min: 1, max: 600, defaultValue: 240, property: 'width' },
|
||||
{ type: 'color', defaultValue: '#2D81FC', property: 'colour1' },
|
||||
{ type: 'color', defaultValue: '#FC0362', property: 'colour2' },
|
||||
];
|
||||
|
||||
constructor(sides, width, colour1, colour2) {
|
||||
super();
|
||||
this.sides = sides;
|
||||
this.width = width;
|
||||
this.colour1 = colour1;
|
||||
this.colour2 = colour2;
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
this.updateFilters(elapsed);
|
||||
const rotation = elapsed * (this.speedMultiplier / 100);
|
||||
const c = 24;
|
||||
|
||||
for (let n = 0; n < 300; n += 1) {
|
||||
const ncolour = LerpHex(this.colour1, this.colour2, Math.cos(rad(n / 2)));
|
||||
const a = n * (rotation / 1000 + 100);
|
||||
const r = c * Math.sqrt(n);
|
||||
const x = r * Math.cos(a) + centerX;
|
||||
const y = r * Math.sin(a) + centerY;
|
||||
|
||||
drawEyelidAccident(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('FloralPhyllo_Accident', FloralPhyllo_Accident);
|
||||
42
docs/js/shapes/MaryFace.js
Normal file
42
docs/js/shapes/MaryFace.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* MaryFace - Face overlay with animated eyes
|
||||
*/
|
||||
class MaryFace extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: -400, max: 400, defaultValue: -110, property: 'x1' },
|
||||
{ type: 'range', min: -400, max: 400, defaultValue: -140, property: 'y1' },
|
||||
{ type: 'range', min: -180, max: 180, defaultValue: 18, property: 'rotate1' },
|
||||
{ type: 'range', min: 0, max: 400, defaultValue: 160, property: 'width1' },
|
||||
{ type: 'range', min: -400, max: 400, defaultValue: 195, property: 'x2' },
|
||||
{ type: 'range', min: -400, max: 400, defaultValue: -30, property: 'y2' },
|
||||
{ type: 'range', min: -180, max: 180, defaultValue: 18, property: 'rotate2' },
|
||||
{ type: 'range', min: 0, max: 400, defaultValue: 160, property: 'width2' },
|
||||
];
|
||||
|
||||
constructor(x1, y1, rotate1, width1, x2, y2, rotate2, width2) {
|
||||
super();
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
this.rotate1 = rotate1;
|
||||
this.width1 = width1;
|
||||
this.x2 = x2;
|
||||
this.y2 = y2;
|
||||
this.rotate2 = rotate2;
|
||||
this.width2 = width2;
|
||||
this.eye1 = new EyePrototype(x1, y1, rotate1, 0, width1, 10, 1, 1, 0, 0, 0, 1, "#00fffb", "#00fffb", "#00fffb");
|
||||
this.eye2 = new EyePrototype(x2, y2, rotate2, 0, width2, 10, 1, 1, 0, 0, 0, 1, "#00fffb", "#00fffb", "#00fffb");
|
||||
this.eye3 = new EyePrototype(110, -280, rotate2 + 2, 1, width2, 10, 1, 1, 0, 0, 0, 1, "#00fffb", "#00fffb", "#00fffb");
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
const img = new Image();
|
||||
img.src = "maryFace.png";
|
||||
|
||||
ctx.drawImage(img, centerX - img.width / 2, centerY - img.height / 2);
|
||||
this.eye1.draw(elapsed);
|
||||
this.eye2.draw(elapsed);
|
||||
this.eye3.draw(elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('MaryFace', MaryFace);
|
||||
52
docs/js/shapes/NewWave.js
Normal file
52
docs/js/shapes/NewWave.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* NewWave - Sine wave pattern radiating from center
|
||||
*/
|
||||
class NewWave extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 300, max: 1600, defaultValue: 342, property: 'width' },
|
||||
{ type: 'range', min: 2, max: 40, defaultValue: 4, property: 'sides' },
|
||||
{ type: 'range', min: 1, max: 100, defaultValue: 1, property: 'step' },
|
||||
{ type: 'range', min: 1, max: 10, defaultValue: 4, property: 'lineWidth' },
|
||||
{ type: 'range', min: 100, max: 1000, defaultValue: 100, property: 'limiter' },
|
||||
];
|
||||
|
||||
constructor(width, sides, step, lineWidth, limiter) {
|
||||
super();
|
||||
this.width = width;
|
||||
this.sides = sides;
|
||||
this.step = step;
|
||||
this.lineWidth = lineWidth;
|
||||
this.limiter = limiter;
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
this.updateFilters(elapsed);
|
||||
const rotation = elapsed * this.speedMultiplier / 400;
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
|
||||
for (let j = 0; j < this.sides; j++) {
|
||||
const radRotation = rad(360 / this.sides * j);
|
||||
const inverter = 1 - (j % 2) * 2;
|
||||
let lastX = centerX;
|
||||
let lastY = centerY;
|
||||
|
||||
for (let i = 0; i < this.width; i += this.step) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(lastX, lastY);
|
||||
ctx.strokeStyle = colourToText(lerpRGB([255, 51, 170], [51, 170, 255], i / this.width));
|
||||
|
||||
const x = i;
|
||||
const y = (Math.sin(-i * inverter / 30 + rotation * inverter) * i / (this.limiter / 100));
|
||||
const xRotated = x * Math.cos(radRotation) - y * Math.sin(radRotation);
|
||||
const yRotated = x * Math.sin(radRotation) + y * Math.cos(radRotation);
|
||||
|
||||
lastX = centerX + xRotated;
|
||||
lastY = centerY + yRotated;
|
||||
ctx.lineTo(centerX + xRotated, centerY + yRotated);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('NewWave', NewWave);
|
||||
53
docs/js/shapes/Nodal_expanding.js
Normal file
53
docs/js/shapes/Nodal_expanding.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Nodal_expanding - Expanding nodal pattern with color gradient
|
||||
*/
|
||||
class Nodal_expanding extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 1, max: 100, defaultValue: 5, property: 'expand' },
|
||||
{ type: 'range', min: 1, max: 1000, defaultValue: 150, property: 'points' },
|
||||
{ type: 'range', min: 1, max: 360, defaultValue: 0, property: 'start' },
|
||||
{ type: 'range', min: 1, max: 10, defaultValue: 6, property: 'line_width' },
|
||||
{ type: 'color', defaultValue: '#2D81FC', property: 'colour1' },
|
||||
{ type: 'color', defaultValue: '#FC0362', property: 'colour2' },
|
||||
{ type: 'range', min: 0, max: 10, defaultValue: 5, property: 'colour_change' },
|
||||
];
|
||||
|
||||
constructor(expand, points, start, line_width, colour1, colour2, colour_change) {
|
||||
super();
|
||||
this.expand = expand;
|
||||
this.points = points;
|
||||
this.start = start;
|
||||
this.line_width = line_width;
|
||||
this.colour1 = colour1;
|
||||
this.colour2 = colour2;
|
||||
this.colour_change = colour_change;
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
this.updateFilters(elapsed);
|
||||
const rotation = elapsed * (this.speedMultiplier / 1000);
|
||||
const angle = (360 / 3000 * rotation) + this.start;
|
||||
let length = this.expand;
|
||||
|
||||
for (let z = 1; z <= this.points; z++) {
|
||||
ctx.beginPath();
|
||||
const ncolour = LerpHex(this.colour1, this.colour2, z / this.points);
|
||||
|
||||
ctx.moveTo(
|
||||
centerX + (Math.cos(rad(angle * (z - 1) + 0)) * (length - this.expand)),
|
||||
centerY + (Math.sin(rad(angle * (z - 1) + 0)) * (length - this.expand))
|
||||
);
|
||||
ctx.lineTo(
|
||||
centerX + (Math.cos(rad(angle * z + 0)) * length),
|
||||
centerY + (Math.sin(rad(angle * z + 0)) * length)
|
||||
);
|
||||
length += this.expand;
|
||||
ctx.lineWidth = this.line_width;
|
||||
ctx.strokeStyle = ncolour;
|
||||
ctx.lineCap = "round";
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('Nodal_expanding', Nodal_expanding);
|
||||
95
docs/js/shapes/Phyllotaxis.js
Normal file
95
docs/js/shapes/Phyllotaxis.js
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Phyllotaxis - Classic phyllotaxis pattern with multiple wave modes
|
||||
*/
|
||||
class Phyllotaxis extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 1, max: 40, defaultValue: 24, property: 'width' },
|
||||
{ type: 'range', min: 1, max: 40, defaultValue: 10, property: 'size' },
|
||||
{ type: 'range', min: 1, max: 40, defaultValue: 4, property: 'sizeMin' },
|
||||
{ type: 'range', min: 0, max: 3141, defaultValue: 0, property: 'start' },
|
||||
{ type: 'range', min: 1, max: 10000, defaultValue: 300, property: 'nMax' },
|
||||
{ type: 'range', min: 0, max: 2, defaultValue: 0, property: 'wave' },
|
||||
{ type: 'range', min: 1, max: 12, defaultValue: 2, property: 'spiralProngs' },
|
||||
{ type: 'color', defaultValue: '#2D81FC', property: 'colour1' },
|
||||
{ type: 'color', defaultValue: '#FC0362', property: 'colour2' },
|
||||
];
|
||||
|
||||
constructor(width, size, sizeMin, start, nMax, wave, spiralProngs, colour1, colour2) {
|
||||
super();
|
||||
this.width = width;
|
||||
this.size = size;
|
||||
this.sizeMin = sizeMin;
|
||||
this.start = start;
|
||||
this.nMax = nMax;
|
||||
this.wave = wave;
|
||||
this.spiralProngs = spiralProngs;
|
||||
this.colour1 = colour1;
|
||||
this.colour2 = colour2;
|
||||
}
|
||||
|
||||
drawWave(angle) {
|
||||
angle /= 1000;
|
||||
const startColor = [45, 129, 252];
|
||||
const endColor = [252, 3, 98];
|
||||
const distanceMultiplier = 3;
|
||||
const maxIterations = this.nMax;
|
||||
|
||||
for (let n = 0; n < maxIterations; n++) {
|
||||
ctx.beginPath();
|
||||
const nColor = lerpRGB(startColor, endColor, Math.cos(rad(n / 2)));
|
||||
const nAngle = n * angle + Math.sin(rad(n * 1 + angle * 40000)) / 2;
|
||||
const radius = distanceMultiplier * n;
|
||||
const xCoord = radius * Math.cos(nAngle) + centerX;
|
||||
const yCoord = radius * Math.sin(nAngle) + centerY;
|
||||
ctx.arc(xCoord, yCoord, this.size, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = colourToText(nColor);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
drawSpiral(angle) {
|
||||
angle /= 5000;
|
||||
const startColor = [45, 129, 252];
|
||||
const endColor = [252, 3, 98];
|
||||
const distanceMultiplier = 2;
|
||||
const maxIterations = 1000;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX, centerY);
|
||||
for (let n = 0; n < maxIterations; n++) {
|
||||
const nAngle = n * angle + Math.sin(angle * n * this.spiralProngs);
|
||||
const radius = distanceMultiplier * n;
|
||||
const xCoord = radius * Math.cos(nAngle) + centerX;
|
||||
const yCoord = radius * Math.sin(nAngle) + centerY;
|
||||
ctx.lineTo(xCoord, yCoord);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
this.updateFilters(elapsed);
|
||||
const rotation = elapsed * (this.speedMultiplier / 300) + this.start;
|
||||
const sizeMultiplier = this.nMax / this.size + (5 - 3);
|
||||
|
||||
if (this.wave === 1) {
|
||||
this.drawWave(rotation);
|
||||
} else if (this.wave === 2) {
|
||||
this.drawSpiral(rotation);
|
||||
} else {
|
||||
for (let n = 0; n < this.nMax; n += 1) {
|
||||
const ncolour = LerpHex(this.colour1, this.colour2, n / this.nMax);
|
||||
const a = n * (rotation / 1000);
|
||||
const r = this.width * Math.sqrt(n);
|
||||
const x = r * Math.cos(a) + centerX;
|
||||
const y = r * Math.sin(a) + centerY;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, (n / sizeMultiplier) + this.sizeMin, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = ncolour;
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('Phyllotaxis', Phyllotaxis);
|
||||
50
docs/js/shapes/PolyTwistColourWidth.js
Normal file
50
docs/js/shapes/PolyTwistColourWidth.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* PolyTwistColourWidth - Twisted polygon with color gradient
|
||||
*/
|
||||
class PolyTwistColourWidth extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 3, max: 10, defaultValue: 5, property: 'sides' },
|
||||
{ type: 'range', min: 400, max: 2000, defaultValue: 400, property: 'width' },
|
||||
{ type: 'range', min: 2, max: 5, defaultValue: 5, property: 'line_width' },
|
||||
{ type: 'range', min: 1, max: 100, defaultValue: 50, property: 'depth' },
|
||||
{ type: 'range', min: -180, max: 180, defaultValue: -90, property: 'rotation' },
|
||||
{ type: 'color', defaultValue: '#4287f5', property: 'colour1' },
|
||||
{ type: 'color', defaultValue: '#42f57b', property: 'colour2' },
|
||||
];
|
||||
|
||||
constructor(sides, width, line_width, depth, rotation, colour1, colour2) {
|
||||
super();
|
||||
this.sides = sides;
|
||||
this.width = width;
|
||||
this.line_width = line_width;
|
||||
this.depth = depth;
|
||||
this.rotation = rotation;
|
||||
this.colour1 = colour1;
|
||||
this.colour2 = colour2;
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
this.updateFilters(elapsed);
|
||||
const rotation = elapsed * (this.speedMultiplier / 100);
|
||||
|
||||
let out_angle = 0;
|
||||
const innerAngle = 180 - ((this.sides - 2) * 180) / this.sides;
|
||||
const scopeAngle = rotation - (innerAngle * Math.floor(rotation / innerAngle));
|
||||
|
||||
if (scopeAngle < innerAngle / 2) {
|
||||
out_angle = innerAngle / (2 * Math.cos((2 * Math.PI * scopeAngle) / (3 * innerAngle))) - innerAngle / 2;
|
||||
} else {
|
||||
out_angle = -innerAngle / (2 * Math.cos(((2 * Math.PI) / 3) - ((2 * Math.PI * scopeAngle) / (3 * innerAngle)))) + (innerAngle * 3) / 2;
|
||||
}
|
||||
const minWidth = Math.sin(rad(innerAngle / 2)) * (0.5 / Math.tan(rad(innerAngle / 2))) * 2;
|
||||
const widthMultiplier = minWidth / Math.sin(Math.PI / 180 * (90 + innerAngle / 2 - out_angle + innerAngle * Math.floor(out_angle / innerAngle)));
|
||||
|
||||
for (let i = 0; i < this.depth; i++) {
|
||||
const fraction = i / this.depth;
|
||||
const ncolour = LerpHex(this.colour1, this.colour2, fraction);
|
||||
DrawPolygon(this.sides, this.width * widthMultiplier ** i, out_angle * i + this.rotation, ncolour, this.line_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('PolyTwistColourWidth', PolyTwistColourWidth);
|
||||
289
docs/js/shapes/RaysInShape.js
Normal file
289
docs/js/shapes/RaysInShape.js
Normal file
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* RaysInShape - Rays bouncing within a box with center-returning trails
|
||||
*/
|
||||
class RaysInShape extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 50, max: 1000, defaultValue: 500, property: 'rays', callback: (instance, newValue) => instance.setRays(newValue) },
|
||||
{ type: 'range', min: 1, max: 30, defaultValue: 2, property: 'speed' },
|
||||
{ type: 'checkbox', defaultValue: true, property: 'doesWave' },
|
||||
{ type: 'range', min: 1, max: 200, defaultValue: 100, property: 'speedVertRate' },
|
||||
{ type: 'range', min: 1, max: 200, defaultValue: 100, property: 'speedHorrRate' },
|
||||
{ type: 'range', min: 1, max: 200, defaultValue: 100, property: 'speedVert' },
|
||||
{ type: 'range', min: 1, max: 200, defaultValue: 100, property: 'speedHorr' },
|
||||
{ type: 'range', min: 10, max: 2000, defaultValue: 800, property: 'boxSize' },
|
||||
{ type: 'range', min: 1, max: 80, defaultValue: 5, property: 'trailLength' },
|
||||
{ type: 'range', min: 1, max: 500, defaultValue: 5, property: 'lineWidth' },
|
||||
{ type: 'checkbox', defaultValue: false, property: 'fade' },
|
||||
{ type: 'color', defaultValue: '#43dbad', property: 'colourFree' },
|
||||
{ type: 'color', defaultValue: '#f05c79', property: 'colourContained' },
|
||||
{ type: 'header', text: '--CollisionBox---' },
|
||||
{ type: 'checkbox', defaultValue: false, property: 'boxVisible' },
|
||||
];
|
||||
|
||||
constructor(rays, speed, doesWave, speedVertRate, speedHorrRate, speedVert, speedHorr, boxSize, trailLength = 50, lineWidth, fade, colourFree, colourContained, boxVisible) {
|
||||
super();
|
||||
this.rays = rays;
|
||||
this.speed = speed;
|
||||
this.speedVert = speedVert;
|
||||
this.speedHorr = speedHorr;
|
||||
this.boxSize = boxSize;
|
||||
this.trailLength = trailLength;
|
||||
this.rayObjects = [];
|
||||
this.centerRays = [];
|
||||
this.lineWidth = lineWidth;
|
||||
this.boxVisible = boxVisible;
|
||||
this.doesWave = doesWave;
|
||||
this.colourFree = colourFree;
|
||||
this.colourContained = colourContained;
|
||||
this.speedHorrRate = speedHorrRate;
|
||||
this.speedVertRate = speedVertRate;
|
||||
this.fade = fade;
|
||||
}
|
||||
|
||||
initializeControls(controlManager) {
|
||||
super.initializeControls(controlManager);
|
||||
this.prepareRayObjects();
|
||||
}
|
||||
|
||||
prepareRayObjects() {
|
||||
this.rayObjects = [];
|
||||
for (let i = 0; i < this.rays; i++) {
|
||||
const angle = (360 / this.rays) * i;
|
||||
this.rayObjects.push({
|
||||
angle: angle,
|
||||
lastX: centerX,
|
||||
lastY: centerY,
|
||||
positions: [{ x: centerX, y: centerY, angle: angle }]
|
||||
});
|
||||
}
|
||||
this.centerRays = [];
|
||||
}
|
||||
|
||||
createCenterRay(x, y) {
|
||||
const dx = centerX - x;
|
||||
const dy = centerY - y;
|
||||
const angleToCenter = Math.atan2(dy, dx) * 180 / Math.PI;
|
||||
|
||||
this.centerRays.push({
|
||||
positions: [{ x: x, y: y }],
|
||||
angle: angleToCenter,
|
||||
reachedCenter: false
|
||||
});
|
||||
}
|
||||
|
||||
updateCenterRays(deltaTime) {
|
||||
const centerThreshold = 5;
|
||||
const maxDistance = 2000;
|
||||
|
||||
for (let i = 0; i < this.centerRays.length; i++) {
|
||||
const ray = this.centerRays[i];
|
||||
|
||||
if (ray.reachedCenter) {
|
||||
if (ray.positions.length > 0) {
|
||||
ray.positions.shift();
|
||||
}
|
||||
|
||||
if (ray.positions.length <= 1) {
|
||||
this.centerRays.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
const currentPos = ray.positions[ray.positions.length - 1];
|
||||
|
||||
const dx = (this.speedHorr / 100) * this.speed * Math.cos(rad(ray.angle));
|
||||
const dy = (this.speedVert / 100) * this.speed * Math.sin(rad(ray.angle));
|
||||
const newX = currentPos.x + dx;
|
||||
const newY = currentPos.y + dy;
|
||||
|
||||
const distFromOrigin = Math.sqrt(
|
||||
Math.pow(newX - centerX, 2) + Math.pow(newY - centerY, 2)
|
||||
);
|
||||
|
||||
if (distFromOrigin > maxDistance) {
|
||||
this.centerRays.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
ray.positions.push({ x: newX, y: newY });
|
||||
|
||||
const distToCenter = Math.sqrt(
|
||||
Math.pow(newX - centerX, 2) + Math.pow(newY - centerY, 2)
|
||||
);
|
||||
|
||||
if (distToCenter <= centerThreshold) {
|
||||
ray.reachedCenter = true;
|
||||
}
|
||||
|
||||
while (ray.positions.length > this.trailLength) {
|
||||
ray.positions.shift();
|
||||
}
|
||||
}
|
||||
|
||||
for (let j = 1; j < ray.positions.length; j++) {
|
||||
const prev = ray.positions[j - 1];
|
||||
const curr = ray.positions[j];
|
||||
|
||||
let alpha = 1;
|
||||
if (this.fade) {
|
||||
alpha = (j / ray.positions.length) * 0.8 + 0.2;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(prev.x, prev.y);
|
||||
ctx.lineTo(curr.x, curr.y);
|
||||
|
||||
const col = hexToRgb(this.colourFree);
|
||||
ctx.strokeStyle = `rgba(${col.r}, ${col.g}, ${col.b}, ${alpha})`;
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setRays(newValue) {
|
||||
this.rays = newValue;
|
||||
this.prepareRayObjects();
|
||||
}
|
||||
|
||||
draw(elapsed, deltaTime) {
|
||||
deltaTime *= this.speedMultiplier / 100;
|
||||
this.updateFilters(elapsed);
|
||||
|
||||
if (this.doesWave) {
|
||||
const vertRate = this.speedVertRate / 100;
|
||||
const horrRate = this.speedHorrRate / 100;
|
||||
this.speedVert = Math.sin(elapsed / 10 * vertRate) * 85 + 100;
|
||||
this.speedHorr = Math.sin(elapsed / 10 * horrRate) * 85 + 100;
|
||||
updateControlInput(this.speedVert, "speedVert");
|
||||
updateControlInput(this.speedHorr, "speedHorr");
|
||||
}
|
||||
|
||||
const boxLeft = centerX - this.boxSize / 2;
|
||||
const boxRight = centerX + this.boxSize / 2;
|
||||
const boxTop = centerY - this.boxSize / 2;
|
||||
const boxBottom = centerY + this.boxSize / 2;
|
||||
|
||||
if (this.boxVisible) {
|
||||
ctx.strokeStyle = "white";
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeRect(boxLeft, boxTop, this.boxSize, this.boxSize);
|
||||
}
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
|
||||
for (let j = 0; j < this.rayObjects.length; j++) {
|
||||
const ray = this.rayObjects[j];
|
||||
const currentPos = ray.positions[ray.positions.length - 1];
|
||||
|
||||
let dx = (this.speedHorr / 100) * this.speed * Math.cos(rad(ray.angle));
|
||||
let dy = (this.speedVert / 100) * this.speed * Math.sin(rad(ray.angle));
|
||||
let newX = currentPos.x + dx;
|
||||
let newY = currentPos.y + dy;
|
||||
let collisionType = null;
|
||||
const oldAngle = ray.angle;
|
||||
|
||||
if (newX < boxLeft || newX > boxRight) {
|
||||
const collisionX = newX < boxLeft ? boxLeft : boxRight;
|
||||
const collisionRatio = (collisionX - currentPos.x) / dx;
|
||||
const collisionY = currentPos.y + dy * collisionRatio;
|
||||
|
||||
ray.positions.push({
|
||||
x: collisionX,
|
||||
y: collisionY,
|
||||
angle: oldAngle,
|
||||
collision: 'horizontal'
|
||||
});
|
||||
|
||||
this.createCenterRay(collisionX, collisionY);
|
||||
|
||||
ray.angle = 180 - ray.angle;
|
||||
ray.angle = ((ray.angle % 360) + 360) % 360;
|
||||
|
||||
const remainingRatio = 1 - collisionRatio;
|
||||
dx = remainingRatio * (this.speedHorr / 100) * this.speed * Math.cos(rad(ray.angle));
|
||||
dy = remainingRatio * (this.speedVert / 100) * this.speed * Math.sin(rad(ray.angle));
|
||||
newX = collisionX + dx;
|
||||
newY = collisionY + dy;
|
||||
collisionType = 'horizontal';
|
||||
}
|
||||
|
||||
if (newY < boxTop || newY > boxBottom) {
|
||||
if (collisionType === null) {
|
||||
const collisionY = newY < boxTop ? boxTop : boxBottom;
|
||||
const collisionRatio = (collisionY - currentPos.y) / dy;
|
||||
const collisionX = currentPos.x + dx * collisionRatio;
|
||||
|
||||
ray.positions.push({
|
||||
x: collisionX,
|
||||
y: collisionY,
|
||||
angle: oldAngle,
|
||||
collision: 'vertical'
|
||||
});
|
||||
|
||||
this.createCenterRay(collisionX, collisionY);
|
||||
|
||||
ray.angle = 360 - ray.angle;
|
||||
ray.angle = ((ray.angle % 360) + 360) % 360;
|
||||
|
||||
const remainingRatio = 1 - collisionRatio;
|
||||
dx = remainingRatio * (this.speedHorr / 100) * this.speed * Math.cos(rad(ray.angle));
|
||||
dy = remainingRatio * (this.speedVert / 100) * this.speed * Math.sin(rad(ray.angle));
|
||||
newX = collisionX + dx;
|
||||
newY = collisionY + dy;
|
||||
} else {
|
||||
newX = Math.max(boxLeft, Math.min(newX, boxRight));
|
||||
newY = Math.max(boxTop, Math.min(newY, boxBottom));
|
||||
ray.positions.push({
|
||||
x: newX,
|
||||
y: newY,
|
||||
angle: ray.angle,
|
||||
collision: 'corner'
|
||||
});
|
||||
|
||||
this.createCenterRay(newX, newY);
|
||||
}
|
||||
}
|
||||
|
||||
newX = Math.max(boxLeft, Math.min(newX, boxRight));
|
||||
newY = Math.max(boxTop, Math.min(newY, boxBottom));
|
||||
|
||||
if (collisionType === null) {
|
||||
ray.positions.push({
|
||||
x: newX,
|
||||
y: newY,
|
||||
angle: ray.angle
|
||||
});
|
||||
}
|
||||
|
||||
while (ray.positions.length > this.trailLength) {
|
||||
ray.positions.shift();
|
||||
}
|
||||
|
||||
for (let i = 1; i < ray.positions.length; i++) {
|
||||
const prev = ray.positions[i - 1];
|
||||
const curr = ray.positions[i];
|
||||
|
||||
let alpha = 1;
|
||||
if (this.fade) {
|
||||
alpha = (i / ray.positions.length) * 0.8 + 0.2;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(prev.x, prev.y);
|
||||
ctx.lineTo(curr.x, curr.y);
|
||||
|
||||
if (curr.collision) {
|
||||
ctx.strokeStyle = `rgba(255, 255, 0, ${alpha})`;
|
||||
} else {
|
||||
const col = hexToRgb(this.colourContained);
|
||||
ctx.strokeStyle = `rgba(${col.r}, ${col.g}, ${col.b}, ${alpha})`;
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
this.updateCenterRays(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('RaysInShape', RaysInShape);
|
||||
60
docs/js/shapes/Spiral1.js
Normal file
60
docs/js/shapes/Spiral1.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Spiral1 - Dual-direction spiral pattern
|
||||
*/
|
||||
class Spiral1 extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 1, max: 50, defaultValue: 20, property: 'sides' },
|
||||
{ type: 'range', min: 1, max: 600, defaultValue: 240, property: 'width' },
|
||||
{ type: 'color', defaultValue: '#4287f5', property: 'colour' },
|
||||
];
|
||||
|
||||
constructor(sides, width, colour) {
|
||||
super();
|
||||
this.sides = sides;
|
||||
this.width = width;
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
this.updateFilters(elapsed);
|
||||
const rotation = elapsed * (this.speedMultiplier / 100);
|
||||
const rot = Math.round((this.sides - 2) * 180 / this.sides * 2);
|
||||
const piv = 360 / this.sides;
|
||||
let stt = 0.5 * Math.PI - rad(rot);
|
||||
let end = 0;
|
||||
const n = this.width / ((this.width / 10) * (this.width / 10));
|
||||
|
||||
for (let i = 1; i < this.sides + 1; i++) {
|
||||
end = stt + rad(rot);
|
||||
ctx.lineWidth = 5;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(
|
||||
centerX + Math.cos(rad(90 + piv * i + rotation)) * this.width,
|
||||
centerY + Math.sin(rad(90 + piv * i + rotation)) * this.width,
|
||||
this.width,
|
||||
stt + rad(rotation) - (stt - end) / 2,
|
||||
end + rad(rotation) + rad(n),
|
||||
0
|
||||
);
|
||||
ctx.strokeStyle = this.colour;
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(
|
||||
centerX + Math.cos(rad(90 + piv * i - rotation)) * this.width,
|
||||
centerY + Math.sin(rad(90 + piv * i - rotation)) * this.width,
|
||||
this.width,
|
||||
stt - rad(rotation),
|
||||
end - (end - stt) / 2 + rad(n) - rad(rotation),
|
||||
0
|
||||
);
|
||||
ctx.strokeStyle = this.colour;
|
||||
ctx.stroke();
|
||||
|
||||
stt = end + -(rad(rot - piv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('Spiral1', Spiral1);
|
||||
42
docs/js/shapes/SquareTwist_angle.js
Normal file
42
docs/js/shapes/SquareTwist_angle.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* SquareTwist_angle - Twisted square pattern with angle-based scaling
|
||||
*/
|
||||
class SquareTwist_angle extends BaseShape {
|
||||
static config = [
|
||||
{ type: 'range', min: 1, max: 800, defaultValue: 400, property: 'width' },
|
||||
{ type: 'range', min: 1, max: 10, defaultValue: 1, property: 'line_width' },
|
||||
{ type: 'color', defaultValue: '#2D81FC', property: 'colour1' },
|
||||
];
|
||||
|
||||
constructor(width, line_width, colour1) {
|
||||
super();
|
||||
this.width = width;
|
||||
this.line_width = line_width;
|
||||
this.colour1 = colour1;
|
||||
}
|
||||
|
||||
drawSquare(angle, size, colour) {
|
||||
ctx.save();
|
||||
ctx.translate(centerX, centerY);
|
||||
ctx.rotate(rad(angle + 180));
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = colour;
|
||||
ctx.lineWidth = this.line_width;
|
||||
ctx.rect(-size / 2, -size / 2, size, size);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
draw(elapsed) {
|
||||
this.updateFilters(elapsed);
|
||||
const rotation = elapsed * (this.speedMultiplier / 100);
|
||||
const out_angle = rotation;
|
||||
const widthMultiplier = 1 / (2 * Math.sin(Math.PI / 180 * (130 - out_angle + 90 * Math.floor(out_angle / 90)))) + 0.5;
|
||||
|
||||
for (let i = 0; i < 25; i++) {
|
||||
this.drawSquare(rotation * i, this.width * widthMultiplier ** i, this.colour1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shapeRegistry.register('SquareTwist_angle', SquareTwist_angle);
|
||||
Reference in New Issue
Block a user