Skip to content

intersect / NgtsIntersect

Utilities for tracking whether an object is within the camera’s view frustum. Uses THREE.js’s built-in frustum culling by monitoring onBeforeRender calls.

Creates a signal that tracks whether an object is visible in the camera’s frustum.

function intersect<TObject extends THREE.Object3D>(
object: () => ElementRef<TObject> | TObject | undefined | null,
options?: { injector?: Injector; source?: WritableSignal<boolean> },
): Signal<boolean>;
import { intersect } from 'angular-three-soba/misc';
@Component({
template: `
<ngt-mesh #mesh>
<ngt-box-geometry />
<ngt-mesh-basic-material />
</ngt-mesh>
`,
})
class MyComponent {
meshRef = viewChild<ElementRef<THREE.Mesh>>('mesh');
isVisible = intersect(() => this.meshRef());
constructor() {
effect(() => {
if (this.isVisible()) {
// Object is in view - start expensive animations
console.log('Mesh is visible');
} else {
// Object is out of view - pause expensive operations
console.log('Mesh is hidden');
}
});
}
}
ParameterTypeDescription
object() => ElementRef<Object3D> | Object3D | nullSignal of the object to track
optionsObject (optional)Configuration options
PropertyTypeDescription
injectorInjectorCustom injector for dependency injection
sourceWritableSignal<boolean>External signal to sync visibility state

Returns a read-only Signal<boolean> that emits true when the object is visible in the frustum.


A directive that tracks whether the host Object3D is in the camera frustum. Provides two-way binding for visibility state.

<ngt-mesh [(intersect)]="isInView">
<ngt-box-geometry />
<ngt-mesh-basic-material />
</ngt-mesh>
@Component({...})
class MyComponent {
isInView = signal(false);
constructor() {
effect(() => {
console.log('Mesh visible:', this.isInView());
});
}
}
<ngt-mesh (intersectChange)="onVisibilityChange($event)">
<ngt-sphere-geometry />
<ngt-mesh-standard-material />
</ngt-mesh>
onVisibilityChange(isVisible: boolean) {
console.log('Visibility changed:', isVisible);
}
class AnimatedMesh {
meshRef = viewChild<ElementRef<THREE.Mesh>>('mesh');
isVisible = intersect(() => this.meshRef());
constructor() {
beforeRender(({ delta }) => {
if (this.isVisible()) {
// Only animate when visible
this.meshRef()?.nativeElement.rotation.y += delta;
}
});
}
}
class LazyContent {
containerRef = viewChild<ElementRef<THREE.Group>>('container');
hasBeenVisible = signal(false);
isVisible = intersect(() => this.containerRef());
constructor() {
effect(() => {
if (this.isVisible() && !this.hasBeenVisible()) {
this.hasBeenVisible.set(true);
this.loadHeavyContent();
}
});
}
}
class OptimizedScene {
objects = viewChildren<ElementRef<THREE.Mesh>>('object');
// Track visibility for each object
visibilityStates = computed(() => this.objects().map((obj) => intersect(() => obj)));
}