ng-cw-v12
Version:
Angular UI Component Library
480 lines (449 loc) • 18.5 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 vert = `
precision highp float;
attribute vec3 position;
attribute vec2 uv;
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = vec4(position, 1.0);
}
`;
const frag = `
precision highp float;
uniform float iTime;
uniform vec3 iResolution;
uniform float hue;
uniform float hover;
uniform float rot;
uniform float hoverIntensity;
uniform vec3 backgroundColor;
varying vec2 vUv;
vec3 rgb2yiq(vec3 c) {
float y = dot(c, vec3(0.299, 0.587, 0.114));
float i = dot(c, vec3(0.596, -0.274, -0.322));
float q = dot(c, vec3(0.211, -0.523, 0.312));
return vec3(y, i, q);
}
vec3 yiq2rgb(vec3 c) {
float r = c.x + 0.956 * c.y + 0.621 * c.z;
float g = c.x - 0.272 * c.y - 0.647 * c.z;
float b = c.x - 1.106 * c.y + 1.703 * c.z;
return vec3(r, g, b);
}
vec3 adjustHue(vec3 color, float hueDeg) {
float hueRad = hueDeg * 3.14159265 / 180.0;
vec3 yiq = rgb2yiq(color);
float cosA = cos(hueRad);
float sinA = sin(hueRad);
float i = yiq.y * cosA - yiq.z * sinA;
float q = yiq.y * sinA + yiq.z * cosA;
yiq.y = i;
yiq.z = q;
return yiq2rgb(yiq);
}
vec3 hash33(vec3 p3) {
p3 = fract(p3 * vec3(0.1031, 0.11369, 0.13787));
p3 += dot(p3, p3.yxz + 19.19);
return -1.0 + 2.0 * fract(vec3(
p3.x + p3.y,
p3.x + p3.z,
p3.y + p3.z
) * p3.zyx);
}
float snoise3(vec3 p) {
const float K1 = 0.333333333;
const float K2 = 0.166666667;
vec3 i = floor(p + (p.x + p.y + p.z) * K1);
vec3 d0 = p - (i - (i.x + i.y + i.z) * K2);
vec3 e = step(vec3(0.0), d0 - d0.yzx);
vec3 i1 = e * (1.0 - e.zxy);
vec3 i2 = 1.0 - e.zxy * (1.0 - e);
vec3 d1 = d0 - (i1 - K2);
vec3 d2 = d0 - (i2 - K1);
vec3 d3 = d0 - 0.5;
vec4 h = max(0.6 - vec4(
dot(d0, d0),
dot(d1, d1),
dot(d2, d2),
dot(d3, d3)
), 0.0);
vec4 n = h * h * h * h * vec4(
dot(d0, hash33(i)),
dot(d1, hash33(i + i1)),
dot(d2, hash33(i + i2)),
dot(d3, hash33(i + 1.0))
);
return dot(vec4(31.316), n);
}
vec4 extractAlpha(vec3 colorIn) {
float a = max(max(colorIn.r, colorIn.g), colorIn.b);
return vec4(colorIn.rgb / (a + 1e-5), a);
}
const vec3 baseColor1 = vec3(0.611765, 0.262745, 0.996078);
const vec3 baseColor2 = vec3(0.298039, 0.760784, 0.913725);
const vec3 baseColor3 = vec3(0.062745, 0.078431, 0.600000);
const float innerRadius = 0.6;
const float noiseScale = 0.65;
float light1(float intensity, float attenuation, float dist) {
return intensity / (1.0 + dist * attenuation);
}
float light2(float intensity, float attenuation, float dist) {
return intensity / (1.0 + dist * dist * attenuation);
}
vec4 draw(vec2 uv) {
vec3 color1 = adjustHue(baseColor1, hue);
vec3 color2 = adjustHue(baseColor2, hue);
vec3 color3 = adjustHue(baseColor3, hue);
float ang = atan(uv.y, uv.x);
float len = length(uv);
float invLen = len > 0.0 ? 1.0 / len : 0.0;
float bgLuminance = dot(backgroundColor, vec3(0.299, 0.587, 0.114));
float n0 = snoise3(vec3(uv * noiseScale, iTime * 0.5)) * 0.5 + 0.5;
float r0 = mix(mix(innerRadius, 1.0, 0.4), mix(innerRadius, 1.0, 0.6), n0);
float d0 = distance(uv, (r0 * invLen) * uv);
float v0 = light1(1.0, 10.0, d0);
v0 *= smoothstep(r0 * 1.05, r0, len);
float innerFade = smoothstep(r0 * 0.8, r0 * 0.95, len);
v0 *= mix(innerFade, 1.0, bgLuminance * 0.7);
float cl = cos(ang + iTime * 2.0) * 0.5 + 0.5;
float a = iTime * -1.0;
vec2 pos = vec2(cos(a), sin(a)) * r0;
float d = distance(uv, pos);
float v1 = light2(1.5, 5.0, d);
v1 *= light1(1.0, 50.0, d0);
float v2 = smoothstep(1.0, mix(innerRadius, 1.0, n0 * 0.5), len);
float v3 = smoothstep(innerRadius, mix(innerRadius, 1.0, 0.5), len);
vec3 colBase = mix(color1, color2, cl);
float fadeAmount = mix(1.0, 0.1, bgLuminance);
vec3 darkCol = mix(color3, colBase, v0);
darkCol = (darkCol + v1) * v2 * v3;
darkCol = clamp(darkCol, 0.0, 1.0);
vec3 lightCol = (colBase + v1) * mix(1.0, v2 * v3, fadeAmount);
lightCol = mix(backgroundColor, lightCol, v0);
lightCol = clamp(lightCol, 0.0, 1.0);
vec3 finalCol = mix(darkCol, lightCol, bgLuminance);
return extractAlpha(finalCol);
}
vec4 mainImage(vec2 fragCoord) {
vec2 center = iResolution.xy * 0.5;
float size = min(iResolution.x, iResolution.y);
vec2 uv = (fragCoord - center) / size * 2.0;
float angle = rot;
float s = sin(angle);
float c = cos(angle);
uv = vec2(c * uv.x - s * uv.y, s * uv.x + c * uv.y);
uv.x += hover * hoverIntensity * 0.1 * sin(uv.y * 10.0 + iTime);
uv.y += hover * hoverIntensity * 0.1 * sin(uv.x * 10.0 + iTime);
return draw(uv);
}
void main() {
vec2 fragCoord = vUv * iResolution.xy;
vec4 col = mainImage(fragCoord);
gl_FragColor = vec4(col.rgb * col.a, col.a);
}
`;
function hslToRgb(h, s, l) {
let r, g, b;
if (s === 0) {
r = g = b = l;
}
else {
const hue2rgb = (p, q, t) => {
if (t < 0)
t += 1;
if (t > 1)
t -= 1;
if (t < 1 / 6)
return p + (q - p) * 6 * t;
if (t < 1 / 2)
return q;
if (t < 2 / 3)
return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return new THREE.Vector3(r, g, b);
}
function hexToVec3(color) {
if (color.startsWith('#')) {
const r = parseInt(color.slice(1, 3), 16) / 255;
const g = parseInt(color.slice(3, 5), 16) / 255;
const b = parseInt(color.slice(5, 7), 16) / 255;
return new THREE.Vector3(r, g, b);
}
const rgbMatch = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
if (rgbMatch) {
return new THREE.Vector3(parseInt(rgbMatch[1], 10) / 255, parseInt(rgbMatch[2], 10) / 255, parseInt(rgbMatch[3], 10) / 255);
}
const hslMatch = color.match(/hsla?\((\d+),\s*(\d+)%,\s*(\d+)%/);
if (hslMatch) {
const h = parseInt(hslMatch[1], 10) / 360;
const s = parseInt(hslMatch[2], 10) / 100;
const l = parseInt(hslMatch[3], 10) / 100;
return hslToRgb(h, s, l);
}
return new THREE.Vector3(0, 0, 0);
}
class OrbBackgroundComponent {
constructor(ngZone) {
this.ngZone = ngZone;
/** 球体的基本色调(0-360)*/
this.ncHue = 0;
/** 控制悬停扭曲效果的强度(0-5)*/
this.ncHoverIntensity = 2;
/** 切换开关以启用或禁用鼠标悬停时的连续旋转 */
this._rotateOnHover = true;
/** 即使球体实际上并未悬停,也强制显示悬停动画 */
this._forceHoverState = false;
/** 容器的背景颜色,十六进制格式 */
this.ncBackgroundColor = '#000000';
this.rafId = null;
this.resizeRafId = null;
this.isVisible = true;
this.targetHover = 0;
this.lastTime = 0;
this.currentRot = 0;
this.rotationSpeed = 0.3;
}
set ncRotateOnHover(val) {
this._rotateOnHover = val !== null && val !== undefined && val !== false && val !== 'false';
}
get ncRotateOnHover() {
return this._rotateOnHover;
}
set ncForceHoverState(val) {
this._forceHoverState = val !== null && val !== undefined && val !== false && val !== 'false';
}
get ncForceHoverState() {
return this._forceHoverState;
}
ngOnInit() { }
ngAfterViewInit() {
this.initWebGL();
}
ngOnDestroy() {
this.cleanup();
}
ngOnChanges(changes) {
if (this.material) {
if (changes['ncHue']) {
this.material.uniforms['hue'].value = this.ncHue;
}
if (changes['ncHoverIntensity']) {
this.material.uniforms['hoverIntensity'].value = this.ncHoverIntensity;
}
if (changes['ncBackgroundColor']) {
this.material.uniforms['backgroundColor'].value = hexToVec3(this.ncBackgroundColor);
}
}
}
initWebGL() {
const container = this.containerRef.nativeElement;
this.renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true, premultipliedAlpha: false });
this.renderer.setClearColor(0x000000, 0); // Transparent background matching WebGL clearColor(0,0,0,0)
container.appendChild(this.renderer.domElement);
this.scene = new THREE.Scene();
this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
const geometry = new THREE.PlaneGeometry(2, 2);
this.material = new THREE.RawShaderMaterial({
vertexShader: vert,
fragmentShader: frag,
transparent: false,
blending: THREE.NoBlending,
uniforms: {
iTime: { value: 0 },
iResolution: { value: new THREE.Vector3(1, 1, 1) },
hue: { value: this.ncHue },
hover: { value: 0 },
rot: { value: 0 },
hoverIntensity: { value: this.ncHoverIntensity },
backgroundColor: { value: hexToVec3(this.ncBackgroundColor) }
}
});
this.plane = new THREE.Mesh(geometry, this.material);
this.scene.add(this.plane);
this.setupResizeObserver(container);
this.setupIntersectionObserver(container);
this.lastTime = performance.now();
this.ngZone.runOutsideAngular(() => {
const loop = (t) => {
this.rafId = requestAnimationFrame(loop);
if (this.isVisible) {
this.update(t);
}
};
loop(performance.now());
});
}
update(t) {
const dt = (t - this.lastTime) * 0.001;
this.lastTime = t;
this.material.uniforms['iTime'].value = t * 0.001;
const effectiveHover = this.ncForceHoverState ? 1 : this.targetHover;
this.material.uniforms['hover'].value += (effectiveHover - this.material.uniforms['hover'].value) * 0.1;
if (this.ncRotateOnHover && effectiveHover > 0.5) {
this.currentRot += dt * this.rotationSpeed;
}
this.material.uniforms['rot'].value = this.currentRot;
this.renderer.render(this.scene, this.camera);
}
handleResize() {
if (!this.containerRef || !this.renderer)
return;
const container = this.containerRef.nativeElement;
const dpr = Math.min(window.devicePixelRatio || 1, 2);
const width = container.clientWidth || 1;
const height = container.clientHeight || 1;
this.renderer.setPixelRatio(dpr);
this.renderer.setSize(width, height, false);
const canvas = this.renderer.domElement;
canvas.style.width = '100%';
canvas.style.height = '100%';
canvas.style.display = 'block';
if (this.material) {
this.material.uniforms['iResolution'].value.set(canvas.width, canvas.height, canvas.width / canvas.height);
}
}
setupResizeObserver(container) {
this.resizeObserver = new ResizeObserver(() => {
if (this.resizeRafId !== null)
cancelAnimationFrame(this.resizeRafId);
this.ngZone.runOutsideAngular(() => {
this.resizeRafId = requestAnimationFrame(() => {
this.handleResize();
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.renderer) {
const canvas = this.renderer.domElement;
if (canvas && canvas.parentNode) {
canvas.parentNode.removeChild(canvas);
}
this.renderer.dispose();
}
}
// ─── HostListeners for Mouse Interaction ──────────────────────────
onMouseMove(event) {
if (!this.containerRef)
return;
const container = this.containerRef.nativeElement;
const rect = container.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const width = rect.width;
const height = rect.height;
// Prevent division by zero
if (width === 0 || height === 0)
return;
const size = Math.min(width, height);
const centerX = width / 2;
const centerY = height / 2;
const uvX = ((x - centerX) / size) * 2.0;
const uvY = ((y - centerY) / size) * 2.0;
if (Math.sqrt(uvX * uvX + uvY * uvY) < 0.8) {
this.targetHover = 1;
}
else {
this.targetHover = 0;
}
}
onMouseLeave() {
this.targetHover = 0;
}
}
OrbBackgroundComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: OrbBackgroundComponent, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
OrbBackgroundComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: OrbBackgroundComponent, selector: "nc-orb-background", inputs: { ncHue: "ncHue", ncHoverIntensity: "ncHoverIntensity", ncRotateOnHover: "ncRotateOnHover", ncForceHoverState: "ncForceHoverState", ncBackgroundColor: "ncBackgroundColor" }, host: { listeners: { "mousemove": "onMouseMove($event)", "mouseleave": "onMouseLeave()" } }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div #container class=\"nc-orb-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-orb-canvas-container{position:absolute;inset:0;z-index:0;background-color:#000}.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: OrbBackgroundComponent, decorators: [{
type: Component,
args: [{
selector: 'nc-orb-background',
templateUrl: './orb-background.component.html',
styleUrls: ['./orb-background.component.less']
}]
}], ctorParameters: function () { return [{ type: i0.NgZone }]; }, propDecorators: { containerRef: [{
type: ViewChild,
args: ['container', { static: true }]
}], ncHue: [{
type: Input
}], ncHoverIntensity: [{
type: Input
}], ncRotateOnHover: [{
type: Input
}], ncForceHoverState: [{
type: Input
}], ncBackgroundColor: [{
type: Input
}], onMouseMove: [{
type: HostListener,
args: ['mousemove', ['$event']]
}], onMouseLeave: [{
type: HostListener,
args: ['mouseleave']
}] } });
class NcOrbBackgroundModule {
}
NcOrbBackgroundModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcOrbBackgroundModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
NcOrbBackgroundModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcOrbBackgroundModule, declarations: [OrbBackgroundComponent], imports: [CommonModule], exports: [OrbBackgroundComponent] });
NcOrbBackgroundModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcOrbBackgroundModule, imports: [[
CommonModule
]] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcOrbBackgroundModule, decorators: [{
type: NgModule,
args: [{
declarations: [
OrbBackgroundComponent
],
imports: [
CommonModule
],
exports: [
OrbBackgroundComponent
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { NcOrbBackgroundModule, OrbBackgroundComponent };
//# sourceMappingURL=ng-cw-v12-orb-background.js.map