diff --git a/example/index.html b/example/index.html index 5484dbf..5fbfb90 100644 --- a/example/index.html +++ b/example/index.html @@ -16,7 +16,7 @@ .grid-item { margin: 0; padding: 0; - width: 50vw; + width: 33.333333vw; height: 50vh; line-height: 0; position: relative; @@ -57,14 +57,21 @@
- - - + + +
+
+
+ + + +
+
diff --git a/example/package-lock.json b/example/package-lock.json index aa4b57d..3094a04 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -8,9 +8,6 @@ "name": "fast-image-sequence examples", "version": "1.0.0", "license": "MIT", - "dependencies": { - "@mediamonks/fast-image-sequence": "^0.1.0" - }, "devDependencies": { "vite": "^5.1.6" } @@ -453,14 +450,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mediamonks/fast-image-sequence": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@mediamonks/fast-image-sequence/-/fast-image-sequence-0.1.3.tgz", - "integrity": "sha512-MhsiOC6Jx0L3bgGlPYyx5XL8TD+YwW9oW+avR3Q9cZ+q1VxDGPBYSH0e1Ms3Sb6X8G1zLnfRQ/pYt7qjnTkI7g==", - "engines": { - "node": ">=16.0.0" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", diff --git a/example/src/exampleLoadTar.js b/example/src/exampleLoadTar.js new file mode 100644 index 0000000..9166200 --- /dev/null +++ b/example/src/exampleLoadTar.js @@ -0,0 +1,65 @@ +import {FastImageSequence} from '../../src/index'; + +const prevButton = document.getElementById('prev-button-2'); +const nextButton = document.getElementById('next-button-2'); +const progress = document.getElementById('slider-input-2'); + +function blobToDataURL(blob) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = _e => resolve(reader.result); + reader.onerror = _e => reject(reader.error); + reader.onabort = _e => reject(new Error("Read aborted")); + reader.readAsDataURL(blob); + }); +} + +export async function initExampleLoadTar(container) { + // load tar file with lowres previews + fetch('lowrespreviews.tar').then(async (response) => { + const blob = await response.blob(); + const dataURL = await blobToDataURL(blob); + + const fastImageSequence = new FastImageSequence(container, { + name: 'PlayWithControlTest', + frames: 89, + src: [ + { + tarURL: dataURL, + imageURL: (i) => `${('' + (i + 1)).padStart(4, '0')}.jpg`, + } + ], + // optional arguments: + loop: true, // default false + objectFit: 'contain', // default 'cover' + fillStyle: '#00000000', // default #00000000 + clearCanvas: false, // default false + showDebugInfo: true, + }); + + await fastImageSequence.ready(); + + fastImageSequence.tick((dt) => { + if (fastImageSequence.playing) { + 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.paused) { + fastImageSequence.progress = progress.value; + } + }); + + fastImageSequence.play(30); + }); +} diff --git a/example/src/exampleWithControl.js b/example/src/exampleWithControl.js index cf9afde..e104897 100644 --- a/example/src/exampleWithControl.js +++ b/example/src/exampleWithControl.js @@ -1,8 +1,8 @@ import {FastImageSequence} from '../../src/index'; -const prevButton = document.getElementById('prev-button'); -const nextButton = document.getElementById('next-button'); -const progress = document.getElementById('slider-input'); +const prevButton = document.getElementById('prev-button-1'); +const nextButton = document.getElementById('next-button-1'); +const progress = document.getElementById('slider-input-1'); export async function initExampleWithControl(container) { const fastImageSequence = new FastImageSequence(container, { diff --git a/example/src/index.js b/example/src/index.js index 3a5314a..2a828ac 100644 --- a/example/src/index.js +++ b/example/src/index.js @@ -2,8 +2,10 @@ import {initExampleWithControl} from "./exampleWithControl.js"; import {initExamplePlayBackwards} from "./examplePlayBackwards.js"; import {initExampleStillImage} from "./exampleStillImage.js"; import {constructDestructTest} from "./exampleConstructDestructTest.js"; +import {initExampleLoadTar} from "./exampleLoadTar.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]); \ No newline at end of file +constructDestructTest(document.getElementsByClassName('grid-item')[3]); +initExampleLoadTar(document.getElementsByClassName('grid-item')[4]); \ No newline at end of file diff --git a/package.json b/package.json index 3298afe..c9439d9 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,5 @@ }, "engines": { "node": ">=16.0.0" - }, - "dependencies": { - "@mediamonks/fast-image-sequence": "^0.6.4" } } diff --git a/src/lib/FastImageSequence.ts b/src/lib/FastImageSequence.ts index 0314e0c..659210b 100644 --- a/src/lib/FastImageSequence.ts +++ b/src/lib/FastImageSequence.ts @@ -2,6 +2,7 @@ import Frame from "./Frame.js"; import {createLogElement, logToScreen} from "./LogToScreen.js"; import ImageSource, {type ImageSourceOptions, INPUT_SRC} from "./ImageSource.js"; import ImageSourceTar from "./ImageSourceTar.js"; +import ImageSourceFetch from "./ImageSourceFetch.js"; export function isMobile(): boolean { return (typeof navigator !== "undefined" && /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)); @@ -147,7 +148,7 @@ export class FastImageSequence { if (src.tarURL !== undefined) { return new ImageSourceTar(this, index, src); } else { - return new ImageSource(this, index, src); + return new ImageSourceFetch(this, index, src); } }); @@ -204,6 +205,18 @@ export class FastImageSequence { this.frame = (this.options.frames - 1) * value; } + /** + * Get the first ImageSource from the sources array. + * @returns {ImageSource} - The first ImageSource object in the sources array. + */ + public get src() { + return this.sources[0] as ImageSource; + } + + private get index(): number { + return this.wrapIndex(this.frame); + } + /** * Returns a promise that resolves when the image sequence is ready to play. */ @@ -221,18 +234,6 @@ export class FastImageSequence { }); } - /** - * Get the first ImageSource from the sources array. - * @returns {ImageSource} - The first ImageSource object in the sources array. - */ - public get src() { - return this.sources[0] as ImageSource; - } - - private get index(): number { - return this.wrapIndex(this.frame); - } - /** * Register a tick function to be called on every frame update. * @@ -331,7 +332,7 @@ export class FastImageSequence { this.clearCanvas = true; } - public setLoadingPriority() { + private setLoadingPriority() { const priorityIndex = this.index;// this.wrapIndex(Math.min(this.spread / 2 - 2, (this.frame - this.prevFrame) * (dt * 60)) + this.frame); this.frames.forEach((image) => { image.priority = Math.abs(image.index + 0.25 - priorityIndex); diff --git a/src/lib/ImageSource.ts b/src/lib/ImageSource.ts index 541a23e..f65cfc5 100644 --- a/src/lib/ImageSource.ts +++ b/src/lib/ImageSource.ts @@ -1,7 +1,5 @@ import {clamp, type FastImageSequence, isMobile} from "./FastImageSequence.js"; import ImageElement from "./ImageElement.js"; -import {getImageFetchWorker, releaseImageFetchWorker} from "./ImageFetch.js"; -import {loadImage} from "./DownloadFile.js"; export const INPUT_SRC = 0; export const INPUT_TAR = 1; @@ -70,7 +68,7 @@ export default class ImageSource { } public getImageURL(index: number): string | undefined { - return this.options.imageURL ? new URL(this.options.imageURL(index), window.location.href).href : undefined; + return undefined; } public async loadResources() { @@ -78,6 +76,7 @@ export default class ImageSource { throw new Error(`No image available for index 0 in ImageSource${this.index} (${this.images[0]?.imageURL})`); } + this.setMaxCachedImages(this.options.maxCachedImages); this.initialized = true; } @@ -111,39 +110,14 @@ export default class ImageSource { public async fetchImage(imageElement: ImageElement) { return new Promise((resolve, reject) => { - if (imageElement.imageURL) { - imageElement.loading = true; - - const loadingDone = (image: ImageBitmap | HTMLImageElement) => { - if (imageElement.loading) { - imageElement.image = image; - resolve(image); - } - }; - - const loadingError = (e: any) => { - imageElement.reset(); - reject(e); - }; - - if (this.options.useWorker) { - const worker = getImageFetchWorker(); - worker.load(this.index, imageElement.imageURL).then((imageBitmap) => { - loadingDone(imageBitmap); - releaseImageFetchWorker(worker); - }).catch(e => loadingError(e)); - } else { - const imgElement = new Image(); - loadImage(imgElement, imageElement.imageURL).then(() => { - loadingDone(imgElement); - }).catch(e => loadingError(e)); - } - } else { - reject('Image url not set'); - } + reject('Not implemented'); }); } + public destruct() { + this.images.forEach(image => image.reset()); + } + private releaseImageWithLowestPriority() { this.context.setLoadingPriority(); const loadedImages = this.images.filter(a => a.image !== undefined && !a.loading); @@ -155,8 +129,4 @@ export default class ImageSource { } } } - - public destruct() { - this.images.forEach(image => image.reset()); - } } \ No newline at end of file diff --git a/src/lib/ImageSourceFetch.ts b/src/lib/ImageSourceFetch.ts new file mode 100644 index 0000000..03cd925 --- /dev/null +++ b/src/lib/ImageSourceFetch.ts @@ -0,0 +1,45 @@ +import ImageElement from "./ImageElement.js"; +import {getImageFetchWorker, releaseImageFetchWorker} from "./ImageFetch.js"; +import {loadImage} from "./DownloadFile.js"; +import ImageSource from "./ImageSource.js"; + +export default class ImageSourceFetch extends ImageSource { + public override getImageURL(index: number): string | undefined { + return this.options.imageURL ? new URL(this.options.imageURL(index), window.location.href).href : undefined; + } + + public override async fetchImage(imageElement: ImageElement) { + return new Promise((resolve, reject) => { + if (imageElement.imageURL) { + imageElement.loading = true; + + const loadingDone = (image: ImageBitmap | HTMLImageElement) => { + if (imageElement.loading) { + imageElement.image = image; + resolve(image); + } + }; + + const loadingError = (e: any) => { + imageElement.reset(); + reject(e); + }; + + if (this.options.useWorker) { + const worker = getImageFetchWorker(); + worker.load(this.index, imageElement.imageURL).then((imageBitmap) => { + loadingDone(imageBitmap); + releaseImageFetchWorker(worker); + }).catch(e => loadingError(e)); + } else { + const imgElement = new Image(); + loadImage(imgElement, imageElement.imageURL).then(() => { + loadingDone(imgElement); + }).catch(e => loadingError(e)); + } + } else { + reject('Image url not set'); + } + }); + } +} \ No newline at end of file diff --git a/src/lib/ImageSourceTar.ts b/src/lib/ImageSourceTar.ts index c927846..3941050 100644 --- a/src/lib/ImageSourceTar.ts +++ b/src/lib/ImageSourceTar.ts @@ -17,7 +17,6 @@ export default class ImageSourceTar extends ImageSource { this.context.log('Tarball', this.tarball); this.images.forEach(image => image.available = image.imageURL !== undefined && this.tarball?.getInfo(image.imageURL) !== undefined); - this.setMaxCachedImages(this.options.maxCachedImages); } return super.loadResources();