ng-cw-v12
Version:
Angular UI Component Library
422 lines (413 loc) • 24 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('three'), require('@angular/common')) :
typeof define === 'function' && define.amd ? define('ng-cw-v12/light-rays-background', ['exports', '@angular/core', 'three', '@angular/common'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global["ng-cw-v12"] = global["ng-cw-v12"] || {}, global["ng-cw-v12"]["light-rays-background"] = {}), global.ng.core, global.THREE, global.ng.common));
})(this, (function (exports, i0, THREE, common) { 'use strict';
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var i0__namespace = /*#__PURE__*/_interopNamespace(i0);
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
// ─── 顶点着色器 ───────────────────────────────────────────────────────
var vert = "\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
// ─── 片元着色器 ───────────────────────────────────────────────────────
var frag = "precision highp float;\n\nuniform float iTime;\nuniform vec2 iResolution;\n\nuniform vec2 rayPos;\nuniform vec2 rayDir;\nuniform vec3 raysColor;\nuniform float raysSpeed;\nuniform float lightSpread;\nuniform float rayLength;\nuniform float pulsating;\nuniform float fadeDistance;\nuniform float saturation;\nuniform vec2 mousePos;\nuniform float mouseInfluence;\nuniform float noiseAmount;\nuniform float distortion;\n\nvarying vec2 vUv;\n\nfloat noise(vec2 st) {\n return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);\n}\n\nfloat rayStrength(vec2 raySource, vec2 rayRefDirection, vec2 coord,\n float seedA, float seedB, float speed) {\n vec2 sourceToCoord = coord - raySource;\n vec2 dirNorm = normalize(sourceToCoord);\n float cosAngle = dot(dirNorm, rayRefDirection);\n\n float distortedAngle = cosAngle + distortion * sin(iTime * 2.0 + length(sourceToCoord) * 0.01) * 0.2;\n \n float spreadFactor = pow(max(distortedAngle, 0.0), 1.0 / max(lightSpread, 0.001));\n\n float distance = length(sourceToCoord);\n float maxDistance = iResolution.x * rayLength;\n float lengthFalloff = clamp((maxDistance - distance) / maxDistance, 0.0, 1.0);\n \n float fadeFalloff = clamp((iResolution.x * fadeDistance - distance) / (iResolution.x * fadeDistance), 0.5, 1.0);\n float pulse = pulsating > 0.5 ? (0.8 + 0.2 * sin(iTime * speed * 3.0)) : 1.0;\n\n float baseStrength = clamp(\n (0.45 + 0.15 * sin(distortedAngle * seedA + iTime * speed)) +\n (0.3 + 0.2 * cos(-distortedAngle * seedB + iTime * speed)),\n 0.0, 1.0\n );\n\n return baseStrength * lengthFalloff * fadeFalloff * spreadFactor * pulse;\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 coord = vec2(fragCoord.x, iResolution.y - fragCoord.y);\n \n vec2 finalRayDir = rayDir;\n if (mouseInfluence > 0.0) {\n vec2 mouseScreenPos = mousePos * iResolution.xy;\n vec2 mouseDirection = normalize(mouseScreenPos - rayPos);\n finalRayDir = normalize(mix(rayDir, mouseDirection, mouseInfluence));\n }\n\n vec4 rays1 = vec4(1.0) *\n rayStrength(rayPos, finalRayDir, coord, 36.2214, 21.11349,\n 1.5 * raysSpeed);\n vec4 rays2 = vec4(1.0) *\n rayStrength(rayPos, finalRayDir, coord, 22.3991, 18.0234,\n 1.1 * raysSpeed);\n\n fragColor = rays1 * 0.5 + rays2 * 0.4;\n\n if (noiseAmount > 0.0) {\n float n = noise(coord * 0.01 + iTime * 0.1);\n fragColor.rgb *= (1.0 - noiseAmount + noiseAmount * n);\n }\n\n float brightness = 1.0 - (coord.y / iResolution.y);\n fragColor.x *= 0.1 + brightness * 0.8;\n fragColor.y *= 0.3 + brightness * 0.6;\n fragColor.z *= 0.5 + brightness * 0.5;\n\n if (saturation != 1.0) {\n float gray = dot(fragColor.rgb, vec3(0.299, 0.587, 0.114));\n fragColor.rgb = mix(vec3(gray), fragColor.rgb, saturation);\n }\n\n fragColor.rgb *= raysColor;\n}\n\nvoid main() {\n vec4 color;\n mainImage(color, gl_FragCoord.xy);\n gl_FragColor = color;\n}\n";
var getAnchorAndDir = function (origin, w, h) {
var outside = 0.2;
switch (origin) {
case 'top-left':
return { anchor: [0, -outside * h], dir: [0, 1] };
case 'top-right':
return { anchor: [w, -outside * h], dir: [0, 1] };
case 'left':
return { anchor: [-outside * w, 0.5 * h], dir: [1, 0] };
case 'right':
return { anchor: [(1 + outside) * w, 0.5 * h], dir: [-1, 0] };
case 'bottom-left':
return { anchor: [0, (1 + outside) * h], dir: [0, -1] };
case 'bottom-center':
return { anchor: [0.5 * w, (1 + outside) * h], dir: [0, -1] };
case 'bottom-right':
return { anchor: [w, (1 + outside) * h], dir: [0, -1] };
default: // "top-center"
return { anchor: [0.5 * w, -outside * h], dir: [0, 1] };
}
};
var LightRaysBackgroundComponent = /** @class */ (function () {
function LightRaysBackgroundComponent(ngZone) {
var _this = this;
this.ngZone = ngZone;
/** 光线发射源位置。可选项: 'top-center', 'top-left', 'top-right', 'right', 'left', 'bottom-center', 'bottom-right', 'bottom-left' */
this.ncRaysOrigin = 'top-center';
/** 射线颜色,十六进制格式 */
this.ncRaysColor = '#ffffff';
/** 射线动画速度(0.1-3) */
this.ncRaysSpeed = 1;
/** 光线扩散宽度(0.1-2) */
this.ncLightSpread = 1;
/** 光线的最大长度/范围(0.5-3)*/
this.ncRayLength = 2;
/** 启用脉动(闪烁)动画效果 */
this._pulsating = false;
/** 射线从源头开始淡出的距离(0.5-2)*/
this.ncFadeDistance = 1.0;
/** 颜色饱和度(0-1)*/
this.ncSaturation = 1.0;
/** 让光线向鼠标光标方向旋转 */
this._followMouse = true;
/** 鼠标对光线的影响程度(0-1)*/
this.ncMouseInfluence = 0.1;
/** 给光线添加噪点/颗粒感(0-1)*/
this.ncNoiseAmount = 0.0;
/** 对光线应用波形扭曲(0-1)*/
this.ncDistortion = 0.0;
/** 背景颜色 */
this.ncBgColor = 'black';
this.renderer = null;
this.material = null;
this.scene = null;
this.camera = null;
this.rafId = null;
this.resizeRafId = null;
this.isVisible = true;
this.running = false;
this.mouse = { x: 0.5, y: 0.5 };
this.smoothMouse = { x: 0.5, y: 0.5 };
this._onVisibility = function () {
if (document.hidden) {
_this.pause();
}
else if (_this.isVisible) {
_this.ngZone.runOutsideAngular(function () {
_this.start();
});
}
};
this.loop = function (t) {
if (!_this.running)
return;
if (_this.material && _this.renderer && _this.scene && _this.camera) {
_this.material.uniforms['iTime'].value = t * 0.001;
if (_this.ncFollowMouse && _this.ncMouseInfluence > 0.0) {
var smoothing = 0.92;
_this.smoothMouse.x = _this.smoothMouse.x * smoothing + _this.mouse.x * (1 - smoothing);
_this.smoothMouse.y = _this.smoothMouse.y * smoothing + _this.mouse.y * (1 - smoothing);
_this.material.uniforms['mousePos'].value.set(_this.smoothMouse.x, _this.smoothMouse.y);
}
try {
_this.renderer.render(_this.scene, _this.camera);
}
catch (error) {
console.warn('WebGL rendering error:', error);
return;
}
}
_this.rafId = requestAnimationFrame(_this.loop);
};
}
Object.defineProperty(LightRaysBackgroundComponent.prototype, "ncPulsating", {
get: function () {
return this._pulsating;
},
set: function (val) {
this._pulsating = val !== null && val !== undefined && val !== false && val !== 'false';
},
enumerable: false,
configurable: true
});
Object.defineProperty(LightRaysBackgroundComponent.prototype, "ncFollowMouse", {
get: function () {
return this._followMouse;
},
set: function (val) {
this._followMouse = val !== null && val !== undefined && val !== false && val !== 'false';
},
enumerable: false,
configurable: true
});
LightRaysBackgroundComponent.prototype.ngOnInit = function () { };
LightRaysBackgroundComponent.prototype.ngAfterViewInit = function () {
var _this = this;
this.ngZone.runOutsideAngular(function () {
_this.initWebGL();
});
};
LightRaysBackgroundComponent.prototype.ngOnDestroy = function () {
this.cleanup();
};
LightRaysBackgroundComponent.prototype.ngOnChanges = function (changes) {
var _this = this;
if (!this.material)
return;
var u = this.material.uniforms;
if (changes['ncRaysColor']) {
u['raysColor'].value.set(this.ncRaysColor);
}
if (changes['ncRaysSpeed'])
u['raysSpeed'].value = this.ncRaysSpeed;
if (changes['ncLightSpread'])
u['lightSpread'].value = this.ncLightSpread;
if (changes['ncRayLength'])
u['rayLength'].value = this.ncRayLength;
if (changes['ncPulsating'])
u['pulsating'].value = this.ncPulsating ? 1.0 : 0.0;
if (changes['ncFadeDistance'])
u['fadeDistance'].value = this.ncFadeDistance;
if (changes['ncSaturation'])
u['saturation'].value = this.ncSaturation;
if (changes['ncMouseInfluence'])
u['mouseInfluence'].value = this.ncMouseInfluence;
if (changes['ncNoiseAmount'])
u['noiseAmount'].value = this.ncNoiseAmount;
if (changes['ncDistortion'])
u['distortion'].value = this.ncDistortion;
if (changes['ncRaysOrigin']) {
this.ngZone.runOutsideAngular(function () {
_this.updatePlacement();
});
}
};
LightRaysBackgroundComponent.prototype.initWebGL = function () {
var container = this.containerRef.nativeElement;
this.renderer = new THREE__namespace.WebGLRenderer({ alpha: true });
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
this.renderer.domElement.style.width = '100%';
this.renderer.domElement.style.height = '100%';
this.renderer.domElement.style.display = 'block';
container.appendChild(this.renderer.domElement);
this.scene = new THREE__namespace.Scene();
this.camera = new THREE__namespace.OrthographicCamera(-1, 1, 1, -1, 0, 1);
var geometry = new THREE__namespace.PlaneGeometry(2, 2);
this.material = new THREE__namespace.ShaderMaterial({
vertexShader: vert,
fragmentShader: frag,
uniforms: {
iTime: { value: 0 },
iResolution: { value: new THREE__namespace.Vector2(1, 1) },
rayPos: { value: new THREE__namespace.Vector2(0, 0) },
rayDir: { value: new THREE__namespace.Vector2(0, 1) },
raysColor: { value: new THREE__namespace.Color(this.ncRaysColor) },
raysSpeed: { value: this.ncRaysSpeed },
lightSpread: { value: this.ncLightSpread },
rayLength: { value: this.ncRayLength },
pulsating: { value: this.ncPulsating ? 1.0 : 0.0 },
fadeDistance: { value: this.ncFadeDistance },
saturation: { value: this.ncSaturation },
mousePos: { value: new THREE__namespace.Vector2(0.5, 0.5) },
mouseInfluence: { value: this.ncMouseInfluence },
noiseAmount: { value: this.ncNoiseAmount },
distortion: { value: this.ncDistortion }
},
transparent: true,
depthWrite: false
});
var mesh = new THREE__namespace.Mesh(geometry, this.material);
this.scene.add(mesh);
this.setupResizeObserver(container);
this.setupIntersectionObserver(container);
// 初始化一次配置
this.updatePlacement();
document.addEventListener('visibilitychange', this._onVisibility);
};
LightRaysBackgroundComponent.prototype.updatePlacement = function () {
var _a;
if (!((_a = this.containerRef) === null || _a === void 0 ? void 0 : _a.nativeElement) || !this.renderer || !this.material)
return;
var container = this.containerRef.nativeElement;
var wCSS = container.clientWidth;
var hCSS = container.clientHeight;
if (wCSS === 0 || hCSS === 0)
return;
this.renderer.setSize(wCSS, hCSS, false); // false 因为我们使用 CSS 100% 缩放
var dpr = this.renderer.getPixelRatio();
var w = wCSS * dpr;
var h = hCSS * dpr;
this.material.uniforms['iResolution'].value.set(w, h);
var _c = getAnchorAndDir(this.ncRaysOrigin, w, h), anchor = _c.anchor, dir = _c.dir;
this.material.uniforms['rayPos'].value.set(anchor[0], anchor[1]);
this.material.uniforms['rayDir'].value.set(dir[0], dir[1]);
};
LightRaysBackgroundComponent.prototype.start = function () {
if (this.running)
return;
this.running = true;
this.rafId = requestAnimationFrame(this.loop);
};
LightRaysBackgroundComponent.prototype.pause = function () {
this.running = false;
if (this.rafId !== null) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
};
LightRaysBackgroundComponent.prototype.setupResizeObserver = function (container) {
var _this = this;
this.resizeObserver = new ResizeObserver(function () {
if (!_this.renderer)
return;
if (_this.resizeRafId !== null)
cancelAnimationFrame(_this.resizeRafId);
_this.ngZone.runOutsideAngular(function () {
_this.resizeRafId = requestAnimationFrame(function () {
_this.updatePlacement();
_this.resizeRafId = null;
});
});
});
this.resizeObserver.observe(container);
};
LightRaysBackgroundComponent.prototype.setupIntersectionObserver = function (container) {
var _this = this;
this.intersectionObserver = new IntersectionObserver(function (entries) {
var entry = entries[0];
_this.isVisible = entry.isIntersecting && entry.intersectionRatio > 0;
if (_this.isVisible && !document.hidden) {
_this.ngZone.runOutsideAngular(function () {
_this.start();
});
}
else {
_this.pause();
}
}, { threshold: [0, 0.01, 0.1] });
this.intersectionObserver.observe(container);
};
LightRaysBackgroundComponent.prototype.cleanup = function () {
var _a, _b;
this.pause();
if (this.resizeRafId !== null) {
cancelAnimationFrame(this.resizeRafId);
this.resizeRafId = null;
}
document.removeEventListener('visibilitychange', this._onVisibility);
try {
(_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
}
catch (e) {
void 0;
}
try {
(_b = this.intersectionObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
}
catch (e) {
void 0;
}
if (this.renderer) {
try {
var canvas = this.renderer.domElement;
this.renderer.dispose();
if (canvas && canvas.parentNode) {
canvas.parentNode.removeChild(canvas);
}
}
catch (error) {
console.warn('Error during WebGL cleanup:', error);
}
this.renderer = null;
}
if (this.material) {
this.material.dispose();
this.material = null;
}
this.scene = null;
this.camera = null;
};
LightRaysBackgroundComponent.prototype.onMouseMove = function (event) {
var _a;
if (!this.ncFollowMouse || !((_a = this.containerRef) === null || _a === void 0 ? void 0 : _a.nativeElement))
return;
var rect = this.containerRef.nativeElement.getBoundingClientRect();
var x = (event.clientX - rect.left) / rect.width;
var y = (event.clientY - rect.top) / rect.height;
this.mouse = { x: x, y: y };
};
return LightRaysBackgroundComponent;
}());
LightRaysBackgroundComponent.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0__namespace, type: LightRaysBackgroundComponent, deps: [{ token: i0__namespace.NgZone }], target: i0__namespace.ɵɵFactoryTarget.Component });
LightRaysBackgroundComponent.ɵcmp = i0__namespace.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: LightRaysBackgroundComponent, selector: "nc-light-rays-background", inputs: { ncRaysOrigin: "ncRaysOrigin", ncRaysColor: "ncRaysColor", ncRaysSpeed: "ncRaysSpeed", ncLightSpread: "ncLightSpread", ncRayLength: "ncRayLength", ncPulsating: "ncPulsating", ncFadeDistance: "ncFadeDistance", ncSaturation: "ncSaturation", ncFollowMouse: "ncFollowMouse", ncMouseInfluence: "ncMouseInfluence", ncNoiseAmount: "ncNoiseAmount", ncDistortion: "ncDistortion", ncBgColor: "ncBgColor" }, host: { listeners: { "mousemove": "onMouseMove($event)" } }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0__namespace, template: "<div #container class=\"nc-light-rays-canvas-container\" [style.background-color]=\"ncBgColor\"></div>\r\n<div class=\"nc-content-wrapper\">\r\n <ng-content></ng-content>\r\n</div>", styles: [":host{display:block;position:relative;width:100%;height:100%;overflow:hidden}.nc-light-rays-canvas-container{position:absolute;inset:0;z-index:0;pointer-events:none}.nc-content-wrapper{position:relative;z-index:1;width:100%;height:100%}\n"] });
i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0__namespace, type: LightRaysBackgroundComponent, decorators: [{
type: i0.Component,
args: [{
selector: 'nc-light-rays-background',
templateUrl: './light-rays-background.component.html',
styleUrls: ['./light-rays-background.component.less']
}]
}], ctorParameters: function () { return [{ type: i0__namespace.NgZone }]; }, propDecorators: { containerRef: [{
type: i0.ViewChild,
args: ['container', { static: true }]
}], ncRaysOrigin: [{
type: i0.Input
}], ncRaysColor: [{
type: i0.Input
}], ncRaysSpeed: [{
type: i0.Input
}], ncLightSpread: [{
type: i0.Input
}], ncRayLength: [{
type: i0.Input
}], ncPulsating: [{
type: i0.Input
}], ncFadeDistance: [{
type: i0.Input
}], ncSaturation: [{
type: i0.Input
}], ncFollowMouse: [{
type: i0.Input
}], ncMouseInfluence: [{
type: i0.Input
}], ncNoiseAmount: [{
type: i0.Input
}], ncDistortion: [{
type: i0.Input
}], ncBgColor: [{
type: i0.Input
}], onMouseMove: [{
type: i0.HostListener,
args: ['mousemove', ['$event']]
}] } });
var NcLightRaysBackgroundModule = /** @class */ (function () {
function NcLightRaysBackgroundModule() {
}
return NcLightRaysBackgroundModule;
}());
NcLightRaysBackgroundModule.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0__namespace, type: NcLightRaysBackgroundModule, deps: [], target: i0__namespace.ɵɵFactoryTarget.NgModule });
NcLightRaysBackgroundModule.ɵmod = i0__namespace.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0__namespace, type: NcLightRaysBackgroundModule, declarations: [LightRaysBackgroundComponent], imports: [common.CommonModule], exports: [LightRaysBackgroundComponent] });
NcLightRaysBackgroundModule.ɵinj = i0__namespace.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0__namespace, type: NcLightRaysBackgroundModule, imports: [[
common.CommonModule
]] });
i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0__namespace, type: NcLightRaysBackgroundModule, decorators: [{
type: i0.NgModule,
args: [{
declarations: [
LightRaysBackgroundComponent
],
imports: [
common.CommonModule
],
exports: [
LightRaysBackgroundComponent
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
exports.LightRaysBackgroundComponent = LightRaysBackgroundComponent;
exports.NcLightRaysBackgroundModule = NcLightRaysBackgroundModule;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=ng-cw-v12-light-rays-background.umd.js.map