@visactor/vrender-animate
Version:
This module provides a graph-based animation system for VRender.
121 lines (113 loc) • 9.33 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: !0
}), exports.Distortion = void 0;
const CustomEffectBase_1 = require("./base/CustomEffectBase");
class Distortion extends CustomEffectBase_1.HybridEffectBase {
constructor(from, to, duration, easing, params) {
var _a, _b, _c;
super(from, to, duration, easing, params), this.distortionConfig = {
distortionType: (null === (_a = null == params ? void 0 : params.options) || void 0 === _a ? void 0 : _a.distortionType) || "wave",
strength: (null === (_b = null == params ? void 0 : params.options) || void 0 === _b ? void 0 : _b.strength) || .3,
useWebGL: void 0 === (null === (_c = null == params ? void 0 : params.options) || void 0 === _c ? void 0 : _c.useWebGL) || params.options.useWebGL
};
}
getShaderSources() {
return {
vertex: "\n attribute vec2 a_position;\n attribute vec2 a_texCoord;\n varying vec2 v_texCoord;\n\n void main() {\n gl_Position = vec4(a_position, 0.0, 1.0);\n v_texCoord = a_texCoord;\n }\n ",
fragment: "\n precision mediump float;\n uniform sampler2D u_texture;\n uniform float u_time;\n uniform float u_strength;\n uniform int u_distortionType;\n uniform vec2 u_resolution;\n varying vec2 v_texCoord;\n\n // 波浪扭曲函数\n vec2 wave(vec2 uv, float time, float strength) {\n float waveX = sin(uv.y * 10.0 + time * 3.0) * strength * 0.1;\n float waveY = sin(uv.x * 10.0 + time * 2.0) * strength * 0.1;\n return uv + vec2(waveX, waveY);\n }\n\n // 涟漪扭曲函数\n vec2 ripple(vec2 uv, float time, float strength) {\n vec2 center = vec2(0.5, 0.5);\n float distance = length(uv - center);\n float ripple = sin(distance * 20.0 - time * 5.0) * strength * 0.1;\n vec2 direction = normalize(uv - center);\n return uv + direction * ripple;\n }\n\n // 漩涡扭曲函数\n vec2 swirl(vec2 uv, float time, float strength) {\n vec2 center = vec2(0.5, 0.5);\n vec2 delta = uv - center;\n float dist = length(delta);\n float originalAngle = atan(delta.y, delta.x);\n float rotationAngle = dist * strength * time * 2.0;\n float finalAngle = originalAngle + rotationAngle;\n return center + dist * vec2(cos(finalAngle), sin(finalAngle));\n }\n\n void main() {\n vec2 uv = v_texCoord;\n\n // 根据扭曲类型应用相应变换\n if (u_distortionType == 0) {\n uv = wave(uv, u_time, u_strength);\n } else if (u_distortionType == 1) {\n uv = ripple(uv, u_time, u_strength);\n } else if (u_distortionType == 2) {\n uv = swirl(uv, u_time, u_strength);\n }\n\n // 边界检查\n if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {\n gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\n } else {\n gl_FragColor = texture2D(u_texture, uv);\n }\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.setDistortionUniforms(),
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4), this.webglCanvas;
} finally {
this.gl.deleteTexture(texture), this.gl.deleteBuffer(vertexBuffer);
}
}
setDistortionUniforms() {
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"), distortionTypeLocation = this.gl.getUniformLocation(this.program, "u_distortionType"), resolutionLocation = this.gl.getUniformLocation(this.program, "u_resolution");
this.gl.uniform1f(timeLocation, currentTime), this.gl.uniform1f(strengthLocation, this.distortionConfig.strength),
this.gl.uniform2f(resolutionLocation, this.webglCanvas.width, this.webglCanvas.height);
this.gl.uniform1i(distortionTypeLocation, {
wave: 0,
ripple: 1,
swirl: 2
}[this.distortionConfig.distortionType] || 0);
}
applyCanvas2DEffect(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 distortedImageData;
switch (this.distortionConfig.distortionType) {
case "wave":
distortedImageData = this.applyWaveDistortion(imageData, this.distortionConfig.strength, currentTime);
break;
case "ripple":
distortedImageData = this.applyRippleDistortion(imageData, this.distortionConfig.strength, currentTime);
break;
case "swirl":
distortedImageData = this.applySwirlDistortion(imageData, this.distortionConfig.strength, currentTime);
break;
default:
distortedImageData = imageData;
}
return ctx.clearRect(0, 0, canvas.width, canvas.height), ctx.putImageData(distortedImageData, 0, 0),
outputCanvas.canvas;
} catch (error) {
return console.warn("Canvas 2D distortion effect failed:", error), null;
}
}
applyWaveDistortion(imageData, strength, time) {
const {data: data, width: width, height: height} = imageData, result = new Uint8ClampedArray(data.length);
for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
const waveX = Math.sin(.1 * y + 3 * time) * strength * 20, waveY = Math.sin(.1 * x + 2 * time) * strength * 20, sourceX = Math.round(x - waveX), sourceY = Math.round(y - waveY), targetIndex = 4 * (y * width + x);
if (sourceX >= 0 && sourceX < width && sourceY >= 0 && sourceY < height) {
const sourceIndex = 4 * (sourceY * width + sourceX);
result[targetIndex] = data[sourceIndex], result[targetIndex + 1] = data[sourceIndex + 1],
result[targetIndex + 2] = data[sourceIndex + 2], result[targetIndex + 3] = data[sourceIndex + 3];
} else result[targetIndex + 3] = 0;
}
return new ImageData(result, width, height);
}
applyRippleDistortion(imageData, strength, time) {
const {data: data, width: width, height: height} = imageData, result = new Uint8ClampedArray(data.length), centerX = width / 2, centerY = height / 2;
for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
const dx = x - centerX, dy = y - centerY, distance = Math.sqrt(dx * dx + dy * dy), ripple = Math.sin(.2 * distance - 5 * time) * strength * 10, angle = Math.atan2(dy, dx), sourceX = Math.round(x - Math.cos(angle) * ripple), sourceY = Math.round(y - Math.sin(angle) * ripple), targetIndex = 4 * (y * width + x);
if (sourceX >= 0 && sourceX < width && sourceY >= 0 && sourceY < height) {
const sourceIndex = 4 * (sourceY * width + sourceX);
result[targetIndex] = data[sourceIndex], result[targetIndex + 1] = data[sourceIndex + 1],
result[targetIndex + 2] = data[sourceIndex + 2], result[targetIndex + 3] = data[sourceIndex + 3];
} else result[targetIndex + 3] = 0;
}
return new ImageData(result, width, height);
}
applySwirlDistortion(imageData, strength, time) {
const {data: data, width: width, height: height} = imageData, result = new Uint8ClampedArray(data.length), centerX = width / 2, centerY = height / 2;
for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
const dx = x - centerX, dy = y - centerY, distance = Math.sqrt(dx * dx + dy * dy), finalAngle = Math.atan2(dy, dx) + distance * strength * time * .02, sourceX = Math.round(centerX + distance * Math.cos(finalAngle)), sourceY = Math.round(centerY + distance * Math.sin(finalAngle)), targetIndex = 4 * (y * width + x);
if (sourceX >= 0 && sourceX < width && sourceY >= 0 && sourceY < height) {
const sourceIndex = 4 * (sourceY * width + sourceX);
result[targetIndex] = data[sourceIndex], result[targetIndex + 1] = data[sourceIndex + 1],
result[targetIndex + 2] = data[sourceIndex + 2], result[targetIndex + 3] = data[sourceIndex + 3];
} else result[targetIndex + 3] = 0;
}
return new ImageData(result, width, height);
}
afterStageRender(stage, canvas) {
return this.distortionConfig.strength <= 0 ? canvas : super.afterStageRender(stage, canvas);
}
}
exports.Distortion = Distortion;
//# sourceMappingURL=distortion.js.map