@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
136 lines (119 loc) • 4.09 kB
text/typescript
import { Color, Texture } from 'three';
import { serializable } from '../../engine/engine_serialization_decorator.js';
import { MaskableGraphic } from './Graphic.js';
class Sprite {
texture: Texture | null = null;
rect?: { width: number, height: number };
}
/**
* [Image](https://engine.needle.tools/docs/api/Image) displays a sprite (2D texture) in the UI. Can be used for icons,
* backgrounds, or any visual element that needs a texture.
*
* **Properties:**
* - `image` - Direct texture assignment (convenience property)
* - `sprite` - Sprite object containing texture and rect info
* - `color` - Tint color applied to the image (inherited from Graphic)
*
* **Usage with Button:**
* Image is commonly paired with {@link Button} to create clickable
* UI elements with visual feedback via color tinting.
*
* @example Set an image texture
* ```ts
* const img = myIcon.getComponent(Image);
* img.image = myTexture;
* img.color = new RGBAColor(1, 0.5, 0.5, 1); // Red tint
* ```
*
* @summary Display a 2D image in the UI
* @category User Interface
* @group Components
* @see {@link Canvas} for the UI root
* @see {@link Button} for clickable images
* @see {@link RawImage} for non-UI image display
*/
export class Image extends MaskableGraphic {
set image(img: Texture | null) {
if (!this.sprite) this.sprite = new Sprite();
this.sprite.texture = img;
this.onAfterCreated();
}
get image(): Texture | null {
if (this.sprite)
return this.sprite.texture;
return null;
}
get sprite(): Sprite | undefined {
return this._sprite;
}
set sprite(sprite: Sprite | undefined) {
if (this._sprite === sprite) return;
this._sprite = sprite;
this.onAfterCreated();
}
private _sprite?: Sprite;
private pixelsPerUnitMultiplier: number = 1;
private isBuiltinSprite() {
const sprite = this.sprite;
switch (sprite?.texture?.name) {
case "InputFieldBackground":
case "UISprite":
case "Background":
case "Knob":
return true;
}
// this is a hack/workaround for production builds where the name of the sprite is missing
// need to remove this!!!!
if (!sprite?.texture?.name?.length && sprite?.texture?.image?.width === 32 && sprite?.texture?.image?.height === 32)
return true;
return false;
}
protected onBeforeCreate(opts: any): void {
super.onBeforeCreate(opts);
if (this.isBuiltinSprite()) {
opts.borderRadius = 5 / this.pixelsPerUnitMultiplier;
if(this.sprite?.texture?.name === "Knob") {
opts.borderRadius = 999;
}
// opts.borderColor = new Color(.4, .4, .4);
// opts.borderOpacity = this.color.alpha;
// opts.borderWidth = .3;
}
}
protected onAfterCreated(): void {
if (!this.__didAwake) return;
super.onAfterCreated();
// TODO: @swingingtom setting a built-in sprite at runtime doesnt update the image
if (this.isBuiltinSprite()) return;
this.setTexture(this.sprite?.texture);
}
}
/**
* @category User Interface
* @group Components
*/
export class RawImage extends MaskableGraphic {
get mainTexture(): Texture | undefined {
return this._mainTexture;
}
set mainTexture(texture: Texture | undefined) {
if (this._mainTexture === texture) return;
this._mainTexture = texture;
this.onAfterCreated();
}
private _mainTexture?: Texture;
protected onAfterCreated(): void {
if(!this.__didAwake) return;
super.onAfterCreated();
// console.log(this);
// if (this.mainTexture) {
// this.mainTexture.flipY = true;
// this.mainTexture.needsUpdate = true;
// }
this.setTexture(this.mainTexture);
}
}