羊皮纸书页大幅重构

This commit is contained in:
strawberry 2025-07-12 23:01:38 +08:00
parent 613ff14595
commit f7863ef77c

View File

@ -22128,35 +22128,569 @@ let time=0
this.pagemax = 1
this.page = 0
}
paperTexture() {
}
paperTexture(dir) {
const textureCanvas = document.createElement('canvas');
textureCanvas.width = 325;
textureCanvas.height = 400;
const textureCtx = textureCanvas.getContext('2d');
// 填充浅色背景
textureCtx.fillStyle = '#f8f4e6';
textureCtx.fillRect(0, 0, 325, 400);
// 生成羊皮纸基础色(更精细的暖色调范围)
const baseHue = 45; // 35-60黄色到橙色范围
const baseSaturation = 40; // 25-50%饱和度
const baseLightness = 85; // 80-95%亮度
// 添加纸张纹理
// 1. 创建高度自然的基底纹理
const createOrganicBase = () => {
// 基础底色(轻微噪点纹理)
textureCtx.fillStyle = `hsl(${baseHue}, ${baseSaturation}%, ${baseLightness}%)`;
textureCtx.fillRect(0, 0, 325, 400);
for (let i = 0; i < 1000; i++) {
const x = Math.random() * 325;
const y = Math.random() * 400;
const radius = Math.random() * 2;
const alpha = Math.random() * 0.1
textureCtx.beginPath();
textureCtx.arc(x, y, radius, 0, Math.PI * 2);
textureCtx.fillStyle = `rgba(100, 80, 60, ${alpha})`;
textureCtx.fill();
// 生成有机噪点纹理(模拟羊皮纸纤维)
const noiseData = textureCtx.createImageData(325, 400);
for (let i = 0; i < noiseData.data.length; i += 4) {
// Perlin噪声生成更自然的纹理
const x = (i / 4) % 325;
const y = Math.floor((i / 4) / 325);
const noiseVal = this.perlinNoise(x / 50, y / 50) * 0.5 +
this.perlinNoise(x / 20, y / 20) * 0.3 +
this.perlinNoise(x / 5, y / 5) * 0.2;
// 基于噪声调整颜色
const hueVar = noiseVal * 10;
const satVar = noiseVal * 15;
const lightVar = noiseVal * 8;
noiseData.data[i] = this.HSLtoRGB(
(baseHue + hueVar) % 360,
Math.max(10, Math.min(70, baseSaturation + satVar)),
Math.max(70, Math.min(98, baseLightness + lightVar))
).r;
noiseData.data[i + 1] = this.HSLtoRGB(
(baseHue + hueVar) % 360,
Math.max(10, Math.min(70, baseSaturation + satVar)),
Math.max(70, Math.min(98, baseLightness + lightVar))
).g;
noiseData.data[i + 2] = this.HSLtoRGB(
(baseHue + hueVar) % 360,
Math.max(10, Math.min(70, baseSaturation + satVar)),
Math.max(70, Math.min(98, baseLightness + lightVar))
).b;
noiseData.data[i + 3] = 255;
}
textureCtx.putImageData(noiseData, 0, 0);
// 添加手工染色效果(不规则色斑)
/*for (let i = 0; i < 8 + Math.random() * 5; i++) {
const centerX = Math.random() * 325;
const centerY = Math.random() * 400;
const radius = 30 + Math.random() * 70;
const hueVar = (Math.random() - 0.5) * 15;
const satVar = (Math.random() - 0.5) * 20;
const lightVar = (Math.random() - 0.5) * 12;
// 创建有机形状的渐变
const gradient = textureCtx.createRadialGradient(
centerX, centerY, 0,
centerX + (Math.random() - 0.5) * 20,
centerY + (Math.random() - 0.5) * 20,
radius * (0.8 + Math.random() * 0.4)
);
gradient.addColorStop(0, `hsla(
${baseHue + hueVar},
${baseSaturation + satVar}%,
${baseLightness + lightVar}%,
${0.2 + Math.random() * 0.3}
)`);
gradient.addColorStop(1, 'hsla(0, 0%, 0%, 0)');
textureCtx.fillStyle = gradient;
textureCtx.beginPath();
// 有机形状绘制(非正圆)
const points = 12 + Math.floor(Math.random() * 8);
textureCtx.moveTo(
centerX + radius * Math.cos(0),
centerY + radius * Math.sin(0)
);
for (let j = 1; j <= points; j++) {
const angle = (j / points) * Math.PI * 2;
const dist = radius * (0.7 + Math.random() * 0.6);
textureCtx.lineTo(
centerX + dist * Math.cos(angle),
centerY + dist * Math.sin(angle)
);
}
textureCtx.closePath();
textureCtx.fill();
}*/
};
createOrganicBase();
// 更自然的边缘破损效果
const addEdgeTears = () => {
const tearCount = Math.floor(Math.random() * 4) + 2;
for (let i = 0; i < tearCount; i++) {
const size = 5 + Math.random() * 12;
const roughness = 2 + Math.random() * 3;
if (dir === "left") {
// 左侧边缘破损
if (Math.random() > 0.3) {
const y = Math.random() * 350 + 25;
createOrganicTear(0, y, size, Math.PI / 2, roughness);
} else {
const x = Math.random() * 50;
const y = Math.random() > 0.5 ? 0 : 400;
createOrganicTear(x, y, size * 0.7, Math.random() > 0.5 ? 0 : Math.PI, roughness);
}
} else {
// 右侧边缘破损
if (Math.random() > 0.3) {
const y = Math.random() * 350 + 25;
createOrganicTear(325, y, size, -Math.PI / 2, roughness);
} else {
const x = 275 + Math.random() * 50;
const y = Math.random() > 0.5 ? 0 : 400;
createOrganicTear(x, y, size * 0.7, Math.random() > 0.5 ? 0 : Math.PI, roughness);
}
}
}
function createOrganicTear(x, y, size, angle, roughness) {
const points = [];
const steps = 10 + Math.floor(Math.random() * 10);
// 生成有机形状的点
for (let i = 0; i <= steps; i++) {
const t = i / steps;
const r = size * (0.8 + Math.random() * 0.4);
const a = angle + Math.PI + t * Math.PI;
const noiseX = (Math.random() * 2 - 1) * roughness;
const noiseY = (Math.random() * 2 - 1) * roughness;
points.push({
x: x + r * Math.cos(a) * (0.5 + t * 0.5) + noiseX,
y: y + r * Math.sin(a) * (0.5 + t * 0.5) + noiseY
});
}
// 绘制破损形状
textureCtx.beginPath();
textureCtx.moveTo(x, y);
points.forEach(p => textureCtx.lineTo(p.x, p.y));
textureCtx.closePath();
// 破损内部阴影
textureCtx.fillStyle = `rgba(140, 110, 80, ${0.08 + Math.random()*0.07})`;
textureCtx.fill();
// 破损边缘线
textureCtx.lineWidth = 0.3 + Math.random() * 0.4;
textureCtx.strokeStyle = `rgba(100, 80, 60, ${0.4 + Math.random()*0.2})`;
textureCtx.stroke();
// 添加一些细节线条
for (let j = 0; j < 2; j++) {
textureCtx.beginPath();
const detailX = x + (points[0].x - x) * 0.3 * (j + 1);
const detailY = y + (points[0].y - y) * 0.3 * (j + 1);
textureCtx.moveTo(detailX, detailY);
for (let k = 1; k < points.length; k++) {
const p = points[k];
const toX = x + (p.x - x) * 0.3 * (j + 1);
const toY = y + (p.y - y) * 0.3 * (j + 1);
textureCtx.lineTo(toX, toY);
}
textureCtx.lineWidth = 0.1 + Math.random() * 0.2;
textureCtx.strokeStyle = `rgba(120, 90, 70, ${0.2 + Math.random()*0.1})`;
textureCtx.stroke();
}
}
};
addEdgeTears();
// 2. 完美四角阴影系统
/**
* 根据方向调整边缘阴影强度
* @param {string} dir 方向参数('left''right')
*/
const addMasterfulEdgeShadows = (dir) => {
const shadowHue = baseHue - 8;
const shadowSaturation = baseSaturation + 15;
const shadowLightness = baseLightness * 0.55;
const shadowSize = 35 + Math.random() * 15;
const shadowCanvas = document.createElement('canvas');
shadowCanvas.width = 325;
shadowCanvas.height = 400;
const shadowCtx = shadowCanvas.getContext('2d');
// 计算每个像素的阴影强度
const gradientData = shadowCtx.createImageData(325, 400);
for (let i = 0; i < gradientData.data.length; i += 4) {
const x = (i / 4) % 325;
const y = Math.floor((i / 4) / 325);
// 基础距离计算
const leftDist = x / shadowSize;
const rightDist = (325 - x) / shadowSize;
const topDist = y / shadowSize;
const bottomDist = (400 - y) / shadowSize;
// 根据方向调整左右阴影强度
let leftShadowIntensity = 0.7;
let rightShadowIntensity = 0.7;
if (dir === "left") {
rightShadowIntensity *= 0.6; // 削弱右侧阴影
} else if (dir === "right") {
leftShadowIntensity *= 0.6; // 削弱左侧阴影
}
// 计算角部距离
const tlDist = Math.sqrt(x * x + y * y) / (shadowSize * 1.4);
const trDist = Math.sqrt((325 - x) * (325 - x) + y * y) / (shadowSize * 1.4);
const blDist = Math.sqrt(x * x + (400 - y) * (400 - y)) / (shadowSize * 1.4);
const brDist = Math.sqrt((325 - x) * (325 - x) + (400 - y) * (400 - y)) / (shadowSize * 1.4);
// 综合阴影强度
let shadowAlpha = 0;
// 左侧阴影计算
if (leftDist < 1) {
shadowAlpha = Math.max(shadowAlpha, leftShadowIntensity * (1 - leftDist));
}
// 右侧阴影计算
if (rightDist < 1) {
shadowAlpha = Math.max(shadowAlpha, rightShadowIntensity * (1 - rightDist));
}
// 顶部和底部阴影(保持不变)
if (topDist < 1) {
shadowAlpha = Math.max(shadowAlpha, 0.7 * (1 - topDist));
}
if (bottomDist < 1) {
shadowAlpha = Math.max(shadowAlpha, 0.7 * (1 - bottomDist));
}
// 角部阴影(取相邻两边强度的中间值)
if (tlDist < 1) {
const cornerAlpha = (leftShadowIntensity + 0.7) / 2 * (1 - tlDist);
shadowAlpha = Math.max(shadowAlpha, cornerAlpha);
}
if (trDist < 1) {
const cornerAlpha = (rightShadowIntensity + 0.7) / 2 * (1 - trDist);
shadowAlpha = Math.max(shadowAlpha, cornerAlpha);
}
if (blDist < 1) {
const cornerAlpha = (leftShadowIntensity + 0.7) / 2 * (1 - blDist);
shadowAlpha = Math.max(shadowAlpha, cornerAlpha);
}
if (brDist < 1) {
const cornerAlpha = (rightShadowIntensity + 0.7) / 2 * (1 - brDist);
shadowAlpha = Math.max(shadowAlpha, cornerAlpha);
}
// 限制最大透明度
shadowAlpha = Math.min(0.6, shadowAlpha);
// 转换为RGB
const rgb = this.HSLtoRGB(shadowHue, shadowSaturation, shadowLightness);
gradientData.data[i] = rgb.r;
gradientData.data[i + 1] = rgb.g;
gradientData.data[i + 2] = rgb.b;
gradientData.data[i + 3] = shadowAlpha * 255;
}
shadowCtx.putImageData(gradientData, 0, 0);
// 应用阴影
textureCtx.globalCompositeOperation = 'multiply';
textureCtx.drawImage(shadowCanvas, 0, 0);
textureCtx.globalCompositeOperation = 'source-over';
}
addMasterfulEdgeShadows(dir);
// 3. 超真实细节系统
const addHyperRealisticDetails = () => {
// 3.1 纤维纹理(模拟羊皮纸纤维)
for (let i = 0; i < 2000; i++) {
const x = Math.random() * 325;
const y = Math.random() * 400;
const length = 1 + Math.random() * 4;
const angle = Math.random() * Math.PI * 2;
const lightVar = (Math.random() > 0.5 ? 5 : -5) * (0.5 + Math.random());
textureCtx.beginPath();
textureCtx.moveTo(x, y);
textureCtx.lineTo(
x + Math.cos(angle) * length,
y + Math.sin(angle) * length
);
textureCtx.strokeStyle = `hsla(
${baseHue + (Math.random() - 0.5) * 8},
${baseSaturation * (0.7 + Math.random() * 0.6)}%,
${baseLightness + lightVar}%,
${0.03 + Math.random() * 0.04}
)`;
textureCtx.lineWidth = 0.2 + Math.random() * 0.3;
textureCtx.stroke();
}
// 3.2 自然划痕系统
for (let i = 0; i < 10 + Math.random() * 10; i++) {
const scratchLength = 15 + Math.random() * 60;
const startX = Math.random() * 325;
const startY = Math.random() * 400;
const angle = Math.random() * Math.PI * 2;
const curveIntensity = 5 + Math.random() * 10;
// 主划痕路径
textureCtx.beginPath();
textureCtx.moveTo(startX, startY);
// 贝塞尔曲线控制点
const cp1x = startX + Math.cos(angle) * scratchLength * 0.3 + (Math.random() - 0.5) * curveIntensity;
const cp1y = startY + Math.sin(angle) * scratchLength * 0.3 + (Math.random() - 0.5) * curveIntensity;
const cp2x = startX + Math.cos(angle) * scratchLength * 0.7 + (Math.random() - 0.5) * curveIntensity;
const cp2y = startY + Math.sin(angle) * scratchLength * 0.7 + (Math.random() - 0.5) * curveIntensity;
const endX = startX + Math.cos(angle) * scratchLength;
const endY = startY + Math.sin(angle) * scratchLength;
textureCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, endX, endY);
// 划痕样式(宽度变化)
const lineWidth = 0.1 + Math.random() * 0.4;
const scratchDarkness = 0.6 + Math.random() * 0.3;
textureCtx.strokeStyle = `hsla(
${baseHue - 5 + Math.random() * 10},
${baseSaturation + 5 + Math.random() * 15}%,
${baseLightness * scratchDarkness}%,
${0.4 + Math.random() * 0.3}
)`;
textureCtx.lineWidth = lineWidth;
textureCtx.lineCap = 'round';
textureCtx.stroke();
// 划痕高光(模拟凹陷)
if (Math.random() > 0.3) {
textureCtx.beginPath();
textureCtx.moveTo(
startX + (Math.random() - 0.5) * lineWidth * 2,
startY + (Math.random() - 0.5) * lineWidth * 2
);
textureCtx.bezierCurveTo(
cp1x + (Math.random() - 0.5) * lineWidth * 2,
cp1y + (Math.random() - 0.5) * lineWidth * 2,
cp2x + (Math.random() - 0.5) * lineWidth * 2,
cp2y + (Math.random() - 0.5) * lineWidth * 2,
endX + (Math.random() - 0.5) * lineWidth * 2,
endY + (Math.random() - 0.5) * lineWidth * 2
);
textureCtx.strokeStyle = `hsla(
${baseHue + 5 + Math.random() * 5},
${baseSaturation * 0.7}%,
${baseLightness * 1.1}%,
${0.2 + Math.random() * 0.1}
)`;
textureCtx.lineWidth = lineWidth * 0.7;
textureCtx.stroke();
}
}
// 3.3 专业级污渍系统
for (let i = 0; i < 3 + Math.random() * 5; i++) {
const stainType = Math.floor(Math.random() * 3); // 0: 液体污渍, 1: 指纹, 2: 老化斑点
if (stainType === 1) {
// 指纹污渍
const centerX = Math.random() * 325;
const centerY = Math.random() * 400;
const size = 10 + Math.random() * 15;
const rotation = Math.random() * Math.PI * 2;
// 指纹纹路
for (let j = 0; j < 15 + Math.random() * 10; j++) {
const radius = size * (0.3 + Math.random() * 0.7);
const angle = (j / 15) * Math.PI * 2 + rotation;
const width = size * (0.03 + Math.random() * 0.05);
textureCtx.beginPath();
textureCtx.arc(
centerX + Math.cos(angle) * radius * 0.5,
centerY + Math.sin(angle) * radius * 0.5,
width, 0, Math.PI * 2
);
textureCtx.fillStyle = `hsla(
${baseHue - 5 + Math.random() * 10},
${baseSaturation + 5}%,
${baseLightness * 0.8}%,
${0.1 + Math.random() * 0.05}
)`;
textureCtx.fill();
}
} else {
// 老化斑点
const centerX = Math.random() * 325;
const centerY = Math.random() * 400;
const size = 5 + Math.random() * 15;
const points = 8 + Math.floor(Math.random() * 6);
textureCtx.beginPath();
textureCtx.moveTo(
centerX + size * Math.cos(0),
centerY + size * Math.sin(0)
);
for (let j = 1; j <= points; j++) {
const angle = (j / points) * Math.PI * 2;
const radius = size * (0.7 + Math.random() * 0.6);
textureCtx.lineTo(
centerX + radius * Math.cos(angle),
centerY + radius * Math.sin(angle)
);
}
textureCtx.closePath();
const gradient = textureCtx.createRadialGradient(
centerX, centerY, 0,
centerX, centerY, size * 1.2
);
gradient.addColorStop(0, `hsla(
${baseHue - 15 + Math.random() * 20},
${baseSaturation + 20}%,
${baseLightness * 0.5}%,
${0.2 + Math.random() * 0.1}
)`);
gradient.addColorStop(1, 'hsla(0, 0%, 0%, 0)');
textureCtx.fillStyle = gradient;
textureCtx.fill();
}
}
};
addHyperRealisticDetails();
return textureCanvas;
}
// 辅助函数(需要在类中定义)
/**
* 2D Perlin噪声生成器
* @param {number} x x坐标
* @param {number} y y坐标
* @returns {number} 噪声值(-1到1之间)
*/
perlinNoise(x, y) {
// 梯度向量表
const grad3 = [
[1, 1, 0],
[-1, 1, 0],
[1, -1, 0],
[-1, -1, 0],
[1, 0, 1],
[-1, 0, 1],
[1, 0, -1],
[-1, 0, -1],
[0, 1, 1],
[0, -1, 1],
[0, 1, -1],
[0, -1, -1]
];
// 置换表(随机排列0-255)
if (!this.perm) {
this.perm = new Array(512);
for (let i = 0; i < 256; i++) {
this.perm[i] = this.perm[i + 256] = Math.floor(Math.random() * 256);
}
}
// 辅助函数
const dot = (g, x, y) => g[0] * x + g[1] * y;
const fade = t => t * t * t * (t * (t * 6 - 15) + 10);
const lerp = (a, b, t) => a + t * (b - a);
// 确定单元方块
const X = Math.floor(x) & 255;
const Y = Math.floor(y) & 255;
// 相对单元方块的坐标
x -= Math.floor(x);
y -= Math.floor(y);
// 计算四个角落的梯度贡献
const n00 = dot(grad3[this.perm[X + this.perm[Y]] % 12], x, y);
const n01 = dot(grad3[this.perm[X + this.perm[Y + 1]] % 12], x, y - 1);
const n10 = dot(grad3[this.perm[X + 1 + this.perm[Y]] % 12], x - 1, y);
const n11 = dot(grad3[this.perm[X + 1 + this.perm[Y + 1]] % 12], x - 1, y - 1);
// 使用缓和曲线插值
const u = fade(x);
const v = fade(y);
return lerp(
lerp(n00, n10, u),
lerp(n01, n11, u),
v
);
}
/**
* HSL颜色空间转RGB
* @param {number} h 色相(0-360)
* @param {number} s 饱和度(0-100)
* @param {number} l 亮度(0-100)
* @returns {Object} {r, g, b} 各通道值(0-255)
*/
HSLtoRGB(h, s, l) {
h /= 360;
s /= 100;
l /= 100;
let r, g, b;
if (s === 0) {
r = g = b = l; // 灰度
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
}
background() {
book.style.display = "block"
this.leftbackground = this.paperTexture('left')
this.rightbackground = this.paperTexture('right')
if (core.domStyle.isVertical) { //对竖屏进行绘制坐标变换,只需要考虑横屏绘制,竖屏将自适应转置
core.maps._setHDCanvasSize(ctx, 416, 676)
ctx.save(); //保存设置
@ -22184,11 +22718,42 @@ let time=0
ctx.lineTo(676 - dx, 0)
ctx.lineTo(676, dy)
ctx.closePath()
ctx.fillStyle = "#e0d6c2"
ctx.fill()
core.drawImage(ctx, this.paperTexture(), 0, 0, 325, 400, dx, 0, this.width, this.height)
core.drawImage(ctx, this.paperTexture(), 0, 0, 325, 400, dx + this.width, 0, this.width, this.height)
ctx.strokeStyle = `rgb(${248*0.7}, ${244*0.7}, ${230*0.7})`;
ctx.beginPath()
ctx.moveTo(2, 100)
ctx.lineTo(2, 130)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(5, 20)
ctx.lineTo(5, 30)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(6, 240)
ctx.lineTo(6, 260)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(10, 340)
ctx.lineTo(10, 360)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(665, 100)
ctx.lineTo(665, 130)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(670, 190)
ctx.lineTo(670, 160)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(671, 20)
ctx.lineTo(671, 130)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(673, 350)
ctx.lineTo(673, 370)
ctx.stroke()
core.drawImage(ctx, this.leftbackground, 0, 0, 325, 400, dx, 0, this.width, this.height)
core.drawImage(ctx, this.rightbackground, 0, 0, 325, 400, dx + this.width, 0, this.width, this.height)
ctx.beginPath()
ctx.moveTo(0, 416)
@ -22204,8 +22769,41 @@ let time=0
ctx.lineTo(338, this.height)
ctx.lineTo(338 + dx, 416)
ctx.closePath()
ctx.fillStyle = '#706b61'
ctx.fill()
ctx.beginPath()
ctx.moveTo(20, 402)
ctx.lineTo(50, 402)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(180, 405)
ctx.lineTo(220, 405)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(40, 409)
ctx.lineTo(80, 409)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(268, 412)
ctx.lineTo(290, 412)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(430, 403)
ctx.lineTo(470, 403)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(510, 407)
ctx.lineTo(530, 407)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(350, 414)
ctx.lineTo(370, 414)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(602, 411)
ctx.lineTo(610, 411)
ctx.stroke()
ctx.restore(); //恢复变换前的坐标,否则将连续转置
}
}