ng-cw-v12
Version:
Angular UI Component Library
261 lines (251 loc) • 11.7 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 = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const fragmentShader = `
precision highp float;
uniform float uTime;
uniform vec3 uResolution;
uniform vec3 uBaseColor;
uniform float uAmplitude;
uniform float uFrequencyX;
uniform float uFrequencyY;
uniform vec2 uMouse;
varying vec2 vUv;
vec4 renderImage(vec2 uvCoord) {
vec2 fragCoord = uvCoord * uResolution.xy;
vec2 uv = (2.0 * fragCoord - uResolution.xy) / min(uResolution.x, uResolution.y);
for (float i = 1.0; i < 10.0; i++){
uv.x += uAmplitude / i * cos(i * uFrequencyX * uv.y + uTime + uMouse.x * 3.14159);
uv.y += uAmplitude / i * cos(i * uFrequencyY * uv.x + uTime + uMouse.y * 3.14159);
}
vec2 diff = (uvCoord - uMouse);
float dist = length(diff);
float falloff = exp(-dist * 20.0);
float ripple = sin(10.0 * dist - uTime * 2.0) * 0.03;
uv += (diff / (dist + 0.0001)) * ripple * falloff;
vec3 color = uBaseColor / abs(sin(uTime - uv.y - uv.x));
return vec4(color, 1.0);
}
void main() {
vec4 col = vec4(0.0);
int samples = 0;
for (int i = -1; i <= 1; i++){
for (int j = -1; j <= 1; j++){
vec2 offset = vec2(float(i), float(j)) * (1.0 / min(uResolution.x, uResolution.y));
col += renderImage(vUv + offset);
samples++;
}
}
gl_FragColor = col / float(samples);
}
`;
class LiquidChromeBackgroundComponent {
constructor(ngZone) {
this.ngZone = ngZone;
/** 基础颜色数组 [r, g, b](0-1) */
this.ncBaseColor = [0.1, 0.1, 0.1];
/** 动画运行速度(0-5) */
this.ncSpeed = 0.2;
/** 波动幅度(0.1-1) */
this.ncAmplitude = 0.3;
/** X轴波频 */
this.ncFrequencyX = 3;
/** Y轴波频 */
this.ncFrequencyY = 3;
/** 是否允许鼠标/触屏交互 */
this._interactive = true;
this.animationId = null;
this.mouse = new THREE.Vector2(0, 0);
this.update = (t) => {
this.animationId = requestAnimationFrame(this.update);
if (this.material) {
this.material.uniforms['uTime'].value = t * 0.001 * this.ncSpeed;
}
this.renderer.render(this.scene, this.camera);
};
}
set ncInteractive(value) {
this._interactive = value !== null && value !== undefined && value !== false && value !== 'false';
}
get ncInteractive() {
return this._interactive;
}
ngOnInit() { }
ngAfterViewInit() {
this.initWebGL();
}
ngOnDestroy() {
this.cleanup();
}
ngOnChanges(changes) {
if (!this.material)
return;
if (changes['ncBaseColor'] && changes['ncBaseColor'].currentValue) {
this.material.uniforms['uBaseColor'].value.fromArray(this.ncBaseColor);
}
if (changes['ncAmplitude'] !== undefined) {
this.material.uniforms['uAmplitude'].value = this.ncAmplitude;
}
if (changes['ncFrequencyX'] !== undefined) {
this.material.uniforms['uFrequencyX'].value = this.ncFrequencyX;
}
if (changes['ncFrequencyY'] !== undefined) {
this.material.uniforms['uFrequencyY'].value = this.ncFrequencyY;
}
// ncInteractive handled via HostListener check
}
initWebGL() {
const container = this.containerRef.nativeElement;
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
// 原版 jsx 清除颜色为白色
this.renderer.setClearColor(0xffffff, 1);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
container.appendChild(this.renderer.domElement);
this.scene = new THREE.Scene();
// 正交相机渲染全屏 Plane
this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 10);
this.camera.position.z = 1;
const geometry = new THREE.PlaneGeometry(2, 2);
this.material = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms: {
uTime: { value: 0 },
uResolution: { value: new THREE.Vector3() },
uBaseColor: { value: new THREE.Vector3().fromArray(this.ncBaseColor) },
uAmplitude: { value: this.ncAmplitude },
uFrequencyX: { value: this.ncFrequencyX },
uFrequencyY: { value: this.ncFrequencyY },
uMouse: { value: this.mouse }
}
});
this.mesh = new THREE.Mesh(geometry, this.material);
this.scene.add(this.mesh);
this.resizeObserver = new ResizeObserver(() => this.resize());
this.resizeObserver.observe(container);
this.resize();
// Interaction handled via HostListener
// 提升性能,不让 requestAnimationFrame 触发 Angular 变更检测
this.ngZone.runOutsideAngular(() => {
this.update(0);
});
}
resize() {
if (!this.containerRef || !this.renderer)
return;
const container = this.containerRef.nativeElement;
const width = container.offsetWidth;
const height = container.offsetHeight;
this.renderer.setSize(width, height);
this.material.uniforms['uResolution'].value.set(width, height, width / height);
}
onMouseMove(event) {
if (!this.ncInteractive || !this.containerRef)
return;
const container = this.containerRef.nativeElement;
const rect = container.getBoundingClientRect();
const x = (event.clientX - rect.left) / rect.width;
const y = 1 - (event.clientY - rect.top) / rect.height;
this.mouse.set(x, y);
}
onTouchMove(event) {
if (!this.ncInteractive || !this.containerRef)
return;
if (event.touches.length > 0) {
const touch = event.touches[0];
const container = this.containerRef.nativeElement;
const rect = container.getBoundingClientRect();
const x = (touch.clientX - rect.left) / rect.width;
const y = 1 - (touch.clientY - rect.top) / rect.height;
this.mouse.set(x, y);
}
}
cleanup() {
var _a, _b;
if (this.animationId !== null) {
cancelAnimationFrame(this.animationId);
this.animationId = null;
}
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
if (this.mesh) {
this.mesh.geometry.dispose();
this.mesh.material.dispose();
this.scene.remove(this.mesh);
}
if (this.renderer) {
this.renderer.dispose();
const dom = this.renderer.domElement;
if (dom && dom.parentElement) {
dom.parentElement.removeChild(dom);
}
(_b = (_a = this.renderer.getContext()) === null || _a === void 0 ? void 0 : _a.getExtension('WEBGL_lose_context')) === null || _b === void 0 ? void 0 : _b.loseContext();
}
}
}
LiquidChromeBackgroundComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: LiquidChromeBackgroundComponent, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
LiquidChromeBackgroundComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: LiquidChromeBackgroundComponent, selector: "nc-liquid-chrome-background", inputs: { ncBaseColor: "ncBaseColor", ncSpeed: "ncSpeed", ncAmplitude: "ncAmplitude", ncFrequencyX: "ncFrequencyX", ncFrequencyY: "ncFrequencyY", ncInteractive: "ncInteractive" }, host: { listeners: { "mousemove": "onMouseMove($event)", "touchmove": "onTouchMove($event)" } }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div #container class=\"nc-liquid-chrome-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-liquid-chrome-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: LiquidChromeBackgroundComponent, decorators: [{
type: Component,
args: [{
selector: 'nc-liquid-chrome-background',
templateUrl: './liquid-chrome-background.component.html',
styleUrls: ['./liquid-chrome-background.component.less']
}]
}], ctorParameters: function () { return [{ type: i0.NgZone }]; }, propDecorators: { containerRef: [{
type: ViewChild,
args: ['container', { static: true }]
}], ncBaseColor: [{
type: Input
}], ncSpeed: [{
type: Input
}], ncAmplitude: [{
type: Input
}], ncFrequencyX: [{
type: Input
}], ncFrequencyY: [{
type: Input
}], ncInteractive: [{
type: Input
}], onMouseMove: [{
type: HostListener,
args: ['mousemove', ['$event']]
}], onTouchMove: [{
type: HostListener,
args: ['touchmove', ['$event']]
}] } });
class NcLiquidChromeBackgroundModule {
}
NcLiquidChromeBackgroundModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcLiquidChromeBackgroundModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
NcLiquidChromeBackgroundModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcLiquidChromeBackgroundModule, declarations: [LiquidChromeBackgroundComponent], imports: [CommonModule], exports: [LiquidChromeBackgroundComponent] });
NcLiquidChromeBackgroundModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcLiquidChromeBackgroundModule, imports: [[
CommonModule
]] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcLiquidChromeBackgroundModule, decorators: [{
type: NgModule,
args: [{
declarations: [
LiquidChromeBackgroundComponent
],
imports: [
CommonModule
],
exports: [
LiquidChromeBackgroundComponent
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { LiquidChromeBackgroundComponent, NcLiquidChromeBackgroundModule };
//# sourceMappingURL=ng-cw-v12-liquid-chrome-background.js.map