ng-cw-v12
Version:
Angular UI Component Library
513 lines (483 loc) • 21 kB
JavaScript
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