Skip to content

Commit

Permalink
Fix typing, improve examples/testing, fix destruct methods
Browse files Browse the repository at this point in the history
  • Loading branch information
reindernijhoff committed Apr 12, 2024
1 parent 0fa1fc2 commit ab3dcf3
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 127 deletions.
55 changes: 20 additions & 35 deletions example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,25 @@
margin: 0;
padding: 0;
height: 100vh;
width: 100vw;
display: flex;
flex-wrap: wrap;
overflow: hidden;
}

.container {
.grid-item {
margin: 0;
padding: 0;
height: 100vh;
width: 100vw;
width: 50vw;
height: 50vh;
line-height: 0;
position: relative;
}

.control {
position: absolute;
bottom: 3vh;
left: 10vw;
width: 80vw;
bottom: 3%;
left: 10%;
width: 80%;
display: flex;
justify-content: space-between;
align-items: center;
Expand All @@ -48,39 +47,25 @@
cursor: pointer;
}

.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 16px solid #f3f3f3;
border-top: 16px solid #3498db;
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}

@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
canvas {
padding: 0;
margin: 0;
}
</style>
</head>

<body>
<div class="container">
<div class="loading"></div>
<div class="grid-item">
<div class="control">
<button class="control-button" id="prev-button">&lt;</button>
<input type="range" min="0" max="1" value="0" step="0.0001" class="control-slider" id="slider-input">
<button class="control-button" id="next-button">&gt;</button>
</div>
</div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>

<div class="control">
<button class="control-button" id="prev-button">&lt;</button>
<input type="range" min="0" max="1" value="0" step="0.0001" class="control-slider" id="slider-input">
<button class="control-button" id="next-button">&gt;</button>
</div>
<script type="module" src="./src/index.js" ></script>
<script type="module" src="./src/index.js"></script>
</body>
</html>
37 changes: 37 additions & 0 deletions example/src/exampleConstructDestructTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {FastImageSequence} from '../../src/index';

export function constructDestructTest(container) {
let fastImageSequence = createFastImageSequence(container);

// construct a FastImageSequence every 3 seconds, and destruct it after 5 seconds
setInterval(() => {
console.log('destructing');
fastImageSequence.destruct();
console.log('constructing');
fastImageSequence = createFastImageSequence(container);
}, 3000);
}

function createFastImageSequence(container) {
const fastImageSequence = new FastImageSequence(container, {
frames: 89,
imageURLCallback: (i) => `${('' + (i + 1)).padStart(4, '0')}.webp`,
tarURL: '/lowrespreviews.tar',
tarImageURLCallback: (i) => `${('' + (i + 1)).padStart(4, '0')}.jpg`,

// optional arguments:
wrap: Math.random() > .5, // default false
size: Math.random() > .5 ? 'cover' : 'contain', // default 'cover'
fillStyle: '#00000000', // default #00000000
preloadAllTarImages: Math.random() > .5,
useWorkerForTar: Math.random() > .5, // default true
numberOfCachedImages: 32, // default 32
clearCanvas: Math.random() > .5, // default false
});

fastImageSequence.ready.then(() => {
fastImageSequence.play(30);
});

return fastImageSequence;
}
25 changes: 25 additions & 0 deletions example/src/examplePlayBackwards.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { FastImageSequence } from '../../src/index';

export async function initExamplePlayBackwards(container) {
const fastImageSequence = new FastImageSequence(container, {
frames: 89,
imageURLCallback: (i) => `${('' + (i + 1)).padStart(4, '0')}.webp`,
// tarURL: '/lowrespreviews.tar',
// tarImageURLCallback: (i) => `${('' + (i+1)).padStart(4, '0')}.jpg`,

// optional arguments:
wrap: true, // default false
size: 'cover', // default 'cover'
fillStyle: '#00000000', // default #00000000
preloadAllTarImages: false,
useWorkerForTar: true, // default true
numberOfCachedImages: 32, // default 32
clearCanvas: false, // default false
});

await fastImageSequence.ready;

fastImageSequence.progress = 0;

fastImageSequence.play(-30);
}
23 changes: 23 additions & 0 deletions example/src/exampleStillImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { FastImageSequence } from '../../src/index';

export async function initExampleStillImage(container) {
const fastImageSequence = new FastImageSequence(container, {
frames: 89,
imageURLCallback: (i) => `${('' + (i + 1)).padStart(4, '0')}.webp`,
// tarURL: '/lowrespreviews.tar',
// tarImageURLCallback: (i) => `${('' + (i+1)).padStart(4, '0')}.jpg`,

// optional arguments:
wrap: true, // default false
size: 'cover', // default 'cover'
fillStyle: '#00000000', // default #00000000
preloadAllTarImages: false,
useWorkerForTar: true, // default true
numberOfCachedImages: 32, // default 32
clearCanvas: false, // default false
});

await fastImageSequence.ready;

console.log('fastImageSequence loaded');
}
50 changes: 50 additions & 0 deletions example/src/exampleWithControl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { FastImageSequence } from '../../src/index';

const prevButton = document.getElementById('prev-button');
const nextButton = document.getElementById('next-button');
const progress = document.getElementById('slider-input');

export async function initExampleWithControl(container) {
const fastImageSequence = new FastImageSequence(container, {
frames: 89,
imageURLCallback: (i) => `${('' + (i+1)).padStart(4, '0')}.webp`,
tarURL: '/lowrespreviews.tar',
tarImageURLCallback: (i) => `${('' + (i+1)).padStart(4, '0')}.jpg`,

// optional arguments:
wrap: true, // default false
size: 'contain', // default 'cover'
fillStyle: '#00000000', // default #00000000
preloadAllTarImages: false,
useWorkerForTar: true, // default true
numberOfCachedImages: 32, // default 32
clearCanvas: false, // default false
});

await fastImageSequence.ready;

fastImageSequence.progress = 0;

fastImageSequence.tick((dt) => {
if (fastImageSequence.isPlaying) {
progress.value = fastImageSequence.progress;
}
});

prevButton.addEventListener('click', () => {
fastImageSequence.play(-30);
});
nextButton.addEventListener('click', () => {
fastImageSequence.play(30);
});
progress.addEventListener('mousedown', (e) => {
fastImageSequence.stop();
});
progress.addEventListener('input', () => {
if (fastImageSequence.isPaused) {
fastImageSequence.progress = progress.value;
}
});

fastImageSequence.play(30);
}
67 changes: 9 additions & 58 deletions example/src/index.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,9 @@
// import {FastImageSequence} from '@mediamonks/fast-image-sequence';
import {FastImageSequence} from '../../src/index';

const container = document.getElementsByClassName('container')[0];
const prevButton = document.getElementById('prev-button');
const nextButton = document.getElementById('next-button');
const progress = document.getElementById('slider-input');

async function init() {
const fastImageSequence = new FastImageSequence(container, {
frames: 89,
imageURLCallback: (i) => `./public/${('' + (i+1)).padStart(4, '0')}.webp`,
tarURL: '/lowrespreviews.tar',
tarImageURLCallback: (i) => `${('' + (i+1)).padStart(4, '0')}.jpg`,

// optional arguments:
wrap: true, // default false
size: 'contain', // default 'cover'
fillStyle: '#00000000', // default #00000000
preloadAllTarImages: false,
useWorkerForTar: true, // default true
numberOfCachedImages: 32, // default 32
clearCanvas: false, // default false
});

await fastImageSequence.ready;

document.getElementsByClassName('loading')[0].remove();
fastImageSequence.progress = 0;

fastImageSequence.tick((dt) => {
if (fastImageSequence.isPlaying) {
progress.value = fastImageSequence.progress;
}
});

prevButton.addEventListener('click', () => {
fastImageSequence.play(-30);
});
nextButton.addEventListener('click', () => {
fastImageSequence.play(30);
});
progress.addEventListener('mousedown', (e) => {
fastImageSequence.stop();
});
progress.addEventListener('input', () => {
if (fastImageSequence.isPaused) {
fastImageSequence.progress = progress.value;
}
});

fastImageSequence.play(30);

console.log(fastImageSequence);
}


init();
import {initExampleWithControl} from "./exampleWithControl.js";
import {initExamplePlayBackwards} from "./examplePlayBackwards.js";
import {initExampleStillImage} from "./exampleStillImage.js";
import {constructDestructTest} from "./exampleConstructDestructTest.js";

initExampleWithControl(document.getElementsByClassName('grid-item')[0]);
initExamplePlayBackwards(document.getElementsByClassName('grid-item')[1]);
initExampleStillImage(document.getElementsByClassName('grid-item')[2]);
constructDestructTest(document.getElementsByClassName('grid-item')[3]);
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export {default as FastImageSequence} from './lib/FastImageSequence.js';
export * from './lib/FastImageSequence.js';

29 changes: 22 additions & 7 deletions src/lib/FastImageSequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ export function isMobile(): boolean {
*/
export type FastImageSequenceOptions = {
frames: number,
} & Partial<{
tarURL: string | undefined,
imageURLCallback: ((index: number) => string) | undefined,
tarImageURLCallback: ((index: number) => string) | undefined,
} & Partial<{
wrap: boolean;
fillStyle: string;
size: 'contain' | 'cover';
Expand All @@ -37,7 +37,7 @@ export type FastImageSequenceOptions = {
clearCanvas: boolean,
}>

export default class FastImageSequence {
export class FastImageSequence {
private static defaultOptions: Required<FastImageSequenceOptions> = {
frames: 1,
imageURLCallback: undefined,
Expand Down Expand Up @@ -68,6 +68,7 @@ export default class FastImageSequence {
private animationRequestId: number = 0;
private container: HTMLElement;
private resizeObserver: ResizeObserver;
private mutationOberver: MutationObserver;
private clearCanvas: boolean = true;

private maxLoading: number = 6;
Expand All @@ -78,6 +79,7 @@ export default class FastImageSequence {
private prevFrame: number = 0;
private direction: number = 1;
private lastFrameDrawn: number = -1;
private destructed: boolean = false;

/**
* Creates an instance of FastImageSequence.
Expand Down Expand Up @@ -111,15 +113,15 @@ export default class FastImageSequence {
this.resizeObserver = new ResizeObserver(() => {
this.clearCanvas = true;
});
this.resizeObserver.observe(container);
this.resizeObserver.observe(this.canvas);

const mutationOberver = new MutationObserver(() => {
this.mutationOberver = new MutationObserver(() => {
if (!this.container.isConnected) {
console.error('FastImageSequence: container is not connected to the DOM, fast image sequence will be destroyed');
this.destruct();
}
});
mutationOberver.observe(container, {childList: true});
this.mutationOberver.observe(container, {childList: true});

// init all frames
for (let i = 0; i < this.options.frames; i++) {
Expand Down Expand Up @@ -227,9 +229,18 @@ export default class FastImageSequence {
* Destruct the FastImageSequence instance.
*/
public destruct() {
cancelAnimationFrame(this.animationRequestId);
if (this.destructed) {
return;
}
this.destructed = true;

if (this.animationRequestId) {
cancelAnimationFrame(this.animationRequestId);
}

this.resizeObserver.disconnect();
this.mutationOberver.disconnect();

this.container.removeChild(this.canvas);
this.canvas.replaceWith(this.canvas.cloneNode(true));
this.frames.forEach(frame => {
Expand All @@ -252,6 +263,10 @@ export default class FastImageSequence {
}

private async drawingLoop(time: number = 0) {
if (this.destructed) {
return;
}

time /= 1000;

const dt = this.startTime < 0 ? 1 / 60 : Math.min(time - this.startTime, 1 / 30);
Expand Down Expand Up @@ -301,7 +316,7 @@ export default class FastImageSequence {

this.tickFuncs.forEach(func => func(dt));

this.animationRequestId = window.requestAnimationFrame(time => this.drawingLoop(time));
this.animationRequestId = requestAnimationFrame(time => this.drawingLoop(time));
}

private drawFrame(image: HTMLImageElement | ImageBitmap, index: number) {
Expand Down
Loading

0 comments on commit ab3dcf3

Please sign in to comment.