UNPKG

ng-cw-v12

Version:

Angular UI Component Library

387 lines (377 loc) 16.9 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 MAX_COLORS = 8; const frag = ` #define MAX_COLORS ${MAX_COLORS} uniform vec2 uCanvas; uniform float uTime; uniform float uSpeed; uniform vec2 uRot; uniform int uColorCount; uniform vec3 uColors[MAX_COLORS]; uniform int uTransparent; uniform float uScale; uniform float uFrequency; uniform float uWarpStrength; uniform vec2 uPointer; // in NDC [-1,1] uniform float uMouseInfluence; uniform float uParallax; uniform float uNoise; varying vec2 vUv; void main() { float t = uTime * uSpeed; vec2 p = vUv * 2.0 - 1.0; p += uPointer * uParallax * 0.1; vec2 rp = vec2(p.x * uRot.x - p.y * uRot.y, p.x * uRot.y + p.y * uRot.x); vec2 q = vec2(rp.x * (uCanvas.x / uCanvas.y), rp.y); q /= max(uScale, 0.0001); q /= 0.5 + 0.2 * dot(q, q); q += 0.2 * cos(t) - 7.56; vec2 toward = (uPointer - rp); q += toward * uMouseInfluence * 0.2; vec3 col = vec3(0.0); float a = 1.0; if (uColorCount > 0) { vec2 s = q; vec3 sumCol = vec3(0.0); float cover = 0.0; for (int i = 0; i < MAX_COLORS; ++i) { if (i >= uColorCount) break; s -= 0.01; // Use uWarpStrength to directly scale the inner phase distortion vec2 r = sin(1.5 * (s.yx * uFrequency) + (2.0 * uWarpStrength) * cos(s * uFrequency)); float m = length(r + sin(5.0 * r.y * uFrequency - 3.0 * t + float(i)) / 4.0); float w = 1.0 - exp(-6.0 / exp(6.0 * m)); sumCol += uColors[i] * w; cover = max(cover, w); } col = clamp(sumCol, 0.0, 1.0); a = uTransparent > 0 ? cover : 1.0; } else { vec2 s = q; for (int k = 0; k < 3; ++k) { s -= 0.01; vec2 r = sin(1.5 * (s.yx * uFrequency) + (2.0 * uWarpStrength) * cos(s * uFrequency)); float m = length(r + sin(5.0 * r.y * uFrequency - 3.0 * t + float(k)) / 4.0); col[k] = 1.0 - exp(-6.0 / exp(6.0 * m)); } a = uTransparent > 0 ? max(max(col.r, col.g), col.b) : 1.0; } if (uNoise > 0.0001) { float n = fract(sin(dot(gl_FragCoord.xy + vec2(uTime), vec2(12.9898, 78.233))) * 43758.5453123); col += (n - 0.5) * uNoise; col = clamp(col, 0.0, 1.0); } vec3 rgb = (uTransparent > 0) ? col * a : col; gl_FragColor = vec4(rgb, a); } `; const vert = ` varying vec2 vUv; void main() { vUv = uv; gl_Position = vec4(position, 1.0); } `; class ColorBendsBackgroundComponent { constructor(ngZone) { this.ngZone = ngZone; /** 容器背景颜色 */ this.ncBgColor = 'black'; /** 旋转角度(deg -180 - 180) */ this.ncRotation = 0; /** 动画速度(0-1)*/ this.ncSpeed = 0.2; /** 用于混合弯曲部分的颜色(十六进制字符串数组,最大支持8个颜色) */ this.ncColors = []; /** 是否开启透明背景 */ this._transparent = true; /** 自动旋转速度(deg/s -5-5) */ this.ncAutoRotate = 0; /** 缩放比例(0.2-5) */ this.ncScale = 1; /** 频率(0-5) */ this.ncFrequency = 1; /** 扭曲强度(0-1) */ this.ncWarpStrength = 1; /** 鼠标影响力的强度(0-2) */ this.ncMouseInfluence = 1; /** 视差效果强度,视差效果是指指针移动内容时产生的效果(0-2) */ this.ncParallax = 0.5; /** 噪点强度(0-1) */ this.ncNoise = 0.1; this.pointerTarget = new THREE.Vector2(0, 0); this.pointerCurrent = new THREE.Vector2(0, 0); this.pointerSmooth = 8; this.isVisible = true; this.rafId = null; this.resizeRafId = null; this.loop = () => { this.rafId = requestAnimationFrame(this.loop); // 只在可见窗口时渲染 if (!this.isVisible || document.hidden) { if (this.clock) this.clock.getDelta(); // 防止重新唤醒时时间突变 return; } if (!this.renderer || !this.scene || !this.camera) return; const dt = this.clock.getDelta(); const elapsed = this.clock.elapsedTime; this.material.uniforms['uTime'].value = elapsed; const deg = (Number(this.ncRotation) % 360) + Number(this.ncAutoRotate) * elapsed; const rad = (deg * Math.PI) / 180; const c = Math.cos(rad); const s = Math.sin(rad); this.material.uniforms['uRot'].value.set(c, s); const amt = Math.min(1, dt * this.pointerSmooth); this.pointerCurrent.lerp(this.pointerTarget, amt); this.material.uniforms['uPointer'].value.copy(this.pointerCurrent); this.renderer.render(this.scene, this.camera); }; } set ncTransparent(val) { this._transparent = val !== null && val !== undefined && val !== false && val !== 'false'; } get ncTransparent() { return this._transparent; } ngOnInit() { } ngAfterViewInit() { this.ngZone.runOutsideAngular(() => { this.initScene(); }); } ngOnDestroy() { this.cleanup(); } ngOnChanges(changes) { if (!this.material) return; this.updateUniforms(); } initScene() { const container = this.containerRef.nativeElement; this.scene = new THREE.Scene(); this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); this.geometry = new THREE.PlaneGeometry(2, 2); const uColorsArray = Array.from({ length: MAX_COLORS }, () => new THREE.Vector3(0, 0, 0)); this.material = new THREE.ShaderMaterial({ vertexShader: vert, fragmentShader: frag, uniforms: { uCanvas: { value: new THREE.Vector2(1, 1) }, uTime: { value: 0 }, uSpeed: { value: Number(this.ncSpeed) }, uRot: { value: new THREE.Vector2(1, 0) }, uColorCount: { value: 0 }, uColors: { value: uColorsArray }, uTransparent: { value: this.ncTransparent ? 1 : 0 }, uScale: { value: Number(this.ncScale) }, uFrequency: { value: Number(this.ncFrequency) }, uWarpStrength: { value: Number(this.ncWarpStrength) }, uPointer: { value: new THREE.Vector2(0, 0) }, uMouseInfluence: { value: Number(this.ncMouseInfluence) }, uParallax: { value: Number(this.ncParallax) }, uNoise: { value: Number(this.ncNoise) } }, premultipliedAlpha: true, transparent: true }); const mesh = new THREE.Mesh(this.geometry, this.material); this.scene.add(mesh); this.renderer = new THREE.WebGLRenderer({ antialias: false, powerPreference: 'high-performance', alpha: true }); this.renderer.outputColorSpace = THREE.SRGBColorSpace; this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); this.renderer.setClearColor(0x000000, this.ncTransparent ? 0 : 1); this.renderer.domElement.style.width = '100%'; this.renderer.domElement.style.height = '100%'; this.renderer.domElement.style.display = 'block'; container.appendChild(this.renderer.domElement); this.clock = new THREE.Clock(); this.updateUniforms(); this.setupResizeObserver(container); this.setupIntersectionObserver(container); this.loop(); } updateUniforms() { if (!this.material) return; this.material.uniforms['uSpeed'].value = Number(this.ncSpeed); this.material.uniforms['uScale'].value = Number(this.ncScale); this.material.uniforms['uFrequency'].value = Number(this.ncFrequency); this.material.uniforms['uWarpStrength'].value = Number(this.ncWarpStrength); this.material.uniforms['uMouseInfluence'].value = Number(this.ncMouseInfluence); this.material.uniforms['uParallax'].value = Number(this.ncParallax); this.material.uniforms['uNoise'].value = Number(this.ncNoise); this.material.uniforms['uTransparent'].value = this.ncTransparent ? 1 : 0; if (this.renderer) { this.renderer.setClearColor(0x000000, this.ncTransparent ? 0 : 1); } const toVec3 = (hex) => { const h = hex.replace('#', '').trim(); const v = h.length === 3 ? [parseInt(h[0] + h[0], 16), parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16)] : [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)]; return new THREE.Vector3(v[0] / 255, v[1] / 255, v[2] / 255); }; const arr = (this.ncColors || []).filter(Boolean).slice(0, MAX_COLORS).map(toVec3); for (let i = 0; i < MAX_COLORS; i++) { const vec = this.material.uniforms['uColors'].value[i]; if (i < arr.length) vec.copy(arr[i]); else vec.set(0, 0, 0); } this.material.uniforms['uColorCount'].value = arr.length; } onPointerMove(e) { var _a; if (!((_a = this.containerRef) === null || _a === void 0 ? void 0 : _a.nativeElement)) return; const container = this.containerRef.nativeElement; const rect = container.getBoundingClientRect(); const x = ((e.clientX - rect.left) / (rect.width || 1)) * 2 - 1; const y = -(((e.clientY - rect.top) / (rect.height || 1)) * 2 - 1); this.pointerTarget.set(x, y); } resize(container) { if (!this.renderer || !this.material) return; const w = container.clientWidth || 1; const h = container.clientHeight || 1; this.renderer.setSize(w, h, false); this.material.uniforms['uCanvas'].value.set(w, h); } setupResizeObserver(container) { this.resize(container); this.resizeObserver = new ResizeObserver(() => { if (!this.renderer) return; if (this.resizeRafId !== null) cancelAnimationFrame(this.resizeRafId); this.ngZone.runOutsideAngular(() => { this.resizeRafId = requestAnimationFrame(() => { if (!this.renderer) return; this.resize(container); this.resizeRafId = null; }); }); }); this.resizeObserver.observe(container); } setupIntersectionObserver(container) { this.intersectionObserver = new IntersectionObserver((entries) => { const entry = entries[0]; this.isVisible = entry.isIntersecting && entry.intersectionRatio > 0; }, { threshold: [0, 0.01, 0.1] }); this.intersectionObserver.observe(container); } cleanup() { var _a, _b; if (this.rafId !== null) { cancelAnimationFrame(this.rafId); this.rafId = null; } if (this.resizeRafId !== null) { cancelAnimationFrame(this.resizeRafId); this.resizeRafId = null; } 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.geometry) this.geometry.dispose(); if (this.material) this.material.dispose(); if (this.renderer) { const dom = this.renderer.domElement; if (dom && dom.parentNode) { dom.parentNode.removeChild(dom); } this.renderer.dispose(); } } } ColorBendsBackgroundComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: ColorBendsBackgroundComponent, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); ColorBendsBackgroundComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: ColorBendsBackgroundComponent, selector: "nc-color-bends-background", inputs: { ncBgColor: "ncBgColor", ncRotation: "ncRotation", ncSpeed: "ncSpeed", ncColors: "ncColors", ncTransparent: "ncTransparent", ncAutoRotate: "ncAutoRotate", ncScale: "ncScale", ncFrequency: "ncFrequency", ncWarpStrength: "ncWarpStrength", ncMouseInfluence: "ncMouseInfluence", ncParallax: "ncParallax", ncNoise: "ncNoise" }, host: { listeners: { "pointermove": "onPointerMove($event)" } }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div #container class=\"nc-color-bends-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-color-bends-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: ColorBendsBackgroundComponent, decorators: [{ type: Component, args: [{ selector: 'nc-color-bends-background', templateUrl: './color-bends-background.component.html', styleUrls: ['./color-bends-background.component.less'] }] }], ctorParameters: function () { return [{ type: i0.NgZone }]; }, propDecorators: { containerRef: [{ type: ViewChild, args: ['container', { static: true }] }], ncBgColor: [{ type: Input }], ncRotation: [{ type: Input }], ncSpeed: [{ type: Input }], ncColors: [{ type: Input }], ncTransparent: [{ type: Input }], ncAutoRotate: [{ type: Input }], ncScale: [{ type: Input }], ncFrequency: [{ type: Input }], ncWarpStrength: [{ type: Input }], ncMouseInfluence: [{ type: Input }], ncParallax: [{ type: Input }], ncNoise: [{ type: Input }], onPointerMove: [{ type: HostListener, args: ['pointermove', ['$event']] }] } }); class NcColorBendsBackgroundModule { } NcColorBendsBackgroundModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcColorBendsBackgroundModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); NcColorBendsBackgroundModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcColorBendsBackgroundModule, declarations: [ColorBendsBackgroundComponent], imports: [CommonModule], exports: [ColorBendsBackgroundComponent] }); NcColorBendsBackgroundModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcColorBendsBackgroundModule, imports: [[ CommonModule ]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcColorBendsBackgroundModule, decorators: [{ type: NgModule, args: [{ declarations: [ ColorBendsBackgroundComponent ], imports: [ CommonModule ], exports: [ ColorBendsBackgroundComponent ] }] }] }); /** * Generated bundle index. Do not edit. */ export { ColorBendsBackgroundComponent, NcColorBendsBackgroundModule }; //# sourceMappingURL=ng-cw-v12-color-bends-background.js.map