书页webgl绘制完成,并兼容canvas2d
This commit is contained in:
parent
6ae5edd25b
commit
20465e0e97
@ -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)}%,
|
||||
|
Loading…
Reference in New Issue
Block a user