书页webgl绘制完成,并兼容canvas2d

This commit is contained in:
strawberry 2025-08-24 15:59:06 +08:00
parent 6ae5edd25b
commit 20465e0e97

View File

@ -23355,17 +23355,17 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
ctx.shadowOffsetY = 0;
core.drawBoxAnimate()
// 绘制左页
core.drawImage(ctx, this.paperpages[this.page - 1][0], 0, 0, 300, 400, dx, dy, this.width, this.height);
core.drawImage(ctx, this.paperpages[this.page - 1][0], 0, 0, 300 - this.ani.x / (this.width * 2), 400, dx, dy, 300 - this.ani.x / (this.width * 2), this.height);
ctx.shadowColor = "transparent";
core.drawImage(ctx, bookInfo.left.canvas, 0, 0, 300, 400, dx, dy, this.width, this.height);
core.drawImage(ctx, bookInfo.left.canvas, 0, 0, 300 - this.ani.x / (this.width * 2), 400, dx, dy, 300 - this.ani.x / (this.width * 2), this.height);
ctx.shadowColor = "rgba(0,0,0,0.5)";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// 绘制右页
core.drawImage(ctx, this.paperpages[this.page - 1][1], 0, 0, 300, 400, dx + this.width, dy, this.width, this.height);
core.drawImage(ctx, this.paperpages[this.page - 1][1], Math.max(0, this.ani.x - 300), 0, Math.min(600 - this.ani.x, 300), 400, dx + this.width + Math.max(0, this.ani.x - 300), dy, Math.min(600 - this.ani.x, 300), this.height);
ctx.shadowColor = "transparent";
core.drawImage(ctx, bookInfo.right.canvas, 0, 0, 300, 400, dx + this.width, dy, this.width, this.height);
core.drawImage(ctx, bookInfo.right.canvas, Math.max(0, this.ani.x - 300), 0, Math.min(600 - this.ani.x, 300), 400, dx + this.width + Math.max(0, this.ani.x - 300), dy, Math.min(600 - this.ani.x, 300), this.height);
ctx.shadowColor = "rgba(0,0,0,0.5)";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 0;
@ -24031,7 +24031,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
ctx.shadowOffsetY = 0;
core.drawBoxAnimate()
// 绘制左页
core.drawImage(ctx, this.paperpages[this.page - 1][0], 0, 0, 300, 400, dx, dy, this.width, this.height);
core.drawImage(ctx, this.paperpages[this.page - 1][0], 0, 0, 300 - Math.max(0, this.ani.x - this.width) / this.width * 300, 400, dx, dy, 300 - Math.max(0, this.ani.x - this.width) / this.width * 300, this.height);
ctx.shadowColor = "transparent";
core.drawImage(ctx, bookInfo.left.canvas, 0, 0, 300, 400, dx, dy, this.width, this.height);
ctx.shadowColor = "rgba(0,0,0,0.5)";
@ -24039,7 +24039,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// 绘制右页
core.drawImage(ctx, this.paperpages[this.page - 1][1], 0, 0, 300, 400, dx + this.width, dy, this.width, this.height);
core.drawImage(ctx, this.paperpages[this.page - 1][1], 0, 0, 300 - this.ani.x / (this.width * 2) * 300, 400, dx + this.width, dy, 300 - this.ani.x / (this.width * 2) * 300, this.height);
ctx.shadowColor = "transparent";
core.drawImage(ctx, bookInfo.right.canvas, 0, 0, 300, 400, dx + this.width, dy, this.width, this.height);
ctx.shadowColor = "rgba(0,0,0,0.5)";
@ -24058,6 +24058,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
core.drawImage(ctx, this.paperpages[page - 1][1], 300 - this.ani.x / (this.width * 2) * 300, 0, this.ani.x / (this.width * 2) * 300, 400, 676 - dx - this.ani.x / 2, dy, this.ani.x / 2, this.height);
ctx.shadowColor = "transparent";
core.drawImage(ctx, bookInfo.tempRight.canvas, 300 - this.ani.x / (this.width * 2) * 300, 0, this.ani.x / (this.width * 2) * 300, 400, 676 - dx - this.ani.x / 2, dy, this.ani.x / 2, this.height);
ctx.shadowColor = "rgba(0,0,0,0.5)";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 0;
@ -24204,6 +24205,10 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
// 创建书页内容 (400px高居中放置)
const createPageContent = () => {
const canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 400;
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
const contentCanvas = document.createElement('canvas');
contentCanvas.width = 300;
contentCanvas.height = 400;
@ -24216,44 +24221,146 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
// 创建高度自然的基底纹理
const createOrganicBase = () => {
// 基础底色(轻微噪点纹理)
contentCtx.fillStyle = `hsl(${baseHue}, ${baseSaturation}%, ${baseLightness}%)`;
contentCtx.fillRect(0, 0, 300, 400);
if (gl) {
// 创建WebGL着色器程序
const vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`);
gl.compileShader(vertShader);
// 生成有机噪点纹理(模拟羊皮纸纤维)
const noiseData = contentCtx.createImageData(300, 400);
for (let i = 0; i < noiseData.data.length; i += 4) {
// Perlin噪声生成更自然的纹理
const x = (i / 4) % 300;
const y = Math.floor((i / 4) / 300);
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 fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, `
precision highp float;
// 噪声函数
float noise(vec2 p) {
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}
// 简化版Perlin噪声
float perlinNoise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
// 四个角落的随机值
float a = noise(i);
float b = noise(i + vec2(1.0, 0.0));
float c = noise(i + vec2(0.0, 1.0));
float d = noise(i + vec2(1.0, 1.0));
// 平滑插值
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) +
(c - a) * u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
// HSL转RGB
vec3 hsl2rgb(float h, float s, float l) {
float c = (1.0 - abs(2.0 * l - 1.0)) * s;
float x = c * (1.0 - abs(mod(h / 60.0, 2.0) - 1.0));
float m = l - c / 2.0;
if (h < 60.0) return vec3(c + m, x + m, m);
else if (h < 120.0) return vec3(x + m, c + m, m);
else if (h < 180.0) return vec3(m, c + m, x + m);
else if (h < 240.0) return vec3(m, x + m, c + m);
else if (h < 300.0) return vec3(x + m, m, c + m);
else return vec3(c + m, m, x + m);
}
void main() {
vec2 uv = gl_FragCoord.xy / vec2(300.0, 400.0);
// 多层Perlin噪声叠加
float n = perlinNoise(uv * 10.0) * 0.5 +
perlinNoise(uv * 30.0) * 0.3 +
perlinNoise(uv * 60.0) * 0.2;
// 基础颜色参数
float baseHue = 45.0;
float baseSaturation = 0.35;
float baseLightness = 0.6;
// 应用噪声变化
float hue = mod(baseHue + n * 10.0, 360.0);
float saturation = clamp(baseSaturation + n * 0.15, 0.1, 0.7);
float lightness = clamp(baseLightness + n * 0.08, 0.7, 0.98);
// 转换为RGB
vec3 color = hsl2rgb(hue, saturation, lightness);
gl_FragColor = vec4(color, 1.0);
}
`);
gl.compileShader(fragShader);
const program = gl.createProgram();
gl.attachShader(program, vertShader);
gl.attachShader(program, fragShader);
gl.linkProgram(program);
gl.useProgram(program);
// 基于噪声调整颜色
const hueVar = noiseVal * 10;
const satVar = noiseVal * 15;
const lightVar = noiseVal * 8;
// 创建全屏四边形
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, 1, -1, -1, 1,
-1, 1, 1, -1, 1, 1
]), gl.STATIC_DRAW);
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;
const positionLoc = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
// 渲染到canvas
gl.viewport(0, 0, 300, 400);
gl.drawArrays(gl.TRIANGLES, 0, 6);
contentCtx.drawImage(canvas, 0, 0, 300, 400)
} else {
console.warn("当前浏览器不支持webgl回退使用canvas2d实现")
// 基础底色(轻微噪点纹理)
contentCtx.fillStyle = `hsl(${baseHue}, ${baseSaturation}%, ${baseLightness}%)`;
contentCtx.fillRect(0, 0, 300, 400);
// 生成有机噪点纹理(模拟羊皮纸纤维)
const noiseData = contentCtx.createImageData(300, 400);
for (let i = 0; i < noiseData.data.length; i += 4) {
// Perlin噪声生成更自然的纹理
const x = (i / 4) % 300;
const y = Math.floor((i / 4) / 300);
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;
}
contentCtx.putImageData(noiseData, 0, 0);
}
contentCtx.putImageData(noiseData, 0, 0);
};
createOrganicBase();
// 更自然的边缘破损效果
@ -24448,7 +24555,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
// 超真实细节系统
const addHyperRealisticDetails = () => {
// 3.1 纤维纹理(模拟羊皮纸纤维)
for (let i = 0; i < 2000; i++) {
for (let i = 0; i < 200; i++) {
const x = Math.random() * 300;
const y = Math.random() * 400;
const length = 1 + Math.random() * 4;
@ -24461,7 +24568,6 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
x + Math.cos(angle) * length,
y + Math.sin(angle) * length
);
contentCtx.strokeStyle = `hsla(
${baseHue + (Math.random() - 0.5) * 8},
${baseSaturation * (0.7 + Math.random() * 0.6)}%,