ng-cw-v12
Version:
Angular UI Component Library
308 lines (299 loc) • 18.7 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/threads-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"]["threads-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 vertexShader = "\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 1.0);\n}\n";
var fragmentShader = "\nprecision highp float;\n\nuniform float iTime;\nuniform vec3 iResolution;\nuniform vec3 uColor;\nuniform float uAmplitude;\nuniform float uDistance;\nuniform vec2 uMouse;\n\n#define PI 3.1415926538\n\nconst int u_line_count = 40;\nconst float u_line_width = 7.0;\nconst float u_line_blur = 10.0;\n\nfloat Perlin2D(vec2 P) {\n vec2 Pi = floor(P);\n vec4 Pf_Pfmin1 = P.xyxy - vec4(Pi, Pi + 1.0);\n vec4 Pt = vec4(Pi.xy, Pi.xy + 1.0);\n Pt = Pt - floor(Pt * (1.0 / 71.0)) * 71.0;\n Pt += vec2(26.0, 161.0).xyxy;\n Pt *= Pt;\n Pt = Pt.xzxz * Pt.yyww;\n vec4 hash_x = fract(Pt * (1.0 / 951.135664));\n vec4 hash_y = fract(Pt * (1.0 / 642.949883));\n vec4 grad_x = hash_x - 0.49999;\n vec4 grad_y = hash_y - 0.49999;\n vec4 grad_results = inversesqrt(grad_x * grad_x + grad_y * grad_y)\n * (grad_x * Pf_Pfmin1.xzxz + grad_y * Pf_Pfmin1.yyww);\n grad_results *= 1.4142135623730950;\n vec2 blend = Pf_Pfmin1.xy * Pf_Pfmin1.xy * Pf_Pfmin1.xy\n * (Pf_Pfmin1.xy * (Pf_Pfmin1.xy * 6.0 - 15.0) + 10.0);\n vec4 blend2 = vec4(blend, vec2(1.0 - blend));\n return dot(grad_results, blend2.zxzx * blend2.wwyy);\n}\n\nfloat pixel(float count, vec2 resolution) {\n return (1.0 / max(resolution.x, resolution.y)) * count;\n}\n\nfloat lineFn(vec2 st, float width, float perc, float offset, vec2 mouse, float time, float amplitude, float distance) {\n float split_offset = (perc * 0.4);\n float split_point = 0.1 + split_offset;\n\n float amplitude_normal = smoothstep(split_point, 0.7, st.x);\n float amplitude_strength = 0.5;\n float finalAmplitude = amplitude_normal * amplitude_strength\n * amplitude * (1.0 + (mouse.y - 0.5) * 0.2);\n\n float time_scaled = time / 10.0 + (mouse.x - 0.5) * 1.0;\n float blur = smoothstep(split_point, split_point + 0.05, st.x) * perc;\n\n float xnoise = mix(\n Perlin2D(vec2(time_scaled, st.x + perc) * 2.5),\n Perlin2D(vec2(time_scaled, st.x + time_scaled) * 3.5) / 1.5,\n st.x * 0.3\n );\n\n float y = 0.5 + (perc - 0.5) * distance + xnoise / 2.0 * finalAmplitude;\n\n float line_start = smoothstep(\n y + (width / 2.0) + (u_line_blur * pixel(1.0, iResolution.xy) * blur),\n y,\n st.y\n );\n\n float line_end = smoothstep(\n y,\n y - (width / 2.0) - (u_line_blur * pixel(1.0, iResolution.xy) * blur),\n st.y\n );\n\n return clamp(\n (line_start - line_end) * (1.0 - smoothstep(0.0, 1.0, pow(perc, 0.3))),\n 0.0,\n 1.0\n );\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n vec2 uv = fragCoord / iResolution.xy;\n\n float line_strength = 1.0;\n for (int i = 0; i < u_line_count; i++) {\n float p = float(i) / float(u_line_count);\n line_strength *= (1.0 - lineFn(\n uv,\n u_line_width * pixel(1.0, iResolution.xy) * (1.0 - p),\n p,\n (PI * 1.0) * p,\n uMouse,\n iTime,\n uAmplitude,\n uDistance\n ));\n }\n\n float colorVal = 1.0 - line_strength;\n fragColor = vec4(uColor * colorVal, colorVal);\n}\n\nvoid main() {\n mainImage(gl_FragColor, gl_FragCoord.xy);\n}\n";
var ThreadsBackgroundComponent = /** @class */ (function () {
function ThreadsBackgroundComponent(ngZone) {
var _this = this;
this.ngZone = ngZone;
/** 颜色 [r, g, b] 数组(0-1) */
this.ncColor = [1, 1, 1];
/** 振幅大小(0-5) */
this.ncAmplitude = 1;
/** 线条距离(0-2) */
this.ncDistance = 0;
/** 背景颜色 */
this.ncBgColor = 'black';
/** 是否启用鼠标交互 */
this._enableMouseInteraction = false;
this.animationFrameId = null;
this.isVisible = true;
this.isRunning = false;
this.currentMouse = new THREE__namespace.Vector2(0.5, 0.5);
this.targetMouse = new THREE__namespace.Vector2(0.5, 0.5);
this.onVisibilityChange = function () {
if (document.hidden) {
_this.pause();
}
else if (_this.isVisible) {
_this.ngZone.runOutsideAngular(function () {
_this.start();
});
}
};
this.update = function (t) {
if (!_this.isRunning)
return;
if (_this.ncEnableMouseInteraction) {
var smoothing = 0.05;
_this.currentMouse.x += smoothing * (_this.targetMouse.x - _this.currentMouse.x);
_this.currentMouse.y += smoothing * (_this.targetMouse.y - _this.currentMouse.y);
_this.material.uniforms['uMouse'].value.copy(_this.currentMouse);
}
else {
_this.material.uniforms['uMouse'].value.set(0.5, 0.5);
}
_this.material.uniforms['iTime'].value = t * 0.001;
_this.renderer.render(_this.scene, _this.camera);
_this.animationFrameId = requestAnimationFrame(_this.update);
};
}
Object.defineProperty(ThreadsBackgroundComponent.prototype, "ncEnableMouseInteraction", {
get: function () {
return this._enableMouseInteraction;
},
set: function (val) {
this._enableMouseInteraction = val !== null && val !== undefined && val !== false && val !== 'false';
},
enumerable: false,
configurable: true
});
ThreadsBackgroundComponent.prototype.ngOnInit = function () { };
ThreadsBackgroundComponent.prototype.ngAfterViewInit = function () {
var _this = this;
this.ngZone.runOutsideAngular(function () {
_this.initWebGL();
});
};
ThreadsBackgroundComponent.prototype.ngOnDestroy = function () {
this.cleanup();
};
ThreadsBackgroundComponent.prototype.ngOnChanges = function (changes) {
if (this.material) {
if (changes['ncColor']) {
this.material.uniforms['uColor'].value.copy(this.getThreeColor(this.ncColor));
}
if (changes['ncAmplitude']) {
this.material.uniforms['uAmplitude'].value = this.ncAmplitude;
}
if (changes['ncDistance']) {
this.material.uniforms['uDistance'].value = this.ncDistance;
}
}
};
ThreadsBackgroundComponent.prototype.getThreeColor = function (color) {
if (Array.isArray(color)) {
return new THREE__namespace.Color(color[0], color[1], color[2]);
}
return new THREE__namespace.Color(color);
};
ThreadsBackgroundComponent.prototype.initWebGL = function () {
var container = this.containerRef.nativeElement;
// 初始化渲染器
this.renderer = new THREE__namespace.WebGLRenderer({ alpha: true, antialias: false });
this.renderer.setClearColor(0x000000, 0);
// 采用与 Threads.jsx 中 blendFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA) 一致的默认混合方式
container.appendChild(this.renderer.domElement);
this.scene = new THREE__namespace.Scene();
this.camera = new THREE__namespace.OrthographicCamera(-1, 1, 1, -1, 0, 1);
this.geometry = new THREE__namespace.PlaneGeometry(2, 2);
// 初始分辨率设定,后续由 resize 更新
var canvasWidth = container.clientWidth || 1;
var canvasHeight = container.clientHeight || 1;
this.material = new THREE__namespace.ShaderMaterial({
vertexShader: vertexShader,
fragmentShader: fragmentShader,
transparent: true,
uniforms: {
iTime: { value: 0 },
iResolution: { value: new THREE__namespace.Vector3(canvasWidth, canvasHeight, canvasWidth / canvasHeight) },
uColor: { value: this.getThreeColor(this.ncColor) },
uAmplitude: { value: this.ncAmplitude },
uDistance: { value: this.ncDistance },
uMouse: { value: new THREE__namespace.Vector2(0.5, 0.5) }
}
});
this.mesh = new THREE__namespace.Mesh(this.geometry, this.material);
this.scene.add(this.mesh);
this.setupResizeObserver(container);
this.setupIntersectionObserver(container);
this.resize();
this.start();
};
ThreadsBackgroundComponent.prototype.setupResizeObserver = function (container) {
var _this = this;
this.resizeObserver = new ResizeObserver(function () {
_this.ngZone.runOutsideAngular(function () {
_this.resize();
});
});
this.resizeObserver.observe(container);
};
ThreadsBackgroundComponent.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);
document.addEventListener('visibilitychange', this.onVisibilityChange);
};
ThreadsBackgroundComponent.prototype.resize = function () {
if (!this.containerRef || !this.renderer)
return;
var container = this.containerRef.nativeElement;
var width = container.clientWidth;
var height = container.clientHeight;
this.renderer.setSize(width, height);
// 可选设置 pixelRatio,如不需要极高精度可保留 1 提高性能
// this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
this.material.uniforms['iResolution'].value.set(width, height, (width / Math.max(height, 1)));
};
ThreadsBackgroundComponent.prototype.start = function () {
if (this.isRunning)
return;
this.isRunning = true;
this.animationFrameId = requestAnimationFrame(this.update);
};
ThreadsBackgroundComponent.prototype.pause = function () {
this.isRunning = false;
if (this.animationFrameId !== null) {
cancelAnimationFrame(this.animationFrameId);
this.animationFrameId = null;
}
};
ThreadsBackgroundComponent.prototype.cleanup = function () {
var _a, _b;
this.pause();
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;
}
document.removeEventListener('visibilitychange', this.onVisibilityChange);
if (this.renderer) {
var canvas = this.renderer.domElement;
if (canvas && canvas.parentNode)
canvas.parentNode.removeChild(canvas);
this.renderer.dispose();
this.renderer.forceContextLoss();
}
if (this.geometry)
this.geometry.dispose();
if (this.material)
this.material.dispose();
};
ThreadsBackgroundComponent.prototype.onMouseMove = function (event) {
if (!this.ncEnableMouseInteraction || !this.containerRef)
return;
var container = this.containerRef.nativeElement;
var rect = container.getBoundingClientRect();
var x = (event.clientX - rect.left) / rect.width;
var y = 1.0 - (event.clientY - rect.top) / rect.height;
this.targetMouse.set(x, y);
};
ThreadsBackgroundComponent.prototype.onMouseLeave = function () {
if (!this.ncEnableMouseInteraction)
return;
this.targetMouse.set(0.5, 0.5);
};
return ThreadsBackgroundComponent;
}());
ThreadsBackgroundComponent.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0__namespace, type: ThreadsBackgroundComponent, deps: [{ token: i0__namespace.NgZone }], target: i0__namespace.ɵɵFactoryTarget.Component });
ThreadsBackgroundComponent.ɵcmp = i0__namespace.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: ThreadsBackgroundComponent, selector: "nc-threads-background", inputs: { ncColor: "ncColor", ncAmplitude: "ncAmplitude", ncDistance: "ncDistance", ncBgColor: "ncBgColor", ncEnableMouseInteraction: "ncEnableMouseInteraction" }, host: { listeners: { "mousemove": "onMouseMove($event)", "mouseleave": "onMouseLeave()" } }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0__namespace, template: "<div #container class=\"nc-threads-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-threads-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: ThreadsBackgroundComponent, decorators: [{
type: i0.Component,
args: [{
selector: 'nc-threads-background',
templateUrl: './threads-background.component.html',
styleUrls: ['./threads-background.component.less']
}]
}], ctorParameters: function () { return [{ type: i0__namespace.NgZone }]; }, propDecorators: { containerRef: [{
type: i0.ViewChild,
args: ['container', { static: true }]
}], ncColor: [{
type: i0.Input
}], ncAmplitude: [{
type: i0.Input
}], ncDistance: [{
type: i0.Input
}], ncBgColor: [{
type: i0.Input
}], ncEnableMouseInteraction: [{
type: i0.Input
}], onMouseMove: [{
type: i0.HostListener,
args: ['mousemove', ['$event']]
}], onMouseLeave: [{
type: i0.HostListener,
args: ['mouseleave']
}] } });
var NcThreadsBackgroundModule = /** @class */ (function () {
function NcThreadsBackgroundModule() {
}
return NcThreadsBackgroundModule;
}());
NcThreadsBackgroundModule.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0__namespace, type: NcThreadsBackgroundModule, deps: [], target: i0__namespace.ɵɵFactoryTarget.NgModule });
NcThreadsBackgroundModule.ɵmod = i0__namespace.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0__namespace, type: NcThreadsBackgroundModule, declarations: [ThreadsBackgroundComponent], imports: [common.CommonModule], exports: [ThreadsBackgroundComponent] });
NcThreadsBackgroundModule.ɵinj = i0__namespace.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0__namespace, type: NcThreadsBackgroundModule, imports: [[
common.CommonModule
]] });
i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0__namespace, type: NcThreadsBackgroundModule, decorators: [{
type: i0.NgModule,
args: [{
declarations: [
ThreadsBackgroundComponent
],
imports: [
common.CommonModule
],
exports: [
ThreadsBackgroundComponent
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
exports.NcThreadsBackgroundModule = NcThreadsBackgroundModule;
exports.ThreadsBackgroundComponent = ThreadsBackgroundComponent;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=ng-cw-v12-threads-background.umd.js.map