UNPKG

ng-cw-v12

Version:

Angular UI Component Library

548 lines (506 loc) 24.8 kB
import * as i0 from '@angular/core'; import { Component, ViewChild, Input, HostListener, NgModule } from '@angular/core'; import { Vector3, Clock, Vector2, Scene, OrthographicCamera, WebGLRenderer, ShaderMaterial, PlaneGeometry, Mesh } from 'three'; import { CommonModule } from '@angular/common'; const vertexShader = ` precision highp float; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `; const fragmentShader = ` precision highp float; uniform float iTime; uniform vec3 iResolution; uniform float animationSpeed; uniform bool enableTop; uniform bool enableMiddle; uniform bool enableBottom; uniform int topLineCount; uniform int middleLineCount; uniform int bottomLineCount; uniform float topLineDistance; uniform float middleLineDistance; uniform float bottomLineDistance; uniform vec3 topWavePosition; uniform vec3 middleWavePosition; uniform vec3 bottomWavePosition; uniform vec2 iMouse; uniform bool interactive; uniform float bendRadius; uniform float bendStrength; uniform float bendInfluence; uniform bool parallax; uniform float parallaxStrength; uniform vec2 parallaxOffset; uniform vec3 lineGradient[8]; uniform int lineGradientCount; const vec3 BLACK = vec3(0.0); const vec3 PINK = vec3(233.0, 71.0, 245.0) / 255.0; const vec3 BLUE = vec3(47.0, 75.0, 162.0) / 255.0; mat2 rotate(float r) { return mat2(cos(r), sin(r), -sin(r), cos(r)); } vec3 background_color(vec2 uv) { vec3 col = vec3(0.0); float y = sin(uv.x - 0.2) * 0.3 - 0.1; float m = uv.y - y; col += mix(BLUE, BLACK, smoothstep(0.0, 1.0, abs(m))); col += mix(PINK, BLACK, smoothstep(0.0, 1.0, abs(m - 0.8))); return col * 0.5; } vec3 getLineColor(float t, vec3 baseColor) { if (lineGradientCount <= 0) { return baseColor; } vec3 gradientColor; if (lineGradientCount == 1) { gradientColor = lineGradient[0]; } else { float clampedT = clamp(t, 0.0, 0.9999); float scaled = clampedT * float(lineGradientCount - 1); int idx = int(floor(scaled)); float f = fract(scaled); int idx2 = min(idx + 1, lineGradientCount - 1); vec3 c1 = lineGradient[idx]; vec3 c2 = lineGradient[idx2]; gradientColor = mix(c1, c2, f); } return gradientColor * 0.5; } float wave(vec2 uv, float offset, vec2 screenUv, vec2 mouseUv, bool shouldBend) { float time = iTime * animationSpeed; float x_offset = offset; float x_movement = time * 0.1; float amp = sin(offset + time * 0.2) * 0.3; float y = sin(uv.x + x_offset + x_movement) * amp; if (shouldBend) { vec2 d = screenUv - mouseUv; float influence = exp(-dot(d, d) * bendRadius); // radial falloff around cursor float bendOffset = (mouseUv.y - screenUv.y) * influence * bendStrength * bendInfluence; y += bendOffset; } float m = uv.y - y; return 0.0175 / max(abs(m) + 0.01, 1e-3) + 0.01; } void mainImage(out vec4 fragColor, in vec2 fragCoord) { vec2 baseUv = (2.0 * fragCoord - iResolution.xy) / iResolution.y; baseUv.y *= -1.0; if (parallax) { baseUv += parallaxOffset; } vec3 col = vec3(0.0); vec3 b = lineGradientCount > 0 ? vec3(0.0) : background_color(baseUv); vec2 mouseUv = vec2(0.0); if (interactive) { mouseUv = (2.0 * iMouse - iResolution.xy) / iResolution.y; mouseUv.y *= -1.0; } if (enableBottom) { for (int i = 0; i < 20; ++i) { // Fixed limit for Loop if(i >= bottomLineCount) break; float fi = float(i); float t = fi / max(float(bottomLineCount - 1), 1.0); vec3 lineCol = getLineColor(t, b); float angle = bottomWavePosition.z * log(length(baseUv) + 1.0); vec2 ruv = baseUv * rotate(angle); col += lineCol * wave( ruv + vec2(bottomLineDistance * fi + bottomWavePosition.x, bottomWavePosition.y), 1.5 + 0.2 * fi, baseUv, mouseUv, interactive ) * 0.2; } } if (enableMiddle) { for (int i = 0; i < 20; ++i) { if(i >= middleLineCount) break; float fi = float(i); float t = fi / max(float(middleLineCount - 1), 1.0); vec3 lineCol = getLineColor(t, b); float angle = middleWavePosition.z * log(length(baseUv) + 1.0); vec2 ruv = baseUv * rotate(angle); col += lineCol * wave( ruv + vec2(middleLineDistance * fi + middleWavePosition.x, middleWavePosition.y), 2.0 + 0.15 * fi, baseUv, mouseUv, interactive ); } } if (enableTop) { for (int i = 0; i < 20; ++i) { if(i >= topLineCount) break; float fi = float(i); float t = fi / max(float(topLineCount - 1), 1.0); vec3 lineCol = getLineColor(t, b); float angle = topWavePosition.z * log(length(baseUv) + 1.0); vec2 ruv = baseUv * rotate(angle); ruv.x *= -1.0; col += lineCol * wave( ruv + vec2(topLineDistance * fi + topWavePosition.x, topWavePosition.y), 1.0 + 0.2 * fi, baseUv, mouseUv, interactive ) * 0.1; } } fragColor = vec4(col, 1.0); } void main() { vec4 color = vec4(0.0); mainImage(color, gl_FragCoord.xy); gl_FragColor = color; } `; const MAX_GRADIENT_STOPS = 8; function hexToVec3(hex) { let value = hex.trim(); if (value.startsWith('#')) { value = value.slice(1); } let r = 255; let g = 255; let b = 255; if (value.length === 3) { r = parseInt(value[0] + value[0], 16); g = parseInt(value[1] + value[1], 16); b = parseInt(value[2] + value[2], 16); } else if (value.length === 6) { r = parseInt(value.slice(0, 2), 16); g = parseInt(value.slice(2, 4), 16); b = parseInt(value.slice(4, 6), 16); } return new Vector3(r / 255, g / 255, b / 255); } class FloatingLinesBackgroundComponent { constructor(ngZone) { this.ngZone = ngZone; /** 用于线条渐变着色的十六进制颜色字符串数组(最多 8 种颜色) */ this.ncLinesGradient = []; /** 显示哪些波形层。可以单独切换显示/隐藏各个波形 */ this.ncEnabledWaves = ['top', 'middle', 'bottom']; /** 每波形的线条数。单个数值适用于所有波形,也可以使用数组进行逐波形控制(1-20) */ this.ncLineCount = 6; /** 行间距。单个数值适用于所有波形,或使用数组进行逐个波形的控制(1-100) */ this.ncLineDistance = 5; /** 顶部波浪的位置和旋转参数 { x, y, rotate } */ this.ncTopWavePosition = { x: 10.0, y: 0.5, rotate: -0.4 }; /** 中部波浪的位置和旋转参数 { x, y, rotate } */ this.ncMiddleWavePosition = { x: 5.0, y: 0.0, rotate: 0.2 }; /** 底部波浪的位置和旋转参数 { x, y, rotate } */ this.ncBottomWavePosition = { x: 2.0, y: -0.7, rotate: -1 }; /** 波浪动画的速度倍增器 */ this.ncAnimationSpeed = 1; /** 线条是否会对鼠标/指针移动做出反应 */ this._interactive = true; /** 受鼠标交互影响区域的半径(1-30) */ this.ncBendRadius = 5.0; /** 与鼠标交互时弯曲效应的强度(-15-15) */ this.ncBendStrength = -0.5; /** 鼠标移动跟踪的平滑因子(0-1) */ this.ncMouseDamping = 0.05; /** 启用鼠标移动时的视差效果 */ this._parallax = true; /** 视差效应的强度 */ this.ncParallaxStrength = 0.2; /** CSS mix-blend-mode 应用于canvas元素 */ this.ncMixBlendMode = 'unset'; this.clock = new Clock(); this.rafId = 0; this.targetMouse = new Vector2(-1000, -1000); this.currentMouse = new Vector2(-1000, -1000); this.targetInfluence = 0; this.currentInfluence = 0; this.targetParallax = new Vector2(0, 0); this.currentParallax = new Vector2(0, 0); this.uniforms = {}; } set ncInteractive(val) { this._interactive = val !== null && val !== undefined && val !== false && val !== 'false'; } get ncInteractive() { return this._interactive; } set ncParallax(val) { this._parallax = val !== null && val !== undefined && val !== false && val !== 'false'; } get ncParallax() { return this._parallax; } ngOnInit() { } ngAfterViewInit() { this.initThree(); this.setupResizeObserver(); this.ngZone.runOutsideAngular(() => { this.animate(); }); } ngOnDestroy() { if (this.rafId) cancelAnimationFrame(this.rafId); if (this.resizeObserver) this.resizeObserver.disconnect(); this.cleanupThree(); } ngOnChanges(changes) { if (this.material) { this.updateUniforms(); } } initThree() { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t; const el = this.containerRef.nativeElement; this.scene = new Scene(); this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1); this.camera.position.z = 1; this.renderer = new WebGLRenderer({ antialias: true, alpha: false }); this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); this.renderer.setSize(el.clientWidth || 1, el.clientHeight || 1, false); el.appendChild(this.renderer.domElement); this.uniforms = { iTime: { value: 0 }, iResolution: { value: new Vector3(el.clientWidth, el.clientHeight, 1) }, animationSpeed: { value: this.ncAnimationSpeed }, enableTop: { value: this.ncEnabledWaves.includes('top') }, enableMiddle: { value: this.ncEnabledWaves.includes('middle') }, enableBottom: { value: this.ncEnabledWaves.includes('bottom') }, topLineCount: { value: this.getLineCount('top') }, middleLineCount: { value: this.getLineCount('middle') }, bottomLineCount: { value: this.getLineCount('bottom') }, topLineDistance: { value: this.getLineDistance('top') * 0.01 }, middleLineDistance: { value: this.getLineDistance('middle') * 0.01 }, bottomLineDistance: { value: this.getLineDistance('bottom') * 0.01 }, topWavePosition: { value: new Vector3((_b = (_a = this.ncTopWavePosition) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 10.0, (_d = (_c = this.ncTopWavePosition) === null || _c === void 0 ? void 0 : _c.y) !== null && _d !== void 0 ? _d : 0.5, (_f = (_e = this.ncTopWavePosition) === null || _e === void 0 ? void 0 : _e.rotate) !== null && _f !== void 0 ? _f : -0.4) }, middleWavePosition: { value: new Vector3((_h = (_g = this.ncMiddleWavePosition) === null || _g === void 0 ? void 0 : _g.x) !== null && _h !== void 0 ? _h : 5.0, (_k = (_j = this.ncMiddleWavePosition) === null || _j === void 0 ? void 0 : _j.y) !== null && _k !== void 0 ? _k : 0.0, (_m = (_l = this.ncMiddleWavePosition) === null || _l === void 0 ? void 0 : _l.rotate) !== null && _m !== void 0 ? _m : 0.2) }, bottomWavePosition: { value: new Vector3((_p = (_o = this.ncBottomWavePosition) === null || _o === void 0 ? void 0 : _o.x) !== null && _p !== void 0 ? _p : 2.0, (_r = (_q = this.ncBottomWavePosition) === null || _q === void 0 ? void 0 : _q.y) !== null && _r !== void 0 ? _r : -0.7, (_t = (_s = this.ncBottomWavePosition) === null || _s === void 0 ? void 0 : _s.rotate) !== null && _t !== void 0 ? _t : -1) }, iMouse: { value: new Vector2(-1000, -1000) }, interactive: { value: this.ncInteractive }, bendRadius: { value: this.ncBendRadius }, bendStrength: { value: this.ncBendStrength }, bendInfluence: { value: 0 }, parallax: { value: this.ncParallax }, parallaxStrength: { value: this.ncParallaxStrength }, parallaxOffset: { value: new Vector2(0, 0) }, lineGradient: { value: Array.from({ length: MAX_GRADIENT_STOPS }, () => new Vector3(1, 1, 1)) }, lineGradientCount: { value: 0 } }; if (this.ncLinesGradient && this.ncLinesGradient.length > 0) { const stops = this.ncLinesGradient.slice(0, MAX_GRADIENT_STOPS); this.uniforms.lineGradientCount.value = stops.length; stops.forEach((hex, i) => { const color = hexToVec3(hex); this.uniforms.lineGradient.value[i].copy(color); }); } this.material = new ShaderMaterial({ uniforms: this.uniforms, vertexShader, fragmentShader }); this.geometry = new PlaneGeometry(2, 2); this.mesh = new Mesh(this.geometry, this.material); this.scene.add(this.mesh); } updateUniforms() { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t; this.uniforms.animationSpeed.value = this.ncAnimationSpeed; this.uniforms.enableTop.value = this.ncEnabledWaves.includes('top'); this.uniforms.enableMiddle.value = this.ncEnabledWaves.includes('middle'); this.uniforms.enableBottom.value = this.ncEnabledWaves.includes('bottom'); this.uniforms.topLineCount.value = this.getLineCount('top'); this.uniforms.middleLineCount.value = this.getLineCount('middle'); this.uniforms.bottomLineCount.value = this.getLineCount('bottom'); this.uniforms.topLineDistance.value = this.getLineDistance('top') * 0.01; this.uniforms.middleLineDistance.value = this.getLineDistance('middle') * 0.01; this.uniforms.bottomLineDistance.value = this.getLineDistance('bottom') * 0.01; this.uniforms.topWavePosition.value.set((_b = (_a = this.ncTopWavePosition) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 10.0, (_d = (_c = this.ncTopWavePosition) === null || _c === void 0 ? void 0 : _c.y) !== null && _d !== void 0 ? _d : 0.5, (_f = (_e = this.ncTopWavePosition) === null || _e === void 0 ? void 0 : _e.rotate) !== null && _f !== void 0 ? _f : -0.4); this.uniforms.middleWavePosition.value.set((_h = (_g = this.ncMiddleWavePosition) === null || _g === void 0 ? void 0 : _g.x) !== null && _h !== void 0 ? _h : 5.0, (_k = (_j = this.ncMiddleWavePosition) === null || _j === void 0 ? void 0 : _j.y) !== null && _k !== void 0 ? _k : 0.0, (_m = (_l = this.ncMiddleWavePosition) === null || _l === void 0 ? void 0 : _l.rotate) !== null && _m !== void 0 ? _m : 0.2); this.uniforms.bottomWavePosition.value.set((_p = (_o = this.ncBottomWavePosition) === null || _o === void 0 ? void 0 : _o.x) !== null && _p !== void 0 ? _p : 2.0, (_r = (_q = this.ncBottomWavePosition) === null || _q === void 0 ? void 0 : _q.y) !== null && _r !== void 0 ? _r : -0.7, (_t = (_s = this.ncBottomWavePosition) === null || _s === void 0 ? void 0 : _s.rotate) !== null && _t !== void 0 ? _t : -1); this.uniforms.interactive.value = this.ncInteractive; this.uniforms.bendRadius.value = this.ncBendRadius; this.uniforms.bendStrength.value = this.ncBendStrength; this.uniforms.parallax.value = this.ncParallax; this.uniforms.parallaxStrength.value = this.ncParallaxStrength; if (this.ncLinesGradient && this.ncLinesGradient.length > 0) { const stops = this.ncLinesGradient.slice(0, MAX_GRADIENT_STOPS); this.uniforms.lineGradientCount.value = stops.length; stops.forEach((hex, i) => { const color = hexToVec3(hex); this.uniforms.lineGradient.value[i].copy(color); }); } else { this.uniforms.lineGradientCount.value = 0; } } getLineCount(waveType) { var _a; if (typeof this.ncLineCount === 'number') return this.ncLineCount; if (!this.ncEnabledWaves.includes(waveType)) return 0; const index = this.ncEnabledWaves.indexOf(waveType); return (_a = this.ncLineCount[index]) !== null && _a !== void 0 ? _a : 0; } getLineDistance(waveType) { var _a; if (typeof this.ncLineDistance === 'number') return this.ncLineDistance; if (!this.ncEnabledWaves.includes(waveType)) return 0.1; const index = this.ncEnabledWaves.indexOf(waveType); return (_a = this.ncLineDistance[index]) !== null && _a !== void 0 ? _a : 0.1; } setupResizeObserver() { this.resizeObserver = new ResizeObserver(() => { const el = this.containerRef.nativeElement; const width = el.clientWidth || 1; const height = el.clientHeight || 1; this.ngZone.runOutsideAngular(() => { this.renderer.setSize(width, height, false); const canvasWidth = this.renderer.domElement.width; const canvasHeight = this.renderer.domElement.height; this.uniforms.iResolution.value.set(canvasWidth, canvasHeight, 1); }); }); this.resizeObserver.observe(this.containerRef.nativeElement); } onPointerMove(event) { if (!this.ncInteractive && !this.ncParallax) return; const rect = this.renderer.domElement.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; const dpr = this.renderer.getPixelRatio(); if (this.ncInteractive) { this.targetMouse.set(x * dpr, (rect.height - y) * dpr); this.targetInfluence = 1.0; } if (this.ncParallax) { const centerX = rect.width / 2; const centerY = rect.height / 2; const offsetX = (x - centerX) / rect.width; const offsetY = -(y - centerY) / rect.height; this.targetParallax.set(offsetX * this.ncParallaxStrength, offsetY * this.ncParallaxStrength); } } onPointerLeave() { this.targetInfluence = 0.0; } animate() { this.rafId = requestAnimationFrame(() => this.animate()); this.render(); } render() { this.uniforms.iTime.value = this.clock.getElapsedTime(); if (this.ncInteractive) { this.currentMouse.lerp(this.targetMouse, this.ncMouseDamping); this.uniforms.iMouse.value.copy(this.currentMouse); this.currentInfluence += (this.targetInfluence - this.currentInfluence) * this.ncMouseDamping; this.uniforms.bendInfluence.value = this.currentInfluence; } if (this.ncParallax) { this.currentParallax.lerp(this.targetParallax, this.ncMouseDamping); this.uniforms.parallaxOffset.value.copy(this.currentParallax); } this.renderer.render(this.scene, this.camera); } cleanupThree() { if (this.geometry) this.geometry.dispose(); if (this.material) this.material.dispose(); if (this.renderer) { this.renderer.dispose(); this.renderer.domElement.remove(); } } } FloatingLinesBackgroundComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: FloatingLinesBackgroundComponent, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); FloatingLinesBackgroundComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: FloatingLinesBackgroundComponent, selector: "nc-floating-lines-background", inputs: { ncLinesGradient: "ncLinesGradient", ncEnabledWaves: "ncEnabledWaves", ncLineCount: "ncLineCount", ncLineDistance: "ncLineDistance", ncTopWavePosition: "ncTopWavePosition", ncMiddleWavePosition: "ncMiddleWavePosition", ncBottomWavePosition: "ncBottomWavePosition", ncAnimationSpeed: "ncAnimationSpeed", ncInteractive: "ncInteractive", ncBendRadius: "ncBendRadius", ncBendStrength: "ncBendStrength", ncMouseDamping: "ncMouseDamping", ncParallax: "ncParallax", ncParallaxStrength: "ncParallaxStrength", ncMixBlendMode: "ncMixBlendMode" }, host: { listeners: { "pointermove": "onPointerMove($event)", "pointerleave": "onPointerLeave()" } }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div #container class=\"nc-floating-lines-canvas-container\" [style.mix-blend-mode]=\"ncMixBlendMode\"></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-floating-lines-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: FloatingLinesBackgroundComponent, decorators: [{ type: Component, args: [{ selector: 'nc-floating-lines-background', templateUrl: './floating-lines-background.component.html', styleUrls: ['./floating-lines-background.component.less'] }] }], ctorParameters: function () { return [{ type: i0.NgZone }]; }, propDecorators: { containerRef: [{ type: ViewChild, args: ['container', { static: true }] }], ncLinesGradient: [{ type: Input }], ncEnabledWaves: [{ type: Input }], ncLineCount: [{ type: Input }], ncLineDistance: [{ type: Input }], ncTopWavePosition: [{ type: Input }], ncMiddleWavePosition: [{ type: Input }], ncBottomWavePosition: [{ type: Input }], ncAnimationSpeed: [{ type: Input }], ncInteractive: [{ type: Input }], ncBendRadius: [{ type: Input }], ncBendStrength: [{ type: Input }], ncMouseDamping: [{ type: Input }], ncParallax: [{ type: Input }], ncParallaxStrength: [{ type: Input }], ncMixBlendMode: [{ type: Input }], onPointerMove: [{ type: HostListener, args: ['pointermove', ['$event']] }], onPointerLeave: [{ type: HostListener, args: ['pointerleave'] }] } }); class NcFloatingLinesBackgroundModule { } NcFloatingLinesBackgroundModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcFloatingLinesBackgroundModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); NcFloatingLinesBackgroundModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcFloatingLinesBackgroundModule, declarations: [FloatingLinesBackgroundComponent], imports: [CommonModule], exports: [FloatingLinesBackgroundComponent] }); NcFloatingLinesBackgroundModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcFloatingLinesBackgroundModule, imports: [[ CommonModule ]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcFloatingLinesBackgroundModule, decorators: [{ type: NgModule, args: [{ declarations: [ FloatingLinesBackgroundComponent ], imports: [ CommonModule ], exports: [ FloatingLinesBackgroundComponent ] }] }] }); /** * Generated bundle index. Do not edit. */ export { FloatingLinesBackgroundComponent, NcFloatingLinesBackgroundModule }; //# sourceMappingURL=ng-cw-v12-floating-lines-background.js.map