UNPKG

ng-cw-v12

Version:

Angular UI Component Library

261 lines (251 loc) 11.7 kB
import * as i0 from '@angular/core'; import { Component, ViewChild, Input, HostListener, NgModule } from '@angular/core'; import * as THREE from 'three'; import { CommonModule } from '@angular/common'; const vertexShader = ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `; const fragmentShader = ` precision highp float; uniform float uTime; uniform vec3 uResolution; uniform vec3 uBaseColor; uniform float uAmplitude; uniform float uFrequencyX; uniform float uFrequencyY; uniform vec2 uMouse; varying vec2 vUv; vec4 renderImage(vec2 uvCoord) { vec2 fragCoord = uvCoord * uResolution.xy; vec2 uv = (2.0 * fragCoord - uResolution.xy) / min(uResolution.x, uResolution.y); for (float i = 1.0; i < 10.0; i++){ uv.x += uAmplitude / i * cos(i * uFrequencyX * uv.y + uTime + uMouse.x * 3.14159); uv.y += uAmplitude / i * cos(i * uFrequencyY * uv.x + uTime + uMouse.y * 3.14159); } vec2 diff = (uvCoord - uMouse); float dist = length(diff); float falloff = exp(-dist * 20.0); float ripple = sin(10.0 * dist - uTime * 2.0) * 0.03; uv += (diff / (dist + 0.0001)) * ripple * falloff; vec3 color = uBaseColor / abs(sin(uTime - uv.y - uv.x)); return vec4(color, 1.0); } void main() { vec4 col = vec4(0.0); int samples = 0; for (int i = -1; i <= 1; i++){ for (int j = -1; j <= 1; j++){ vec2 offset = vec2(float(i), float(j)) * (1.0 / min(uResolution.x, uResolution.y)); col += renderImage(vUv + offset); samples++; } } gl_FragColor = col / float(samples); } `; class LiquidChromeBackgroundComponent { constructor(ngZone) { this.ngZone = ngZone; /** 基础颜色数组 [r, g, b](0-1) */ this.ncBaseColor = [0.1, 0.1, 0.1]; /** 动画运行速度(0-5) */ this.ncSpeed = 0.2; /** 波动幅度(0.1-1) */ this.ncAmplitude = 0.3; /** X轴波频 */ this.ncFrequencyX = 3; /** Y轴波频 */ this.ncFrequencyY = 3; /** 是否允许鼠标/触屏交互 */ this._interactive = true; this.animationId = null; this.mouse = new THREE.Vector2(0, 0); this.update = (t) => { this.animationId = requestAnimationFrame(this.update); if (this.material) { this.material.uniforms['uTime'].value = t * 0.001 * this.ncSpeed; } this.renderer.render(this.scene, this.camera); }; } set ncInteractive(value) { this._interactive = value !== null && value !== undefined && value !== false && value !== 'false'; } get ncInteractive() { return this._interactive; } ngOnInit() { } ngAfterViewInit() { this.initWebGL(); } ngOnDestroy() { this.cleanup(); } ngOnChanges(changes) { if (!this.material) return; if (changes['ncBaseColor'] && changes['ncBaseColor'].currentValue) { this.material.uniforms['uBaseColor'].value.fromArray(this.ncBaseColor); } if (changes['ncAmplitude'] !== undefined) { this.material.uniforms['uAmplitude'].value = this.ncAmplitude; } if (changes['ncFrequencyX'] !== undefined) { this.material.uniforms['uFrequencyX'].value = this.ncFrequencyX; } if (changes['ncFrequencyY'] !== undefined) { this.material.uniforms['uFrequencyY'].value = this.ncFrequencyY; } // ncInteractive handled via HostListener check } initWebGL() { const container = this.containerRef.nativeElement; this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); // 原版 jsx 清除颜色为白色 this.renderer.setClearColor(0xffffff, 1); this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); container.appendChild(this.renderer.domElement); this.scene = new THREE.Scene(); // 正交相机渲染全屏 Plane this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 10); this.camera.position.z = 1; const geometry = new THREE.PlaneGeometry(2, 2); this.material = new THREE.ShaderMaterial({ vertexShader, fragmentShader, uniforms: { uTime: { value: 0 }, uResolution: { value: new THREE.Vector3() }, uBaseColor: { value: new THREE.Vector3().fromArray(this.ncBaseColor) }, uAmplitude: { value: this.ncAmplitude }, uFrequencyX: { value: this.ncFrequencyX }, uFrequencyY: { value: this.ncFrequencyY }, uMouse: { value: this.mouse } } }); this.mesh = new THREE.Mesh(geometry, this.material); this.scene.add(this.mesh); this.resizeObserver = new ResizeObserver(() => this.resize()); this.resizeObserver.observe(container); this.resize(); // Interaction handled via HostListener // 提升性能,不让 requestAnimationFrame 触发 Angular 变更检测 this.ngZone.runOutsideAngular(() => { this.update(0); }); } resize() { if (!this.containerRef || !this.renderer) return; const container = this.containerRef.nativeElement; const width = container.offsetWidth; const height = container.offsetHeight; this.renderer.setSize(width, height); this.material.uniforms['uResolution'].value.set(width, height, width / height); } onMouseMove(event) { if (!this.ncInteractive || !this.containerRef) return; const container = this.containerRef.nativeElement; const rect = container.getBoundingClientRect(); const x = (event.clientX - rect.left) / rect.width; const y = 1 - (event.clientY - rect.top) / rect.height; this.mouse.set(x, y); } onTouchMove(event) { if (!this.ncInteractive || !this.containerRef) return; if (event.touches.length > 0) { const touch = event.touches[0]; const container = this.containerRef.nativeElement; const rect = container.getBoundingClientRect(); const x = (touch.clientX - rect.left) / rect.width; const y = 1 - (touch.clientY - rect.top) / rect.height; this.mouse.set(x, y); } } cleanup() { var _a, _b; if (this.animationId !== null) { cancelAnimationFrame(this.animationId); this.animationId = null; } if (this.resizeObserver) { this.resizeObserver.disconnect(); } if (this.mesh) { this.mesh.geometry.dispose(); this.mesh.material.dispose(); this.scene.remove(this.mesh); } if (this.renderer) { this.renderer.dispose(); const dom = this.renderer.domElement; if (dom && dom.parentElement) { dom.parentElement.removeChild(dom); } (_b = (_a = this.renderer.getContext()) === null || _a === void 0 ? void 0 : _a.getExtension('WEBGL_lose_context')) === null || _b === void 0 ? void 0 : _b.loseContext(); } } } LiquidChromeBackgroundComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: LiquidChromeBackgroundComponent, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); LiquidChromeBackgroundComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: LiquidChromeBackgroundComponent, selector: "nc-liquid-chrome-background", inputs: { ncBaseColor: "ncBaseColor", ncSpeed: "ncSpeed", ncAmplitude: "ncAmplitude", ncFrequencyX: "ncFrequencyX", ncFrequencyY: "ncFrequencyY", ncInteractive: "ncInteractive" }, host: { listeners: { "mousemove": "onMouseMove($event)", "touchmove": "onTouchMove($event)" } }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div #container class=\"nc-liquid-chrome-canvas-container\"></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-liquid-chrome-canvas-container{position:absolute;inset:0;z-index:0}.nc-content-wrapper{position:relative;z-index:1;width:100%;height:100%}\n"] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: LiquidChromeBackgroundComponent, decorators: [{ type: Component, args: [{ selector: 'nc-liquid-chrome-background', templateUrl: './liquid-chrome-background.component.html', styleUrls: ['./liquid-chrome-background.component.less'] }] }], ctorParameters: function () { return [{ type: i0.NgZone }]; }, propDecorators: { containerRef: [{ type: ViewChild, args: ['container', { static: true }] }], ncBaseColor: [{ type: Input }], ncSpeed: [{ type: Input }], ncAmplitude: [{ type: Input }], ncFrequencyX: [{ type: Input }], ncFrequencyY: [{ type: Input }], ncInteractive: [{ type: Input }], onMouseMove: [{ type: HostListener, args: ['mousemove', ['$event']] }], onTouchMove: [{ type: HostListener, args: ['touchmove', ['$event']] }] } }); class NcLiquidChromeBackgroundModule { } NcLiquidChromeBackgroundModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcLiquidChromeBackgroundModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); NcLiquidChromeBackgroundModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcLiquidChromeBackgroundModule, declarations: [LiquidChromeBackgroundComponent], imports: [CommonModule], exports: [LiquidChromeBackgroundComponent] }); NcLiquidChromeBackgroundModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcLiquidChromeBackgroundModule, imports: [[ CommonModule ]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcLiquidChromeBackgroundModule, decorators: [{ type: NgModule, args: [{ declarations: [ LiquidChromeBackgroundComponent ], imports: [ CommonModule ], exports: [ LiquidChromeBackgroundComponent ] }] }] }); /** * Generated bundle index. Do not edit. */ export { LiquidChromeBackgroundComponent, NcLiquidChromeBackgroundModule }; //# sourceMappingURL=ng-cw-v12-liquid-chrome-background.js.map