UNPKG

ng-cw-v12

Version:

Angular UI Component Library

308 lines (299 loc) 18.7 kB
(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