- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 434
- 435
- 436
- 437
- 438
- 439
- 440
- 441
- 442
- 443
- 444
- 445
- 446
- 447
- 448
- 449
- 450
- 451
- 452
- 453
- 454
- 455
- 456
- 457
- 458
- 459
- 460
- 461
- 462
- 463
- 464
- 465
- 466
- 467
- 468
- 469
- 470
- 471
- 472
- 473
- 474
- 475
- 476
- 477
- 478
- 479
- 480
- 481
- 482
- 483
- 484
- 485
- 486
- 487
- 488
- 489
- 490
- 491
- 492
- 493
- 494
- 495
- 496
- 497
- 498
- 499
- 500
- 501
- 502
- 503
- 504
- 505
- 506
- 507
- 508
- 509
- 510
- 511
- 512
- 513
- 514
- 515
- 516
- 517
- 518
- 519
- 520
- 521
- 522
- 523
- 524
- 525
- 526
- 527
- 528
- 529
- 530
- 531
- 532
- 533
- 534
- 535
- 536
- 537
- 538
- 539
- 540
- 541
- 542
- 543
- 544
- 545
- 546
- 547
- 548
- 549
- 550
- 551
- 552
- 553
- 554
- 555
- 556
- 557
- 558
- 559
- 560
- 561
- 562
- 563
- 564
- 565
- 566
- 567
- 568
- 569
- 570
- 571
- 572
- 573
- 574
- 575
- 576
- 577
- 578
- 579
- 580
- 581
- 582
- 583
- 584
- 585
- 586
- 587
- 588
- 589
- 590
- 591
- 592
- 593
- 594
- 595
- 596
- 597
- 598
- 599
- 600
- 601
- 602
- 603
- 604
- 605
- 606
- 607
- 608
- 609
- 610
- 611
- 612
- 613
- 614
- 615
- 616
- 617
- 618
- 619
- 620
- 621
- 622
- 623
- 624
- 625
- 626
- 627
- 628
- 629
- 630
- 631
- 632
- 633
- 634
- 635
- 636
- 637
- 638
- 639
- 640
- 641
- 642
- 643
- 644
- 645
- 646
- 647
- 648
- 649
- 650
- 651
- 652
- 653
- 654
- 655
- 656
- 657
- 658
- 659
- 660
- 661
- 662
- 663
- 664
- 665
- 666
- 667
- 668
- 669
- 670
- 671
- 672
- 673
- 674
- 675
- 676
- 677
- 678
- 679
- 680
- 681
- 682
- 683
- 684
- 685
- 686
- 687
- 688
- 689
- 690
- 691
import { renderer } from "./../video/video.js";
import pool from "./../system/pooling.js";
import { getImage } from "./../loader/loader.js";
import { TextureAtlas } from "./../video/texture/atlas.js";
import Renderable from "./renderable.js";
import Color from "../math/color.js";
import * as event from "../system/event.js";
/**
* @import Vector2d from "./../math/vector2.js";
* @import CanvasRenderer from "./../video/canvas/canvas_renderer.js";
* @import WebGLRenderer from "./../video/webgl/webgl_renderer.js";
*/
/**
* @classdesc
* An object to display a fixed or animated sprite on screen.
* @augments Renderable
*/
export default class Sprite extends Renderable {
/**
* @param {number} x - the x coordinates of the sprite object
* @param {number} y - the y coordinates of the sprite object
* @param {object} settings - Configuration parameters for the Sprite object
* @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement|TextureAtlas|string} settings.image - reference to spritesheet image, a texture atlas, a video element, or to a texture atlas
* @param {string} [settings.name=""] - name of this object
* @param {string} [settings.region] - region name of a specific region to use when using a texture atlas, see {@link TextureAtlas}
* @param {number} [settings.framewidth] - Width of a single frame within the spritesheet
* @param {number} [settings.frameheight] - Height of a single frame within the spritesheet
* @param {string|Color} [settings.tint] - a tint to be applied to this sprite
* @param {number} [settings.flipX] - flip the sprite on the horizontal axis
* @param {number} [settings.flipY] - flip the sprite on the vertical axis
* @param {Vector2d} [settings.anchorPoint={x:0.5, y:0.5}] - Anchor point to draw the frame at (defaults to the center of the frame).
* @example
* // create a single sprite from a standalone image, with anchor in the center
* let sprite = new me.Sprite(0, 0, {
* image : "PlayerTexture",
* framewidth : 64,
* frameheight : 64,
* anchorPoint : new me.Vector2d(0.5, 0.5)
* });
*
* // create a single sprite from a packed texture
* mytexture = new me.TextureAtlas(
* me.loader.getJSON("texture"),
* me.loader.getImage("texture")
* );
* let sprite = new me.Sprite(0, 0, {
* image : mytexture,
* region : "npc2.png",
* });
*
* // create a video sprite
* let videoSprite = new me.Sprite(0, 0, {
* image : me.loader.getVideo("bigbunny"),
* anchorPoint : new me.Vector2d(0.5, 0.5)
* });
* // scale the video sprite
* videoSprite.currentTransform.scale(2);
* // start playing the video (if video is preloaded with `autoplay` set to false)
* videoSprite.play();
*/
constructor(x, y, settings) {
// call the super constructor
super(x, y, 0, 0);
/**
* @type {boolean}
* @default false
*/
this.animationpause = false;
/**
* animation cycling speed (delay between frame in ms)
* @type {number}
* @default 100
*/
this.animationspeed = 100;
/**
* global offset for the position to draw from on the source image.
* @type {Vector2d}
* @default <0.0,0.0>
*/
this.offset = pool.pull("Vector2d", 0, 0);
/**
* true if this is a video sprite (e.g. a HTMLVideoElement was passed as as source)
* @type {boolean}
* @default false
*/
this.isVideo = false;
/**
* a callback fired when the end of a video or current animation was reached
* @type {Function}
* @default undefined
*/
this.onended;
/**
* The source texture object this sprite object is using
* @type {TextureAtlas}
*/
this.source = null;
// hold all defined animation
this.anim = {};
// a flag to reset animation
this.resetAnim = undefined;
// current frame information
// (reusing current, any better/cleaner place?)
this.current = {
// the current animation name
name : undefined,
// length of the current animation name
length : 0,
//current frame texture offset
offset : pool.pull("Vector2d", 0, 0),
// current frame size
width : 0,
height : 0,
// Source rotation angle for pre-rotating the source image
angle : 0,
// current frame index
idx : 0
};
// animation frame delta
this.dt = 0;
// flicker settings
this._flicker = {
isFlickering : false,
duration : 0,
callback : null,
state : false
};
// set the proper image/texture to use
if (settings.image instanceof TextureAtlas) {
this.source = settings.image;
this.image = this.source.getTexture();
this.textureAtlas = settings.image;
// check for defined region
if (typeof (settings.region) !== "undefined") {
// use a texture atlas
let region = this.source.getRegion(settings.region);
if (region) {
// set the sprite region within the texture
this.setRegion(region);
} else {
// throw an error
throw new Error("Texture - region for " + settings.region + " not found");
}
}
} else {
// HTMLImageElement/HTMLVideoElementCanvas or {string}
this.image = (typeof settings.image === "object") ? settings.image : getImage(settings.image);
// throw an error if image ends up being null/undefined
if (!this.image) {
throw new Error("me.Sprite: '" + settings.image + "' image/texture not found!");
}
this.isVideo = HTMLVideoElement && this.image instanceof HTMLVideoElement;
if (this.isVideo) {
this.width = this.current.width = settings.framewidth = settings.framewidth || this.image.videoWidth;
this.height = this.current.height = settings.frameheight = settings.frameheight || this.image.videoHeight;
// video specific parameter
this.animationpause = this.image.autoplay !== true;
if (this.animationpause) {
this.image.pause();
}
// pause the video when losing focus
this._onBlurFn = () => { this.image.pause(); };
event.on(event.STATE_PAUSE, this._onBlurFn);
// call the onended when the video has ended
this.image.onended = () => {
if (typeof this.onended === "function") {
// prevent the video from restarting if video.loop is false
if (!this.image.loop) {
this.animationpause = true;
}
this.onended();
}
};
} else {
// update the default "current" frame size
this.width = this.current.width = settings.framewidth = settings.framewidth || this.image.width;
this.height = this.current.height = settings.frameheight = settings.frameheight || this.image.height;
this.source = renderer.cache.get(this.image, settings);
this.textureAtlas = this.source.getAtlas();
}
}
// store/reset the current atlas information if specified
if (typeof(settings.atlas) !== "undefined") {
this.textureAtlas = settings.atlas;
this.atlasIndices = settings.atlasIndices;
}
// apply flip flags if specified
if (typeof (settings.flipX) !== "undefined") {
this.flipX(!!settings.flipX);
}
if (typeof (settings.flipY) !== "undefined") {
this.flipY(!!settings.flipY);
}
// set the default rotation angle is defined in the settings
// * WARNING: rotating sprites decreases performance with Canvas Renderer
if (typeof (settings.rotation) !== "undefined") {
this.rotate(settings.rotation);
}
// update anchorPoint
if (settings.anchorPoint) {
this.anchorPoint.set(settings.anchorPoint.x, settings.anchorPoint.y);
}
if (typeof (settings.tint) !== "undefined") {
if (settings.tint instanceof Color) {
this.tint.copy(settings.tint);
} else {
// string (#RGB, #ARGB, #RRGGBB, #AARRGGBB)
this.tint.parseCSS(settings.tint);
}
}
// set the sprite name if specified
if (typeof (settings.name) === "string") {
this.name = settings.name;
}
// displaying order
if (typeof settings.z !== "undefined") {
this.pos.z = settings.z;
}
// add predefined animations if defined (e.g. aseprite)
if (typeof settings.anims !== "undefined") {
for (const anim in settings.anims) {
this.addAnimation(settings.anims[anim].name, settings.anims[anim].index, settings.anims[anim].speed);
}
}
// addAnimation will return 0 if no texture atlas is defined
if (!this.isVideo && this.addAnimation("default", null) !== 0) {
// set as default
this.setCurrentAnimation("default");
}
}
/**
* return the flickering state of the object
* @returns {boolean}
*/
isFlickering() {
return this._flicker.isFlickering;
}
/**
* play or resume the current animation or video
*/
play() {
this.animationpause = false;
}
/**
* play or resume the current animation or video
*/
pause() {
this.animationpause = true;
}
/**
* make the object flicker
* @param {number} duration - expressed in milliseconds
* @param {Function} callback - Function to call when flickering ends
* @returns {Sprite} Reference to this object for method chaining
* @example
* // make the object flicker for 1 second
* // and then remove it
* this.flicker(1000, function () {
* world.removeChild(this);
* });
*/
flicker(duration, callback) {
this._flicker.duration = duration;
if (this._flicker.duration <= 0) {
this._flicker.isFlickering = false;
this._flicker.callback = null;
}
else if (!this._flicker.isFlickering) {
this._flicker.callback = callback;
this._flicker.isFlickering = true;
}
return this;
}
/**
* add an animation <br>
* For fixed-sized cell sprite sheet, the index list must follow the
* logic as per the following example :<br>
* <img src="images/spritesheet_grid.png"/>
* @param {string} name - animation id
* @param {number[]|string[]|object[]} index - list of sprite index or name defining the animation. Can also use objects to specify delay for each frame, see below
* @param {number} [animationspeed] - cycling speed for animation in ms
* @returns {number} frame amount of frame added to the animation (delay between each frame).
* @see Sprite#animationspeed
* @example
* // walking animation
* this.addAnimation("walk", [ 0, 1, 2, 3, 4, 5 ]);
* // standing animation
* this.addAnimation("stand", [ 11, 12 ]);
* // eating animation
* this.addAnimation("eat", [ 6, 6 ]);
* // rolling animation
* this.addAnimation("roll", [ 7, 8, 9, 10 ]);
* // slower animation
* this.addAnimation("roll", [ 7, 8, 9, 10 ], 200);
* // or get more specific with delay for each frame. Good solution instead of repeating:
* this.addAnimation("turn", [{ name: 0, delay: 200 }, { name: 1, delay: 100 }])
* // can do this with atlas values as well:
* this.addAnimation("turn", [{ name: "turnone", delay: 200 }, { name: "turntwo", delay: 100 }])
* // define an dying animation that stop on the last frame
* this.addAnimation("die", [{ name: 3, delay: 200 }, { name: 4, delay: 100 }, { name: 5, delay: Infinity }])
* // set the standing animation as default
* this.setCurrentAnimation("stand");
*/
addAnimation(name, index, animationspeed) {
this.anim[name] = {
name : name,
frames : [],
idx : 0,
length : 0
};
// # of frames
let counter = 0;
if (typeof (this.textureAtlas) !== "object") {
return 0;
}
if (index == null) {
index = [];
// create a default animation with all frame
Object.keys(this.textureAtlas).forEach((v, i) => {
index[i] = i;
});
}
// set each frame configuration (offset, size, etc..)
for (let i = 0, len = index.length; i < len; i++) {
let frame = index[i];
let frameObject;
if (typeof(frame) === "number" || typeof(frame) === "string") {
frameObject = {
name: frame,
delay: animationspeed || this.animationspeed
};
}
else {
frameObject = frame;
}
let frameObjectName = frameObject.name;
if (typeof(frameObjectName) === "number") {
if (typeof (this.textureAtlas[frameObjectName]) !== "undefined") {
// TODO: adding the cache source coordinates add undefined entries in webGL mode
this.anim[name].frames[i] = Object.assign(
{},
this.textureAtlas[frameObjectName],
frameObject
);
counter++;
}
} else { // string
if (this.source.getFormat().includes("Spritesheet")) {
throw new Error(
"string parameters for addAnimation are not allowed for standard spritesheet based Texture"
);
} else {
this.anim[name].frames[i] = Object.assign(
{},
this.textureAtlas[this.atlasIndices[frameObjectName]],
frameObject
);
counter++;
}
}
}
this.anim[name].length = counter;
return counter;
}
/**
* set the current animation
* this will always change the animation & set the frame to zero
* @param {string} name - animation id
* @param {string|Function} [resetAnim] - animation id to switch to when complete, or callback
* @param {boolean} [preserve_dt=false] - if false will reset the elapsed time counter since last frame
* @returns {Sprite} Reference to this object for method chaining
* @example
* // set "walk" animation
* this.setCurrentAnimation("walk");
*
* // set "walk" animation if it is not the current animation
* if (this.isCurrentAnimation("walk")) {
* this.setCurrentAnimation("walk");
* }
*
* // set "eat" animation, and switch to "walk" when complete
* this.setCurrentAnimation("eat", "walk");
*
* // set "die" animation, and remove the object when finished
* this.setCurrentAnimation("die", () => {
* world.removeChild(this);
* return false; // do not reset to first frame
* });
*
* // set "attack" animation, and pause for a short duration
* this.setCurrentAnimation("die", () => {
* this.animationpause = true;
*
* // back to "standing" animation after 1 second
* setTimeout(function () {
* this.setCurrentAnimation("standing");
* }, 1000);
*
* return false; // do not reset to first frame
* });
*/
setCurrentAnimation(name, resetAnim, preserve_dt = false) {
if (typeof this.anim[name] !== "undefined") {
if (!this.isCurrentAnimation(name)) {
this.current.name = name;
this.current.length = this.anim[this.current.name].length;
if (typeof resetAnim === "string") {
this.resetAnim = this.setCurrentAnimation.bind(this, resetAnim, null, true);
} else if (typeof resetAnim === "function") {
this.resetAnim = resetAnim;
} else {
this.resetAnim = undefined;
}
this.setAnimationFrame(0);
if (!preserve_dt) {
this.dt = 0;
}
this.isDirty = true;
}
} else {
throw new Error("animation id '" + name + "' not defined");
}
return this;
}
/**
* reverse the given or current animation if none is specified
* @param {string} [name] - animation id
* @returns {Sprite} Reference to this object for method chaining
* @see Sprite#animationspeed
*/
reverseAnimation(name) {
if (typeof name !== "undefined" && typeof this.anim[name] !== "undefined") {
this.anim[name].frames.reverse();
} else {
this.anim[this.current.name].frames.reverse();
}
this.isDirty = true;
return this;
}
/**
* return true if the specified animation is the current one.
* @param {string} name - animation id
* @returns {boolean}
* @example
* if (!this.isCurrentAnimation("walk")) {
* // do something funny...
* }
*/
isCurrentAnimation(name) {
return this.current.name === name;
}
/**
* change the current texture atlas region for this sprite
* @see Texture.getRegion
* @param {object} region - typically returned through me.Texture.getRegion()
* @returns {Sprite} Reference to this object for method chaining
* @example
* // change the sprite to "shadedDark13.png";
* mySprite.setRegion(mytexture.getRegion("shadedDark13.png"));
*/
setRegion(region) {
// set the source texture for the given region
this.image = this.source.getTexture(region);
// set the sprite offset within the texture
this.current.offset.setV(region.offset);
// set angle if defined
this.current.angle = typeof region.angle === "number" ? region.angle : 0;
// update the default "current" size
this.width = this.current.width = region.width;
this.height = this.current.height = region.height;
// set global anchortPoint if defined
if (region.anchorPoint) {
this.anchorPoint.setMuted(
this._flip.x && region.trimmed === true ? 1 - region.anchorPoint.x : region.anchorPoint.x,
this._flip.y && region.trimmed === true ? 1 - region.anchorPoint.y : region.anchorPoint.y
);
}
// update the sprite bounding box
this.updateBounds();
this.isDirty = true;
return this;
}
/**
* force the current animation frame index.
* @param {number} [index=0] - animation frame index
* @returns {Sprite} Reference to this object for method chaining
* @example
* // reset the current animation to the first frame
* this.setAnimationFrame();
*/
setAnimationFrame(index = 0) {
this.current.idx = index % this.current.length;
return this.setRegion(this.getAnimationFrameObjectByIndex(this.current.idx));
}
/**
* return the current animation frame index.
* @returns {number} current animation frame index
*/
getCurrentAnimationFrame() {
return this.current.idx;
}
/**
* Returns the frame object by the index.
* @ignore
* @param {number} id - the frame id
* @returns {number} if using number indices. Returns {object} containing frame data if using texture atlas
*/
getAnimationFrameObjectByIndex(id) {
return this.anim[this.current.name].frames[id];
}
/**
* update function. <br>
* automatically called by the game manager {@link game}
* @protected
* @param {number} dt - time since the last update in milliseconds.
* @returns {boolean} true if the Sprite is dirty
*/
update(dt) {
// play/pause video if necessary
if (this.isVideo) {
if (this.animationpause) {
this.image.pause();
} else if (this.image.paused) {
this.image.play();
}
this.isDirty = !this.image.paused;
} else {
// Update animation if necessary
if (!this.animationpause && this.current.length > 1) {
let duration = this.getAnimationFrameObjectByIndex(this.current.idx).delay;
this.dt += dt;
while (this.dt >= duration) {
this.isDirty = true;
this.dt -= duration;
let nextFrame = (this.current.length > 1 ? this.current.idx + 1 : this.current.idx);
this.setAnimationFrame(nextFrame);
// Switch animation if we reach the end of the strip and a callback is defined
if (this.current.idx === 0) {
if (typeof this.onended === "function") {
this.onended();
}
if (typeof this.resetAnim === "function") {
// Otherwise is must be callable
if (this.resetAnim() === false) {
// Reset to last frame
this.setAnimationFrame(this.current.length - 1);
// Bail early without skipping any more frames.
this.dt %= duration;
break;
}
}
}
// Get next frame duration
duration = this.getAnimationFrameObjectByIndex(this.current.idx).delay;
}
}
}
//update the "flickering" state if necessary
if (this._flicker.isFlickering) {
this._flicker.duration -= dt;
if (this._flicker.duration < 0) {
if (typeof (this._flicker.callback) === "function") {
this._flicker.callback();
}
this.flicker(-1);
}
this.isDirty = true;
}
return super.update(dt);
}
/**
* draw this srite (automatically called by melonJS)
* @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance
* @param {Camera2d} [viewport] - the viewport to (re)draw
*/
draw(renderer, viewport) { // eslint-disable-line no-unused-vars
// do nothing if we are flickering
if (this._flicker.isFlickering) {
this._flicker.state = !this._flicker.state;
if (!this._flicker.state) {
return;
}
}
// the frame to draw
let frame = this.current;
// cache the current position and size
let xpos = this.pos.x,
ypos = this.pos.y;
let w = frame.width,
h = frame.height;
// frame offset in the texture/atlas
let frame_offset = frame.offset;
let g_offset = this.offset;
// remove image's TexturePacker/ShoeBox rotation
if (frame.angle !== 0) {
renderer.translate(-xpos, -ypos);
renderer.rotate(frame.angle);
xpos -= h;
w = frame.height;
h = frame.width;
}
renderer.drawImage(
this.image,
g_offset.x + frame_offset.x, // sx
g_offset.y + frame_offset.y, // sy
w, h, // sw,sh
xpos, ypos, // dx,dy
w, h // dw,dh
);
}
/**
* Destroy function<br>
* @ignore
*/
destroy() {
pool.push(this.offset);
this.offset = undefined;
if (this.isVideo) {
event.off(event.STATE_PAUSE, this._onBlurFn);
this._onBlurFn = undefined;
this.image.onended = undefined;
this.image.pause();
this.image.currentTime = 0;
}
this.image = undefined;
super.destroy();
}
}