feat: 实现宣纸纹理叠加,使用OffscreenCanvas生成可复用纹理图案
This commit is contained in:
@@ -163,6 +163,59 @@ function getResponsiveConfig(W: number): ResponsiveConfig {
|
||||
};
|
||||
}
|
||||
|
||||
function generatePaperTexture(size: number): HTMLCanvasElement {
|
||||
const offscreen = document.createElement('canvas');
|
||||
offscreen.width = size;
|
||||
offscreen.height = size;
|
||||
const octx = offscreen.getContext('2d');
|
||||
if (!octx) {
|
||||
return offscreen;
|
||||
}
|
||||
|
||||
octx.fillStyle = '#FAFAF5';
|
||||
octx.fillRect(0, 0, size, size);
|
||||
|
||||
for (let i = 0; i < size * size * 0.15; i++) {
|
||||
const x = Math.random() * size;
|
||||
const y = Math.random() * size;
|
||||
const gray = 200 + Math.floor(Math.random() * 40);
|
||||
const alpha = 0.02 + Math.random() * 0.04;
|
||||
octx.fillStyle = `rgba(${gray}, ${gray}, ${gray}, ${alpha})`;
|
||||
octx.fillRect(x, y, 1, 1);
|
||||
}
|
||||
|
||||
octx.strokeStyle = 'rgba(180, 175, 168, 0.015)';
|
||||
octx.lineWidth = 0.5;
|
||||
for (let i = 0; i < size * 0.3; i++) {
|
||||
const y = Math.random() * size;
|
||||
const startX = Math.random() * size * 0.3;
|
||||
const endX = startX + size * (0.1 + Math.random() * 0.4);
|
||||
octx.beginPath();
|
||||
octx.moveTo(startX, y);
|
||||
octx.lineTo(Math.min(endX, size), y + (Math.random() - 0.5) * 2);
|
||||
octx.stroke();
|
||||
}
|
||||
|
||||
return offscreen;
|
||||
}
|
||||
|
||||
function drawPaperTexture(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
W: number,
|
||||
H: number,
|
||||
texture: HTMLCanvasElement,
|
||||
): void {
|
||||
const pattern = ctx.createPattern(texture, 'repeat');
|
||||
if (!pattern) {
|
||||
return;
|
||||
}
|
||||
ctx.save();
|
||||
ctx.fillStyle = pattern;
|
||||
ctx.globalAlpha = 0.03;
|
||||
ctx.fillRect(0, 0, W, H);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function easeInOutCubic(t: number): number {
|
||||
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
||||
}
|
||||
@@ -700,6 +753,7 @@ export function InkDataMorph({
|
||||
const animationRef = useRef<number | undefined>(undefined);
|
||||
const startTimeRef = useRef<number>(0);
|
||||
const configRef = useRef<ResponsiveConfig | null>(null);
|
||||
const paperTextureRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
const drawStaticFinal = useCallback(() => {
|
||||
@@ -773,6 +827,7 @@ export function InkDataMorph({
|
||||
const config = getResponsiveConfig(W);
|
||||
configRef.current = config;
|
||||
particlesRef.current = initParticlesArray(W, H, config);
|
||||
paperTextureRef.current = generatePaperTexture(config.paperTextureSize);
|
||||
|
||||
if (shouldReduceMotion) {
|
||||
drawStaticFinal();
|
||||
@@ -810,6 +865,10 @@ export function InkDataMorph({
|
||||
drawConnections(ctx, particles, config.connectionDistance);
|
||||
drawInkStrings(ctx, particles, config);
|
||||
|
||||
if (paperTextureRef.current) {
|
||||
drawPaperTexture(ctx, W, H, paperTextureRef.current);
|
||||
}
|
||||
|
||||
if (allComplete) {
|
||||
animationRef.current = undefined;
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user