UNPKG

@visactor/vrender-animate

Version:

This module provides a graph-based animation system for VRender.

127 lines (121 loc) 8.29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: !0 }), exports.Grayscale = void 0; const CustomEffectBase_1 = require("./base/CustomEffectBase"), ImageProcessUtils_1 = require("./base/ImageProcessUtils"); class Grayscale extends CustomEffectBase_1.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: ImageProcessUtils_1.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 ${ImageProcessUtils_1.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_1.ImageProcessUtils.createTempCanvas(canvas.width, canvas.height), ctx = outputCanvas.getContext("2d"); if (!ctx) return null; const currentTime = this.getAnimationTime(), dynamicStrength = ImageProcessUtils_1.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_1.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_1.ImageProcessUtils.getLuminance(r, g, b); result[i] = Math.round(ImageProcessUtils_1.ImageProcessUtils.lerp(r, gray, dynamicStrength)), result[i + 1] = Math.round(ImageProcessUtils_1.ImageProcessUtils.lerp(g, gray, dynamicStrength)), result[i + 2] = Math.round(ImageProcessUtils_1.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_1.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_1.ImageProcessUtils.applySepiaToPixel(r, g, b); result[i] = Math.round(ImageProcessUtils_1.ImageProcessUtils.lerp(r, sepiaR, dynamicStrength)), result[i + 1] = Math.round(ImageProcessUtils_1.ImageProcessUtils.lerp(g, sepiaG, dynamicStrength)), result[i + 2] = Math.round(ImageProcessUtils_1.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); } } exports.Grayscale = Grayscale; //# sourceMappingURL=grayscale.js.map