feat: 实现4层晕染绘制系统,替换原有渐变绘制逻辑
This commit is contained in:
@@ -289,6 +289,94 @@ function drawInkDot(
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function drawInkBlob(
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
cx: number,
|
||||||
|
cy: number,
|
||||||
|
baseR: number,
|
||||||
|
seed1: number,
|
||||||
|
seed2: number,
|
||||||
|
wobble: number,
|
||||||
|
detail: number,
|
||||||
|
rgb: string,
|
||||||
|
opacity: number,
|
||||||
|
): void {
|
||||||
|
ctx.beginPath();
|
||||||
|
for (let i = 0; i <= detail; i++) {
|
||||||
|
const angle = (i / detail) * Math.PI * 2;
|
||||||
|
const noise = Math.sin(angle * 3 + seed1) * 0.3 + Math.cos(angle * 5 + seed2) * 0.2;
|
||||||
|
const r = Math.max(0.5, baseR * (1 + noise * wobble));
|
||||||
|
const x = cx + Math.cos(angle) * r;
|
||||||
|
const y = cy + Math.sin(angle) * r;
|
||||||
|
if (i === 0) {
|
||||||
|
ctx.moveTo(x, y);
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fillStyle = `rgba(${rgb}, ${opacity})`;
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawInkLayers(
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
p: Particle,
|
||||||
|
config: ResponsiveConfig,
|
||||||
|
): void {
|
||||||
|
const tone = TONES[p.toneIndex];
|
||||||
|
if (!tone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAccent = ACCENT_TONES.includes(p.toneIndex);
|
||||||
|
const deepRgb = isAccent ? tone.rgb : (p.toneIndex <= 1 ? tone.rgb : TONES[0].rgb);
|
||||||
|
const lightRgb = isAccent ? TONES[1].rgb : (p.toneIndex >= 2 ? tone.rgb : TONES[2].rgb);
|
||||||
|
|
||||||
|
const r = Math.max(0.5, p.radius) * config.inkRadiusScale;
|
||||||
|
const op = p.opacity;
|
||||||
|
const detail = config.wobbleDetail;
|
||||||
|
|
||||||
|
const layers: Array<{ radiusMul: number; opacityMul: number; rgb: string; wobble: number; useRect: boolean }> = [
|
||||||
|
{ radiusMul: 5, opacityMul: 0.04, rgb: lightRgb, wobble: 0.25, useRect: true },
|
||||||
|
{ radiusMul: 3, opacityMul: 0.12, rgb: lightRgb, wobble: 0.2, useRect: true },
|
||||||
|
{ radiusMul: 1.5, opacityMul: 0.4, rgb: deepRgb, wobble: 0.12, useRect: false },
|
||||||
|
{ radiusMul: 1, opacityMul: 0.9, rgb: deepRgb, wobble: 0.06, useRect: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
const layerCount = p.inkLayerCount;
|
||||||
|
const startIdx = layers.length - layerCount;
|
||||||
|
|
||||||
|
for (let i = startIdx; i < layers.length; i++) {
|
||||||
|
const layer = layers[i];
|
||||||
|
if (!layer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const layerR = r * layer.radiusMul;
|
||||||
|
const layerOp = op * layer.opacityMul;
|
||||||
|
|
||||||
|
if (layer.useRect) {
|
||||||
|
const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, layerR);
|
||||||
|
grad.addColorStop(0, `rgba(${layer.rgb}, ${layerOp})`);
|
||||||
|
grad.addColorStop(0.5, `rgba(${layer.rgb}, ${layerOp * 0.3})`);
|
||||||
|
grad.addColorStop(1, 'transparent');
|
||||||
|
ctx.fillStyle = grad;
|
||||||
|
ctx.fillRect(p.x - layerR, p.y - layerR, layerR * 2, layerR * 2);
|
||||||
|
} else {
|
||||||
|
const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, layerR);
|
||||||
|
grad.addColorStop(0, `rgba(${layer.rgb}, ${layerOp})`);
|
||||||
|
grad.addColorStop(0.6, `rgba(${layer.rgb}, ${layerOp * 0.5})`);
|
||||||
|
grad.addColorStop(1, 'transparent');
|
||||||
|
ctx.fillStyle = grad;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(p.x, p.y, layerR, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
drawInkBlob(ctx, p.x, p.y, layerR * 0.7, p.seed1, p.seed2, layer.wobble, detail, layer.rgb, layerOp * 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function drawParticle(
|
function drawParticle(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
p: Particle,
|
p: Particle,
|
||||||
@@ -309,53 +397,9 @@ function drawParticle(
|
|||||||
|
|
||||||
if (p.phase === 'spreading') {
|
if (p.phase === 'spreading') {
|
||||||
drawTrail(ctx, p, rgb);
|
drawTrail(ctx, p, rgb);
|
||||||
|
drawInkLayers(ctx, p, config);
|
||||||
if (config.gradientLayers >= 3) {
|
|
||||||
const grad3 = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, r * 5 * gs);
|
|
||||||
grad3.addColorStop(0, `rgba(${rgb}, ${p.opacity * 0.06})`);
|
|
||||||
grad3.addColorStop(0.5, `rgba(${rgb}, ${p.opacity * 0.02})`);
|
|
||||||
grad3.addColorStop(1, 'transparent');
|
|
||||||
ctx.fillStyle = grad3;
|
|
||||||
ctx.fillRect(p.x - r * 5 * gs, p.y - r * 5 * gs, r * 10 * gs, r * 10 * gs);
|
|
||||||
}
|
|
||||||
|
|
||||||
const grad2R = r * 2.8 * gs;
|
|
||||||
const grad2 = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, grad2R);
|
|
||||||
grad2.addColorStop(0, `rgba(${rgb}, ${p.opacity * 0.2})`);
|
|
||||||
grad2.addColorStop(0.6, `rgba(${rgb}, ${p.opacity * 0.06})`);
|
|
||||||
grad2.addColorStop(1, 'transparent');
|
|
||||||
ctx.fillStyle = grad2;
|
|
||||||
ctx.fillRect(p.x - grad2R, p.y - grad2R, grad2R * 2, grad2R * 2);
|
|
||||||
|
|
||||||
const grad1 = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, r);
|
|
||||||
grad1.addColorStop(0, `rgba(${rgb}, ${p.opacity * 0.9})`);
|
|
||||||
grad1.addColorStop(0.7, `rgba(${rgb}, ${p.opacity * 0.4})`);
|
|
||||||
grad1.addColorStop(1, 'transparent');
|
|
||||||
ctx.fillStyle = grad1;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(p.x, p.y, r, 0, Math.PI * 2);
|
|
||||||
ctx.fill();
|
|
||||||
|
|
||||||
drawInkDot(ctx, p.x, p.y, r * 0.6, p.rotation, p.scaleX, p.scaleY, rgb, p.opacity * 0.7);
|
|
||||||
} else if (p.phase === 'settling') {
|
} else if (p.phase === 'settling') {
|
||||||
const grad2R = r * 2.2 * gs;
|
drawInkLayers(ctx, p, config);
|
||||||
const grad2 = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, grad2R);
|
|
||||||
grad2.addColorStop(0, `rgba(${rgb}, ${p.opacity * 0.15})`);
|
|
||||||
grad2.addColorStop(0.5, `rgba(${rgb}, ${p.opacity * 0.05})`);
|
|
||||||
grad2.addColorStop(1, 'transparent');
|
|
||||||
ctx.fillStyle = grad2;
|
|
||||||
ctx.fillRect(p.x - grad2R, p.y - grad2R, grad2R * 2, grad2R * 2);
|
|
||||||
|
|
||||||
const grad1 = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, r);
|
|
||||||
grad1.addColorStop(0, `rgba(${rgb}, ${p.opacity * 0.85})`);
|
|
||||||
grad1.addColorStop(0.6, `rgba(${rgb}, ${p.opacity * 0.3})`);
|
|
||||||
grad1.addColorStop(1, 'transparent');
|
|
||||||
ctx.fillStyle = grad1;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(p.x, p.y, r, 0, Math.PI * 2);
|
|
||||||
ctx.fill();
|
|
||||||
|
|
||||||
drawInkDot(ctx, p.x, p.y, r * 0.5, p.rotation, p.scaleX, p.scaleY, rgb, p.opacity * 0.5);
|
|
||||||
} else if (p.phase === 'fading') {
|
} else if (p.phase === 'fading') {
|
||||||
const grad1 = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, r * 1.5);
|
const grad1 = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, r * 1.5);
|
||||||
grad1.addColorStop(0, `rgba(${rgb}, ${p.opacity * 0.6})`);
|
grad1.addColorStop(0, `rgba(${rgb}, ${p.opacity * 0.6})`);
|
||||||
|
|||||||
Reference in New Issue
Block a user