ng-cw-v12
Version:
Angular UI Component Library
387 lines (377 loc) • 16.9 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 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