Skip to content

Commit

Permalink
Merge pull request #345 from Codas/fix-effect-looping
Browse files Browse the repository at this point in the history
Fix effect loops also looping the `end` portion of the effect
  • Loading branch information
Haxxer authored Oct 25, 2024
2 parents adcf822 + b983687 commit b74c848
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 27 deletions.
25 changes: 13 additions & 12 deletions src/canvas-effects/canvas-effect.js
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ export default class CanvasEffect extends PIXI.Container {
}

async playMedia() {
if (this.destroyed || this._ended || this.isEnding) {
if (this.destroyed || this._ended || this._isEnding) {
return
}
await this.sprite.play()
Expand Down Expand Up @@ -3235,52 +3235,53 @@ export default class CanvasEffect extends PIXI.Container {
this.mediaCurrentTime = this._loopOffset
}
await this.playMedia();
this._addToTicker(this.loopHandler)
this._addToTicker(this.loopHandler);
}

async loopHandler() {
if (this._ended || this.isEnding) {
if (this._ended || this._isEnding) {
return;
}
if (this.mediaCurrentTime < this._endTime) {
if (this.mediaCurrentTime < (this._animationTimes.loopEnd ?? this._endTime)) {
return;
}
if (this.restartLoopHandler != null) {
return;
}

// if we're above end time, we can safely just pause for now
this.pauseMedia()
this.pauseMedia();

// default media current time to exactly end time so we don't
// continue to trigger certain parts of the following code
// unnecessarily
this.mediaCurrentTime = this._endTime
this.mediaCurrentTime = this._endTime;


// if we reached maximum loops, stay paused or even end the effect
if ((this.loops || !this.data.persist) && this._currentLoops >= this.loops) {
if (!this.data.persist || (this.data.persist && this.data.loopOptions?.endOnLastLoop)) {
this.endEffect();
}
this._ticker?.remove(this.loopHandler, this)
this._ticker?.remove(this.loopHandler, this);
return;
}

const restartTime = this._startTime || this._animationTimes.loopStart;
// no loop delay means just start again at the beginning!
if (!this.loopDelay) {
this._currentLoops++;
this.mediaCurrentTime = this._startTime
this.playMedia()
this.mediaCurrentTime = restartTime;
this.playMedia();
return;
}

this._currentLoops++;
// register restart handler to trigger after loop delay
this.restartLoopHandler = setTimeout(() => {
this.restartLoopHandler = null
this.mediaCurrentTime = this._startTime
this.playMedia()
this.restartLoopHandler = null;
this.mediaCurrentTime = restartTime;
this.playMedia();
}, this.loopDelay)
}
}
Expand Down
42 changes: 27 additions & 15 deletions src/canvas-effects/sequencer-sprite-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,12 @@ class SpritePlaybackControls extends PlaybackControls {
set volume(_value) {}

get currentTime() {
return (this.#sprite.currentFrame + 1) / this.#framerate;
// return currentTime rounded with ms accuracy.
// This solves issues with videos vor example having 265 frames, 24 fps.
// This resolves to an accurate total time of 11.04166.. s, whereas
// the reported time from the video file would be 11.042s.
const accurateTime = (this.#sprite.currentFrame + 1) / this.#framerate;
return Math.round(accurateTime * 1000) / 1000;
}
set currentTime(value) {
const newFrame = Math.floor(value * this.#framerate);
Expand Down Expand Up @@ -288,9 +293,11 @@ export class SequencerSpriteManager extends PIXI.Container {

/** @type {{width: number, height: number, scaleX: number, scaleY: number}} */
#defaultScaling = {
width: 0, height: 0, scaleX: 1, scaleY: 1
}

width: 0,
height: 0,
scaleX: 1,
scaleY: 1,
};

get preloadingPromise() {
if (this.#preloadingPromise) {
Expand Down Expand Up @@ -338,8 +345,8 @@ export class SequencerSpriteManager extends PIXI.Container {
scaleX: this.scale.x,
scaleY: this.scale.y,
width: this.width,
height: this.height
}
height: this.height,
};
}

updateVideoTextures() {
Expand Down Expand Up @@ -374,8 +381,10 @@ export class SequencerSpriteManager extends PIXI.Container {
}
if (this.#sharedSpriteConfig.isPersisted) {
requestAnimationFrame(async () => {
const minimumScale = this.#calculateMinimumSpritesheetScale()
const spritesheet = await SequencerFileCache.requestCompiledSpritesheet(filePath, {minimumScale: minimumScale});
const minimumScale = this.#calculateMinimumSpritesheetScale();
const spritesheet = await SequencerFileCache.requestCompiledSpritesheet(filePath, {
minimumScale: minimumScale,
});
if (!spritesheet) {
return;
}
Expand Down Expand Up @@ -600,7 +609,10 @@ export class SequencerSpriteManager extends PIXI.Container {
texture?.baseTexture?.resource.levels === 1
) {
texture?.baseTexture?.setStyle(0, 0);
} else if (this.#sharedSpriteConfig.antialiasing && this.#sharedSpriteConfig.antialiasing !== PIXI.SCALE_MODES.LINEAR) {
} else if (
this.#sharedSpriteConfig.antialiasing &&
this.#sharedSpriteConfig.antialiasing !== PIXI.SCALE_MODES.LINEAR
) {
texture?.baseTexture.setStyle(0, this.#sharedSpriteConfig.antialiasing);
}
if (texture instanceof PIXI.Spritesheet) {
Expand Down Expand Up @@ -711,19 +723,19 @@ export class SequencerSpriteManager extends PIXI.Container {
}
}

/**
* calculates the minimum scale required for generated spritesheets
/**
* calculates the minimum scale required for generated spritesheets
* @returns {number}
*/
#calculateMinimumSpritesheetScale() {
const defaultScale = this.#defaultScaling
// Return the largest of the x, y scaling factors.
const defaultScale = this.#defaultScaling;
// Return the largest of the x, y scaling factors.
// if any of those is 0, fall back to the default 1x scaling.
const maxScale = Math.max(defaultScale.scaleX || 1, defaultScale.scaleY || 1)
const maxScale = Math.max(defaultScale.scaleX || 1, defaultScale.scaleY || 1);

// Never return a value larger than 1 as it makes no sense to upscale the video
// textures for spritesheets
return Math.min(maxScale, 1)
return Math.min(maxScale, 1);
}
}
//#endregion

0 comments on commit b74c848

Please sign in to comment.