• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excaliburjs / Excalibur / 14804036802

02 May 2025 09:58PM UTC coverage: 5.927% (-83.4%) from 89.28%
14804036802

Pull #3404

github

web-flow
Merge 5c103d7f8 into 0f2ccaeb2
Pull Request #3404: feat: added Graph module to Math

234 of 8383 branches covered (2.79%)

229 of 246 new or added lines in 1 file covered. (93.09%)

13145 existing lines in 208 files now uncovered.

934 of 15759 relevant lines covered (5.93%)

4.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/engine/Graphics/Sprite.ts
1
import { Graphic, GraphicOptions } from './Graphic';
2
import { ImageSource } from './ImageSource';
3
import { ExcaliburGraphicsContext } from './Context/ExcaliburGraphicsContext';
4
import { Logger } from '../Util/Log';
5

6
export type SourceView = { x: number; y: number; width: number; height: number };
7
export type DestinationSize = { width: number; height: number };
8

9
export interface SpriteOptions {
10
  /**
11
   * Image to create a sprite from
12
   */
13
  image: ImageSource;
14
  /**
15
   * By default the source is the entire dimension of the {@apilink ImageSource}
16
   */
17
  sourceView?: { x: number; y: number; width: number; height: number };
18
  /**
19
   * By default the size of the final sprite is the size of the {@apilink ImageSource}
20
   */
21
  destSize?: { width: number; height: number };
22
}
23

24
export class Sprite extends Graphic {
UNCOV
25
  private _logger = Logger.getInstance();
×
26
  public image: ImageSource;
27
  public sourceView: SourceView;
28
  public destSize: DestinationSize;
UNCOV
29
  private _dirty = true;
×
30

31
  public static from(image: ImageSource, options?: Omit<GraphicOptions & SpriteOptions, 'image'>): Sprite {
UNCOV
32
    return new Sprite({
×
33
      image,
34
      ...options
35
    });
36
  }
37

38
  constructor(options: GraphicOptions & SpriteOptions) {
UNCOV
39
    super(options);
×
UNCOV
40
    this.image = options.image;
×
UNCOV
41
    const { width, height } = options;
×
UNCOV
42
    this.sourceView = options.sourceView ?? { x: 0, y: 0, width: width ?? 0, height: height ?? 0 };
×
UNCOV
43
    this.destSize = options.destSize ?? { width: width ?? 0, height: height ?? 0 };
×
UNCOV
44
    this._updateSpriteDimensions();
×
45
    // Fire when loaded
46
    // eslint-disable-next-line @typescript-eslint/no-floating-promises
UNCOV
47
    this.image.ready.then(() => {
×
UNCOV
48
      this._updateSpriteDimensions();
×
49
    });
50
  }
51

52
  public override get width(): number {
UNCOV
53
    return Math.abs(this.destSize.width * this.scale.x);
×
54
  }
55

56
  public override get height(): number {
UNCOV
57
    return Math.abs(this.destSize.height * this.scale.y);
×
58
  }
59

60
  public override set width(newWidth: number) {
UNCOV
61
    newWidth /= Math.abs(this.scale.x);
×
UNCOV
62
    this.destSize.width = newWidth;
×
UNCOV
63
    super.width = Math.ceil(this.destSize.width);
×
64
  }
65

66
  public override set height(newHeight: number) {
UNCOV
67
    newHeight /= Math.abs(this.scale.y);
×
UNCOV
68
    this.destSize.height = newHeight;
×
UNCOV
69
    super.height = Math.ceil(this.destSize.height);
×
70
  }
71

72
  private _updateSpriteDimensions() {
UNCOV
73
    const { width: nativeWidth, height: nativeHeight } = this.image;
×
74
    // This code uses || to avoid 0's
75
    // If the source is not specified, use the native dimension
UNCOV
76
    this.sourceView.width = this.sourceView?.width || nativeWidth;
×
UNCOV
77
    this.sourceView.height = this.sourceView?.height || nativeHeight;
×
78

79
    // If the destination is not specified, use the source if specified, then native
UNCOV
80
    this.destSize.width = this.destSize?.width || this.sourceView?.width || nativeWidth;
×
UNCOV
81
    this.destSize.height = this.destSize?.height || this.sourceView?.height || nativeHeight;
×
82

UNCOV
83
    this.width = Math.ceil(this.destSize.width) * this.scale.x;
×
UNCOV
84
    this.height = Math.ceil(this.destSize.height) * this.scale.y;
×
85
  }
86

87
  protected _preDraw(ex: ExcaliburGraphicsContext, x: number, y: number): void {
UNCOV
88
    if (this.image.isLoaded() && this._dirty) {
×
UNCOV
89
      this._dirty = false;
×
UNCOV
90
      this._updateSpriteDimensions();
×
91
    }
UNCOV
92
    super._preDraw(ex, x, y);
×
93
  }
94

95
  public _drawImage(ex: ExcaliburGraphicsContext, x: number, y: number): void {
UNCOV
96
    if (this.image.isLoaded()) {
×
UNCOV
97
      ex.drawImage(
×
98
        this.image.image,
99
        this.sourceView.x,
100
        this.sourceView.y,
101
        this.sourceView.width,
102
        this.sourceView.height,
103
        x,
104
        y,
105
        this.destSize.width,
106
        this.destSize.height
107
      );
108
    } else {
UNCOV
109
      this._logger.warnOnce(
×
110
        `ImageSource ${this.image.path}` +
111
          ` is not yet loaded and won't be drawn. Please call .load() or include in a Loader.\n\n` +
112
          `Read https://excaliburjs.com/docs/imagesource for more information.`
113
      );
114
    }
115
  }
116

117
  public clone(): Sprite {
UNCOV
118
    return new Sprite({
×
119
      image: this.image,
120
      sourceView: { ...this.sourceView },
121
      destSize: { ...this.destSize },
122
      ...this.cloneGraphicOptions()
123
    });
124
  }
125
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc