@visactor/vrender-animate
Version:
This module provides a graph-based animation system for VRender.
118 lines (114 loc) • 7.83 kB
JavaScript
import { HybridEffectBase } from "./base/CustomEffectBase";
import { ImageProcessUtils, ShaderLibrary } from "./base/ImageProcessUtils";
export class Grayscale extends HybridEffectBase {
constructor(from, to, duration, easing, params) {
var _a, _b, _c;
super(from, to, duration, easing, params);
const rawStrength = void 0 !== (null === (_a = null == params ? void 0 : params.options) || void 0 === _a ? void 0 : _a.strength) ? params.options.strength : 1, clampedStrength = Math.max(0, Math.min(1, rawStrength));
this.colorConfig = {
effectType: (null === (_b = null == params ? void 0 : params.options) || void 0 === _b ? void 0 : _b.effectType) || "grayscale",
strength: clampedStrength,
useWebGL: void 0 === (null === (_c = null == params ? void 0 : params.options) || void 0 === _c ? void 0 : _c.useWebGL) || params.options.useWebGL
};
}
getShaderSources() {
return {
vertex: ShaderLibrary.STANDARD_VERTEX_SHADER,
fragment: `\n precision mediump float;\n uniform sampler2D u_texture;\n uniform float u_time;\n uniform float u_strength;\n uniform int u_effectType;\n uniform vec2 u_resolution;\n varying vec2 v_texCoord;\n\n ${ShaderLibrary.SHADER_FUNCTIONS}\n\n void main() {\n vec2 uv = v_texCoord;\n vec4 originalColor = texture2D(u_texture, uv);\n vec3 color = originalColor.rgb;\n\n // 计算动态强度\n float dynamicStrength = calculateDynamicStrength(u_strength, u_time);\n\n if (u_effectType == 0) {\n // 灰度效果\n float gray = luminance(color);\n vec3 grayColor = vec3(gray);\n color = mix(color, grayColor, dynamicStrength);\n } else if (u_effectType == 1) {\n // 褐色调效果\n vec3 sepiaColor = sepia(color);\n color = mix(color, sepiaColor, dynamicStrength);\n }\n\n gl_FragColor = vec4(color, originalColor.a);\n }\n `
};
}
applyWebGLEffect(canvas) {
if (!this.gl || !this.program || !this.webglCanvas) return null;
this.setupWebGLState(canvas);
const texture = this.createTextureFromCanvas(canvas);
if (!texture) return null;
const vertexBuffer = this.createFullScreenQuad();
if (!vertexBuffer) return this.gl.deleteTexture(texture), null;
try {
return this.gl.useProgram(this.program), this.setupVertexAttributes(), this.setColorUniforms(),
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4), this.webglCanvas;
} finally {
this.gl.deleteTexture(texture), this.gl.deleteBuffer(vertexBuffer);
}
}
setColorUniforms() {
if (!this.gl || !this.program) return;
const currentTime = this.getAnimationTime(), timeLocation = this.gl.getUniformLocation(this.program, "u_time"), strengthLocation = this.gl.getUniformLocation(this.program, "u_strength"), effectTypeLocation = this.gl.getUniformLocation(this.program, "u_effectType"), resolutionLocation = this.gl.getUniformLocation(this.program, "u_resolution");
this.gl.uniform1f(timeLocation, currentTime), this.gl.uniform1f(strengthLocation, this.colorConfig.strength),
this.gl.uniform2f(resolutionLocation, this.webglCanvas.width, this.webglCanvas.height);
this.gl.uniform1i(effectTypeLocation, {
grayscale: 0,
sepia: 1
}[this.colorConfig.effectType] || 0);
}
applyCanvas2DEffect(canvas) {
if (this.colorConfig.strength <= 0) {
const outputCanvas = this.createOutputCanvas(canvas);
return outputCanvas ? outputCanvas.canvas : null;
}
if (this.canUseCSSFilter()) return this.applyCSSFilter(canvas);
const outputCanvas = this.createOutputCanvas(canvas);
if (!outputCanvas) return null;
const {ctx: ctx} = outputCanvas;
try {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height), currentTime = this.getAnimationTime();
let processedImageData;
switch (this.colorConfig.effectType) {
case "grayscale":
default:
processedImageData = this.applyGrayscaleEffect(imageData, this.colorConfig.strength, currentTime);
break;
case "sepia":
processedImageData = this.applySepiaEffect(imageData, this.colorConfig.strength, currentTime);
}
return ctx.clearRect(0, 0, canvas.width, canvas.height), ctx.putImageData(processedImageData, 0, 0),
outputCanvas.canvas;
} catch (error) {
return console.warn("Canvas 2D color effect failed:", error), null;
}
}
canUseCSSFilter() {
var _a;
return !!window.useFilterAPI && "undefined" != typeof CSS && (null === (_a = CSS.supports) || void 0 === _a ? void 0 : _a.call(CSS, "filter", "grayscale(1)"));
}
applyCSSFilter(canvas) {
try {
const outputCanvas = ImageProcessUtils.createTempCanvas(canvas.width, canvas.height), ctx = outputCanvas.getContext("2d");
if (!ctx) return null;
const currentTime = this.getAnimationTime(), dynamicStrength = ImageProcessUtils.calculateDynamicStrength(this.colorConfig.strength, currentTime);
let filterValue = "";
return "grayscale" === this.colorConfig.effectType ? filterValue = `grayscale(${Math.min(1, dynamicStrength)})` : "sepia" === this.colorConfig.effectType && (filterValue = `sepia(${Math.min(1, dynamicStrength)})`),
ctx.filter = filterValue, ctx.drawImage(canvas, 0, 0), ctx.filter = "none", outputCanvas;
} catch (error) {
return console.warn("CSS Filter API failed, falling back to pixel processing:", error),
null;
}
}
applyGrayscaleEffect(imageData, strength, time) {
const {data: data, width: width, height: height} = imageData, result = new Uint8ClampedArray(data.length), dynamicStrength = ImageProcessUtils.calculateDynamicStrength(strength, time);
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i + 1], b = data[i + 2], a = data[i + 3], gray = ImageProcessUtils.getLuminance(r, g, b);
result[i] = Math.round(ImageProcessUtils.lerp(r, gray, dynamicStrength)), result[i + 1] = Math.round(ImageProcessUtils.lerp(g, gray, dynamicStrength)),
result[i + 2] = Math.round(ImageProcessUtils.lerp(b, gray, dynamicStrength)), result[i + 3] = a;
}
return new ImageData(result, width, height);
}
applySepiaEffect(imageData, strength, time) {
const {data: data, width: width, height: height} = imageData, result = new Uint8ClampedArray(data.length), dynamicStrength = ImageProcessUtils.calculateDynamicStrength(strength, time);
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i + 1], b = data[i + 2], a = data[i + 3], [sepiaR, sepiaG, sepiaB] = ImageProcessUtils.applySepiaToPixel(r, g, b);
result[i] = Math.round(ImageProcessUtils.lerp(r, sepiaR, dynamicStrength)), result[i + 1] = Math.round(ImageProcessUtils.lerp(g, sepiaG, dynamicStrength)),
result[i + 2] = Math.round(ImageProcessUtils.lerp(b, sepiaB, dynamicStrength)),
result[i + 3] = a;
}
return new ImageData(result, width, height);
}
afterStageRender(stage, canvas) {
if (this.canUseCSSFilter() && this.colorConfig.strength > 0) {
const cssResult = this.applyCSSFilter(canvas);
if (cssResult) return cssResult;
}
return super.afterStageRender(stage, canvas);
}
}
//# sourceMappingURL=grayscale.js.map