UNPKG

ng-cw-v12

Version:

Angular UI Component Library

513 lines (483 loc) 21 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 = ` in vec3 position; in vec2 uv; out vec2 vUv; void main() { vUv = uv; gl_Position = vec4(position, 1.0); } `; const fragmentShader = ` precision highp float; precision highp int; out vec4 fragColor; uniform vec2 uResolution; uniform float uTime; uniform float uIntensity; uniform float uSpeed; uniform int uAnimType; uniform vec2 uMouse; uniform int uColorCount; uniform float uDistort; uniform vec2 uOffset; uniform sampler2D uGradient; uniform float uNoiseAmount; uniform int uRayCount; float hash21(vec2 p){ p = floor(p); float f = 52.9829189 * fract(dot(p, vec2(0.065, 0.005))); return fract(f); } mat2 rot30(){ return mat2(0.8, -0.5, 0.5, 0.8); } float layeredNoise(vec2 fragPx){ vec2 p = mod(fragPx + vec2(uTime * 30.0, -uTime * 21.0), 1024.0); vec2 q = rot30() * p; float n = 0.0; n += 0.40 * hash21(q); n += 0.25 * hash21(q * 2.0 + 17.0); n += 0.20 * hash21(q * 4.0 + 47.0); n += 0.10 * hash21(q * 8.0 + 113.0); n += 0.05 * hash21(q * 16.0 + 191.0); return n; } vec3 rayDir(vec2 frag, vec2 res, vec2 offset, float dist){ float focal = res.y * max(dist, 1e-3); return normalize(vec3(2.0 * (frag - offset) - res, focal)); } float edgeFade(vec2 frag, vec2 res, vec2 offset){ vec2 toC = frag - 0.5 * res - offset; float r = length(toC) / (0.5 * min(res.x, res.y)); float x = clamp(r, 0.0, 1.0); float q = x * x * x * (x * (x * 6.0 - 15.0) + 10.0); float s = q * 0.5; s = pow(s, 1.5); float tail = 1.0 - pow(1.0 - s, 2.0); s = mix(s, tail, 0.2); float dn = (layeredNoise(frag * 0.15) - 0.5) * 0.0015 * s; return clamp(s + dn, 0.0, 1.0); } mat3 rotX(float a){ float c = cos(a), s = sin(a); return mat3(1.0,0.0,0.0, 0.0,c,-s, 0.0,s,c); } mat3 rotY(float a){ float c = cos(a), s = sin(a); return mat3(c,0.0,s, 0.0,1.0,0.0, -s,0.0,c); } mat3 rotZ(float a){ float c = cos(a), s = sin(a); return mat3(c,-s,0.0, s,c,0.0, 0.0,0.0,1.0); } vec3 sampleGradient(float t){ t = clamp(t, 0.0, 1.0); return texture(uGradient, vec2(t, 0.5)).rgb; } vec2 rot2(vec2 v, float a){ float s = sin(a), c = cos(a); return mat2(c, -s, s, c) * v; } float bendAngle(vec3 q, float t){ float a = 0.8 * sin(q.x * 0.55 + t * 0.6) + 0.7 * sin(q.y * 0.50 - t * 0.5) + 0.6 * sin(q.z * 0.60 + t * 0.7); return a; } void main(){ vec2 frag = gl_FragCoord.xy; float t = uTime * uSpeed; float jitterAmp = 0.1 * clamp(uNoiseAmount, 0.0, 1.0); vec3 dir = rayDir(frag, uResolution, uOffset, 1.0); float marchT = 0.0; vec3 col = vec3(0.0); float n = layeredNoise(frag); vec4 c = cos(t * 0.2 + vec4(0.0, 33.0, 11.0, 0.0)); mat2 M2 = mat2(c.x, c.y, c.z, c.w); float amp = clamp(uDistort, 0.0, 50.0) * 0.15; mat3 rot3dMat = mat3(1.0); if(uAnimType == 1){ vec3 ang = vec3(t * 0.31, t * 0.21, t * 0.17); rot3dMat = rotZ(ang.z) * rotY(ang.y) * rotX(ang.x); } mat3 hoverMat = mat3(1.0); if(uAnimType == 2){ vec2 m = uMouse * 2.0 - 1.0; vec3 ang = vec3(m.y * 0.6, m.x * 0.6, 0.0); hoverMat = rotY(ang.y) * rotX(ang.x); } for (int i = 0; i < 44; ++i) { vec3 P = marchT * dir; P.z -= 2.0; float rad = length(P); vec3 Pl = P * (10.0 / max(rad, 1e-6)); if(uAnimType == 0){ Pl.xz *= M2; } else if(uAnimType == 1){ Pl = rot3dMat * Pl; } else { Pl = hoverMat * Pl; } float stepLen = min(rad - 0.3, n * jitterAmp) + 0.1; float grow = smoothstep(0.35, 3.0, marchT); float a1 = amp * grow * bendAngle(Pl * 0.6, t); float a2 = 0.5 * amp * grow * bendAngle(Pl.zyx * 0.5 + 3.1, t * 0.9); vec3 Pb = Pl; Pb.xz = rot2(Pb.xz, a1); Pb.xy = rot2(Pb.xy, a2); float rayPattern = smoothstep( 0.5, 0.7, sin(Pb.x + cos(Pb.y) * cos(Pb.z)) * sin(Pb.z + sin(Pb.y) * cos(Pb.x + t)) ); if (uRayCount > 0) { float ang = atan(Pb.y, Pb.x); float comb = 0.5 + 0.5 * cos(float(uRayCount) * ang); comb = pow(comb, 3.0); rayPattern *= smoothstep(0.15, 0.95, comb); } vec3 spectralDefault = 1.0 + vec3( cos(marchT * 3.0 + 0.0), cos(marchT * 3.0 + 1.0), cos(marchT * 3.0 + 2.0) ); float saw = fract(marchT * 0.25); float tRay = saw * saw * (3.0 - 2.0 * saw); vec3 userGradient = 2.0 * sampleGradient(tRay); vec3 spectral = (uColorCount > 0) ? userGradient : spectralDefault; vec3 base = (0.05 / (0.4 + stepLen)) * smoothstep(5.0, 0.0, rad) * spectral; col += base * rayPattern; marchT += stepLen; } col *= edgeFade(frag, uResolution, uOffset); col *= uIntensity; fragColor = vec4(clamp(col, 0.0, 1.0), 1.0); } `; const hexToRgb01 = (hex) => { let h = hex.trim(); if (h.startsWith('#')) h = h.slice(1); if (h.length === 3) { const r = h[0], g = h[1], b = h[2]; h = r + r + g + g + b + b; } const intVal = parseInt(h, 16); if (isNaN(intVal) || (h.length !== 6 && h.length !== 8)) return [1, 1, 1]; const r = ((intVal >> 16) & 255) / 255; const g = ((intVal >> 8) & 255) / 255; const b = (intVal & 255) / 255; return [r, g, b]; }; const toPx = (v) => { if (v == null) return 0; if (typeof v === 'number') return v; const s = String(v).trim(); const num = parseFloat(s.replace('px', '')); return isNaN(num) ? 0 : num; }; class PrismaticBurstBackgroundComponent { constructor(ngZone) { this.ngZone = ngZone; /** 累加后应用整体亮度倍增器(0.1-5) */ this.ncIntensity = 2; /** 控制光线运动和扭曲的全局时间倍增器(0-2) */ this.ncSpeed = 0.5; /** 动画类型:'rotate' | '3drotate' | 'hover' */ this.ncAnimationType = '3drotate'; /** 设置特定颜色(十六进制字符串数组) */ this.ncColors = []; /** 对行进空间施加的弯曲/扭曲程度(增加自然的摇摆感,0-10) */ this.ncDistort = 0; /** 是否暂停动画 */ this._paused = false; /** 中心点的偏移量 (x, y) */ this.ncOffset = { x: 0, y: 0 }; /** 当ncAnimationType='hover'时,指针跟踪的平滑因子(0-1) */ this.ncHoverDampness = 0; /** 如果大于0,则应用角度梳状滤波器生成离散光线辐条(0-64) */ this.ncRayCount = 0; this.mouseTarget = [0.5, 0.5]; this.mouseSmooth = [0.5, 0.5]; this.isVisible = true; this.rafId = null; this.lastTime = performance.now(); this.accumTime = 0; this.onResizeBound = () => this.resizeCanvas(); this.updateLoop = (now) => { const dt = Math.max(0, now - this.lastTime) * 0.001; this.lastTime = now; const visible = this.isVisible && !document.hidden; if (!this.ncPaused) { this.accumTime += dt; } if (!visible) { this.rafId = requestAnimationFrame(this.updateLoop); return; } const tau = 0.02 + Math.max(0, Math.min(1, this.ncHoverDampness)) * 0.5; const alpha = 1 - Math.exp(-dt / tau); const tgt = this.mouseTarget; const sm = this.mouseSmooth; sm[0] += (tgt[0] - sm[0]) * alpha; sm[1] += (tgt[1] - sm[1]) * alpha; if (this.material) { this.material.uniforms['uMouse'].value.set(sm[0], sm[1]); this.material.uniforms['uTime'].value = this.accumTime; } if (this.renderer && this.scene && this.camera) { this.renderer.render(this.scene, this.camera); } this.rafId = requestAnimationFrame(this.updateLoop); }; } set ncPaused(val) { this._paused = val !== null && val !== undefined && val !== false && val !== 'false'; } get ncPaused() { return this._paused; } ngOnInit() { } ngAfterViewInit() { this.ngZone.runOutsideAngular(() => { this.initScene(); this.applyOptions(); this.setupObservers(); this.updateLoop(performance.now()); }); } ngOnDestroy() { this.cleanup(); } ngOnChanges(changes) { if (this.material) { this.applyOptions(); } } onPointerMove(event) { var _a; if (!((_a = this.containerRef) === null || _a === void 0 ? void 0 : _a.nativeElement)) return; const rect = this.containerRef.nativeElement.getBoundingClientRect(); const x = (event.clientX - rect.left) / Math.max(rect.width, 1); const y = (event.clientY - rect.top) / Math.max(rect.height, 1); this.mouseTarget = [Math.min(Math.max(x, 0), 1), Math.min(Math.max(y, 0), 1)]; } onVisibilityChange() { this.isVisible = !document.hidden; } initScene() { const container = this.containerRef.nativeElement; const dpr = Math.min(window.devicePixelRatio || 1, 2); this.renderer = new THREE.WebGLRenderer({ alpha: false, antialias: false }); this.renderer.setPixelRatio(dpr); // 初始化画布样式 const canvas = this.renderer.domElement; canvas.style.position = 'absolute'; canvas.style.inset = '0'; canvas.style.width = '100%'; canvas.style.height = '100%'; container.appendChild(canvas); this.scene = new THREE.Scene(); this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); const white = new Uint8Array([255, 255, 255, 255]); this.gradientTex = new THREE.DataTexture(white, 1, 1, THREE.RGBAFormat); this.gradientTex.minFilter = THREE.LinearFilter; this.gradientTex.magFilter = THREE.LinearFilter; this.gradientTex.wrapS = THREE.ClampToEdgeWrapping; this.gradientTex.wrapT = THREE.ClampToEdgeWrapping; this.gradientTex.generateMipmaps = false; this.gradientTex.needsUpdate = true; this.material = new THREE.RawShaderMaterial({ vertexShader: vertexShader, fragmentShader: fragmentShader, glslVersion: THREE.GLSL3, uniforms: { uResolution: { value: new THREE.Vector2(1, 1) }, uTime: { value: 0 }, uIntensity: { value: 1 }, uSpeed: { value: 1 }, uAnimType: { value: 0 }, uMouse: { value: new THREE.Vector2(0.5, 0.5) }, uColorCount: { value: 0 }, uDistort: { value: 0 }, uOffset: { value: new THREE.Vector2(0, 0) }, uGradient: { value: this.gradientTex }, uNoiseAmount: { value: 0.8 }, uRayCount: { value: 0 } } }); const geometry = new THREE.PlaneGeometry(2, 2); this.mesh = new THREE.Mesh(geometry, this.material); this.scene.add(this.mesh); this.resizeCanvas(); } setupObservers() { const container = this.containerRef.nativeElement; if (typeof ResizeObserver !== 'undefined') { this.resizeObserver = new ResizeObserver(() => { this.resizeCanvas(); }); this.resizeObserver.observe(container); } else { window.addEventListener('resize', this.onResizeBound); } if (typeof IntersectionObserver !== 'undefined') { this.intersectionObserver = new IntersectionObserver((entries) => { if (entries[0]) { this.isVisible = entries[0].isIntersecting; } }, { root: null, threshold: 0.01 }); this.intersectionObserver.observe(container); } } resizeCanvas() { if (!this.containerRef || !this.renderer) return; const container = this.containerRef.nativeElement; const w = container.clientWidth || 1; const h = container.clientHeight || 1; // false 指代不修改 style 里面的 width 和 height this.renderer.setSize(w, h, false); const gl = this.renderer.getContext(); this.material.uniforms['uResolution'].value.set(gl.drawingBufferWidth, gl.drawingBufferHeight); } applyOptions() { var _a, _b, _c, _d, _e, _f, _g; if (!this.material) return; const uniforms = this.material.uniforms; uniforms['uIntensity'].value = (_a = this.ncIntensity) !== null && _a !== void 0 ? _a : 1; uniforms['uSpeed'].value = (_b = this.ncSpeed) !== null && _b !== void 0 ? _b : 1; const animTypeMap = { rotate: 0, '3drotate': 1, hover: 2 }; uniforms['uAnimType'].value = (_d = animTypeMap[(_c = this.ncAnimationType) !== null && _c !== void 0 ? _c : 'rotate']) !== null && _d !== void 0 ? _d : 0; uniforms['uDistort'].value = typeof this.ncDistort === 'number' ? this.ncDistort : 0; const ox = toPx((_e = this.ncOffset) === null || _e === void 0 ? void 0 : _e.x); const oy = toPx((_f = this.ncOffset) === null || _f === void 0 ? void 0 : _f.y); uniforms['uOffset'].value.set(ox, oy); uniforms['uRayCount'].value = Math.max(0, Math.floor((_g = this.ncRayCount) !== null && _g !== void 0 ? _g : 0)); let count = 0; if (Array.isArray(this.ncColors) && this.ncColors.length > 0) { const capped = this.ncColors.slice(0, 64); count = capped.length; const data = new Uint8Array(count * 4); for (let i = 0; i < count; i++) { const [r, g, b] = hexToRgb01(capped[i]); data[i * 4 + 0] = Math.round(r * 255); data[i * 4 + 1] = Math.round(g * 255); data[i * 4 + 2] = Math.round(b * 255); data[i * 4 + 3] = 255; } if (this.gradientTex) { this.gradientTex.dispose(); } this.gradientTex = new THREE.DataTexture(data, count, 1, THREE.RGBAFormat); this.gradientTex.minFilter = THREE.LinearFilter; this.gradientTex.magFilter = THREE.LinearFilter; this.gradientTex.wrapS = THREE.ClampToEdgeWrapping; this.gradientTex.wrapT = THREE.ClampToEdgeWrapping; this.gradientTex.generateMipmaps = false; this.gradientTex.needsUpdate = true; uniforms['uGradient'].value = this.gradientTex; } uniforms['uColorCount'].value = count; } cleanup() { var _a; if (this.rafId !== null) { cancelAnimationFrame(this.rafId); this.rafId = null; } if (this.resizeObserver) { this.resizeObserver.disconnect(); } window.removeEventListener('resize', this.onResizeBound); if (this.intersectionObserver) { this.intersectionObserver.disconnect(); } if (this.mesh && this.scene) { this.scene.remove(this.mesh); this.mesh.geometry.dispose(); if (this.material) { this.material.dispose(); } } if (this.gradientTex) { this.gradientTex.dispose(); } if (this.renderer) { this.renderer.dispose(); const container = (_a = this.containerRef) === null || _a === void 0 ? void 0 : _a.nativeElement; if (container && this.renderer.domElement.parentNode === container) { container.removeChild(this.renderer.domElement); } } } } PrismaticBurstBackgroundComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: PrismaticBurstBackgroundComponent, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); PrismaticBurstBackgroundComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: PrismaticBurstBackgroundComponent, selector: "nc-prismatic-burst-background", inputs: { ncIntensity: "ncIntensity", ncSpeed: "ncSpeed", ncAnimationType: "ncAnimationType", ncColors: "ncColors", ncDistort: "ncDistort", ncPaused: "ncPaused", ncOffset: "ncOffset", ncHoverDampness: "ncHoverDampness", ncRayCount: "ncRayCount" }, host: { listeners: { "pointermove": "onPointerMove($event)", "document:visibilitychange": "onVisibilityChange()" } }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div #container class=\"nc-prismatic-burst-canvas-container\"></div>\r\n<div class=\"nc-content-wrapper\">\r\n <ng-content></ng-content>\r\n</div>\r\n", styles: [":host{display:block;position:relative;width:100%;height:100%;overflow:hidden}.nc-prismatic-burst-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: PrismaticBurstBackgroundComponent, decorators: [{ type: Component, args: [{ selector: 'nc-prismatic-burst-background', templateUrl: './prismatic-burst-background.component.html', styleUrls: ['./prismatic-burst-background.component.less'] }] }], ctorParameters: function () { return [{ type: i0.NgZone }]; }, propDecorators: { containerRef: [{ type: ViewChild, args: ['container', { static: true }] }], ncIntensity: [{ type: Input }], ncSpeed: [{ type: Input }], ncAnimationType: [{ type: Input }], ncColors: [{ type: Input }], ncDistort: [{ type: Input }], ncPaused: [{ type: Input }], ncOffset: [{ type: Input }], ncHoverDampness: [{ type: Input }], ncRayCount: [{ type: Input }], onPointerMove: [{ type: HostListener, args: ['pointermove', ['$event']] }], onVisibilityChange: [{ type: HostListener, args: ['document:visibilitychange'] }] } }); class NcPrismaticBurstBackgroundModule { } NcPrismaticBurstBackgroundModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcPrismaticBurstBackgroundModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); NcPrismaticBurstBackgroundModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcPrismaticBurstBackgroundModule, declarations: [PrismaticBurstBackgroundComponent], imports: [CommonModule], exports: [PrismaticBurstBackgroundComponent] }); NcPrismaticBurstBackgroundModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcPrismaticBurstBackgroundModule, imports: [[ CommonModule ]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcPrismaticBurstBackgroundModule, decorators: [{ type: NgModule, args: [{ declarations: [ PrismaticBurstBackgroundComponent ], imports: [ CommonModule ], exports: [ PrismaticBurstBackgroundComponent ] }] }] }); /** * Generated bundle index. Do not edit. */ export { NcPrismaticBurstBackgroundModule, PrismaticBurstBackgroundComponent }; //# sourceMappingURL=ng-cw-v12-prismatic-burst-background.js.map