From dd0a483fb8fabdb72b6e17ef1737ddf3fc5d7c72 Mon Sep 17 00:00:00 2001 From: Emilien Date: Thu, 16 Apr 2020 20:13:11 +0200 Subject: [PATCH 01/10] Create GIF component --- packages/2d/src/Components/GIF.ts | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 packages/2d/src/Components/GIF.ts diff --git a/packages/2d/src/Components/GIF.ts b/packages/2d/src/Components/GIF.ts new file mode 100644 index 00000000..3e0fd71b --- /dev/null +++ b/packages/2d/src/Components/GIF.ts @@ -0,0 +1,33 @@ +import { AnimationAPI, AnimationFrame } from './Animation'; +import { useType } from '@hex-engine/core'; + +export default function GIF(): AnimationAPI { + useType(GIF); + + return { + frames: [], + loop: false, + currentFrameIndex: 0, + currentFrame: new AnimationFrame(null, { + duration: 0, + }), + currentFrameCompletion: 0, + pause() { + return void 0; + }, + resume() { + return void 0; + }, + play() { + return void 0; + }, + restart() { + return void 0; + }, + + + goToFrame(frameNumber: number) { + return void 0; + }, + } +} \ No newline at end of file From 598aad46e4fd4ac72e39ef463fc39c071e69bd74 Mon Sep 17 00:00:00 2001 From: Emilien Date: Sat, 18 Apr 2020 22:19:54 +0200 Subject: [PATCH 02/10] Read gif using gifken --- packages/2d/package.json | 1 + packages/2d/src/Components/GIF.ts | 41 ++++++++++++++++++++++++----- packages/2d/src/Components/index.ts | 2 ++ yarn.lock | 12 +++++++++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/packages/2d/package.json b/packages/2d/package.json index 01f483f0..03d85376 100644 --- a/packages/2d/package.json +++ b/packages/2d/package.json @@ -9,6 +9,7 @@ "@hex-engine/core": "0.5.2", "@hex-engine/inspector": "0.5.2", "@types/matter-js": "^0.10.7", + "gifken": "^2.1.1", "layout-bmfont-text": "^1.3.4", "matter-js": "^0.14.2", "mem": "^6.0.1", diff --git a/packages/2d/src/Components/GIF.ts b/packages/2d/src/Components/GIF.ts index 3e0fd71b..8d38f0ac 100644 --- a/packages/2d/src/Components/GIF.ts +++ b/packages/2d/src/Components/GIF.ts @@ -1,14 +1,45 @@ import { AnimationAPI, AnimationFrame } from './Animation'; import { useType } from '@hex-engine/core'; +import gifken from 'gifken'; -export default function GIF(): AnimationAPI { +interface GIFInterface extends AnimationAPI<{}> { + drawCurrentFrame(context: CanvasRenderingContext2D): void; +} + +export default function GIF(options: { url: string }): GIFInterface { useType(GIF); + let frames: any[] = []; + let i = 0; + + var xhr = new XMLHttpRequest(); + xhr.open("GET", options.url, true); + xhr.responseType = "arraybuffer"; + xhr.onload = (e: any) => { + var arrayBuffer = e.target["response"]; + var gif = gifken.Gif.parse(arrayBuffer); + + frames = gif.split(false).map(frame => { + const url = URL.createObjectURL(new Blob(frame.writeToArrayBuffer())).toString(); + const img = document.createElement('img'); + img.src = url; + return img + }) + }; + xhr.send(); return { + drawCurrentFrame(context: CanvasRenderingContext2D) { + if(frames.length !== 0) { + context.drawImage(frames[i], 0, 0); + if(frames.length - 1 > i) { + i++; + } + } + }, frames: [], loop: false, currentFrameIndex: 0, - currentFrame: new AnimationFrame(null, { + currentFrame: new AnimationFrame({}, { duration: 0, }), currentFrameCompletion: 0, @@ -23,11 +54,9 @@ export default function GIF(): AnimationAPI { }, restart() { return void 0; - }, - - + }, goToFrame(frameNumber: number) { - return void 0; + return frameNumber; }, } } \ No newline at end of file diff --git a/packages/2d/src/Components/index.ts b/packages/2d/src/Components/index.ts index cb0c919d..e949a828 100644 --- a/packages/2d/src/Components/index.ts +++ b/packages/2d/src/Components/index.ts @@ -11,6 +11,7 @@ import Font from "./Font"; import FontMetrics from "./FontMetrics"; import Gamepad from "./Gamepad"; import Geometry from "./Geometry"; +import GIF from "./GIF"; import Image from "./Image"; import ImageFilter from "./ImageFilter"; import Keyboard from "./Keyboard"; @@ -42,6 +43,7 @@ export { FontMetrics, Gamepad, Geometry, + GIF, Image, ImageFilter, Keyboard, diff --git a/yarn.lock b/yarn.lock index e592e032..01c87177 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4180,6 +4180,11 @@ brorand@^1.0.1: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= +browser-or-node@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/browser-or-node/-/browser-or-node-1.2.1.tgz#cd65172da6a7fd689c7a650d326bd2ad145419a7" + integrity sha512-sVIA0cysIED0nbmNOm7sZzKfgN1rpFmrqvLZaFWspaBAftfQcezlC81G6j6U2RJf4Lh66zFxrCeOsvkUXIcPWg== + browser-resolve@^1.11.3: version "1.11.3" resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" @@ -6713,6 +6718,13 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +gifken@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/gifken/-/gifken-2.1.1.tgz#24ce88dbcb43f557bb17ae1e2d91d5633b71041b" + integrity sha512-q8uBypvYwFfoJhNm+4xjKkj5AZiF8PtXW2VsddS3YpCdXtLa6nJRVQ6Aj0R51iWv4dl1Y3qqe1t+wz9viNhfVQ== + dependencies: + browser-or-node "^1.2.1" + github-slugger@^1.0.0, github-slugger@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.2.1.tgz#47e904e70bf2dccd0014748142d31126cfd49508" From 89b5172c4837f1d1de657f2faaac58ea790c7918 Mon Sep 17 00:00:00 2001 From: Emilien Date: Sun, 26 Apr 2020 16:54:49 +0200 Subject: [PATCH 03/10] read a gif and add some options --- packages/2d/src/Components/GIF.ts | 99 ++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 34 deletions(-) diff --git a/packages/2d/src/Components/GIF.ts b/packages/2d/src/Components/GIF.ts index 8d38f0ac..3068c04e 100644 --- a/packages/2d/src/Components/GIF.ts +++ b/packages/2d/src/Components/GIF.ts @@ -1,62 +1,93 @@ import { AnimationAPI, AnimationFrame } from './Animation'; import { useType } from '@hex-engine/core'; -import gifken from 'gifken'; +import gifken, { Gif } from 'gifken'; -interface GIFInterface extends AnimationAPI<{}> { +interface GIFInterface extends AnimationAPI { + getGif(): Gif, drawCurrentFrame(context: CanvasRenderingContext2D): void; } -export default function GIF(options: { url: string }): GIFInterface { +export default function GIF(options: { url: string, width: number, height: number, fps?: number, compressed?: boolean, loop?: boolean }): GIFInterface { useType(GIF); - let frames: any[] = []; + let gif: Gif = new Gif(); + let frames: AnimationFrame[] = []; + let play: boolean = false; let i = 0; - var xhr = new XMLHttpRequest(); - xhr.open("GET", options.url, true); - xhr.responseType = "arraybuffer"; - xhr.onload = (e: any) => { - var arrayBuffer = e.target["response"]; - var gif = gifken.Gif.parse(arrayBuffer); - - frames = gif.split(false).map(frame => { - const url = URL.createObjectURL(new Blob(frame.writeToArrayBuffer())).toString(); - const img = document.createElement('img'); - img.src = url; - return img + load(options.url).then(arrayBuffer => { + gif = gifken.Gif.parse(arrayBuffer); + frames = getFrames({ + gif, + width: options.width, + height: options.height, + compressed: options.compressed }) - }; - xhr.send(); + setInterval(() => { + if (frames.length - 1 > i && play) { + i++; + } + + if(frames.length - 1 <= i && options.loop && play) { + i = 0; + } + }, 30) + }) + + return { + getGif() { + return gif; + }, drawCurrentFrame(context: CanvasRenderingContext2D) { - if(frames.length !== 0) { - context.drawImage(frames[i], 0, 0); - if(frames.length - 1 > i) { - i++; - } + if(frames.length !== 0 && play) { + context.drawImage(frames[i].data, 0, 0); } }, - frames: [], - loop: false, - currentFrameIndex: 0, - currentFrame: new AnimationFrame({}, { - duration: 0, - }), + frames: frames, + loop: options.loop || false, + currentFrameIndex: i, + currentFrame: frames[i], currentFrameCompletion: 0, pause() { - return void 0; + play = false; }, resume() { - return void 0; + play = true; }, play() { - return void 0; + play = true; }, restart() { - return void 0; + i = 0; + play = true; }, goToFrame(frameNumber: number) { - return frameNumber; + i = frameNumber; }, } +} + +async function load(url: string) { + return await fetch(url) + .then((res) => res.arrayBuffer()); +} + +function getFrames({ gif, width, height, compressed = false }: { + gif: Gif, + width: number, + height: number, + compressed?: boolean, +}){ + return gif.split(compressed).map(frame => { + const blob = gifken.GifPresenter.writeToBlob(frame.writeToArrayBuffer()) + const url = URL.createObjectURL(blob).toString(); + const img = document.createElement('img'); + img.width = width; + img.height = height; + img.src = url; + return new AnimationFrame(img, { + duration: 0, + }); + }); } \ No newline at end of file From b943af76f59e9d23ba5c905cbc37a2197678a062 Mon Sep 17 00:00:00 2001 From: Emilien Date: Sun, 26 Apr 2020 17:07:40 +0200 Subject: [PATCH 04/10] option to update the fps of the gif --- packages/2d/src/Components/GIF.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/2d/src/Components/GIF.ts b/packages/2d/src/Components/GIF.ts index 3068c04e..1a7c2adc 100644 --- a/packages/2d/src/Components/GIF.ts +++ b/packages/2d/src/Components/GIF.ts @@ -4,10 +4,17 @@ import gifken, { Gif } from 'gifken'; interface GIFInterface extends AnimationAPI { getGif(): Gif, - drawCurrentFrame(context: CanvasRenderingContext2D): void; + drawCurrentFrame(context: CanvasRenderingContext2D, x?: number, y?: number): void; } -export default function GIF(options: { url: string, width: number, height: number, fps?: number, compressed?: boolean, loop?: boolean }): GIFInterface { +export default function GIF(options: { + url: string, + width: number, + height: number, + fps?: number, + compressed?: boolean, + loop?: boolean +}): GIFInterface { useType(GIF); let gif: Gif = new Gif(); let frames: AnimationFrame[] = []; @@ -31,7 +38,7 @@ export default function GIF(options: { url: string, width: number, height: numbe if(frames.length - 1 <= i && options.loop && play) { i = 0; } - }, 30) + }, 1000 / (options.fps || 25)) }) @@ -39,9 +46,9 @@ export default function GIF(options: { url: string, width: number, height: numbe getGif() { return gif; }, - drawCurrentFrame(context: CanvasRenderingContext2D) { + drawCurrentFrame(context: CanvasRenderingContext2D, x: number = 0, y: number = 0) { if(frames.length !== 0 && play) { - context.drawImage(frames[i].data, 0, 0); + context.drawImage(frames[i].data, x, y); } }, frames: frames, From 175eb645db7cd974fe8b4764e982b3254d89e41c Mon Sep 17 00:00:00 2001 From: Emilien Date: Mon, 1 Jun 2020 15:57:57 +0200 Subject: [PATCH 05/10] Fixes trouble with compressed gifs --- packages/2d/src/Components/GIF.ts | 178 +++++++++++++++++------------- 1 file changed, 99 insertions(+), 79 deletions(-) diff --git a/packages/2d/src/Components/GIF.ts b/packages/2d/src/Components/GIF.ts index 1a7c2adc..b3efbb99 100644 --- a/packages/2d/src/Components/GIF.ts +++ b/packages/2d/src/Components/GIF.ts @@ -3,98 +3,118 @@ import { useType } from '@hex-engine/core'; import gifken, { Gif } from 'gifken'; interface GIFInterface extends AnimationAPI { - getGif(): Gif, - drawCurrentFrame(context: CanvasRenderingContext2D, x?: number, y?: number): void; + getGif(): Gif, + drawCurrentFrame(context: CanvasRenderingContext2D, x?: number, y?: number): void; } export default function GIF(options: { - url: string, - width: number, - height: number, - fps?: number, - compressed?: boolean, - loop?: boolean + url: string, + width: number, + height: number, + fps?: number, + loop?: boolean }): GIFInterface { - useType(GIF); - let gif: Gif = new Gif(); - let frames: AnimationFrame[] = []; - let play: boolean = false; - let i = 0; + useType(GIF); + let gif: Gif = new Gif(); + let frames: AnimationFrame[] = []; + let play: boolean = false; + let i = 0; - load(options.url).then(arrayBuffer => { - gif = gifken.Gif.parse(arrayBuffer); - frames = getFrames({ - gif, - width: options.width, - height: options.height, - compressed: options.compressed - }) + load(options.url).then(async arrayBuffer => { + gif = gifken.Gif.parse(arrayBuffer); + frames = await getFrames({ + gif, + width: options.width, + height: options.height, + }) - setInterval(() => { - if (frames.length - 1 > i && play) { - i++; - } + setInterval(() => { + if (frames.length - 1 > i && play) { + i++; + } - if(frames.length - 1 <= i && options.loop && play) { - i = 0; - } - }, 1000 / (options.fps || 25)) - }) + if(frames.length - 1 <= i && options.loop && play) { + i = 0; + } + }, 1000 / (options.fps || 25)) + }) - return { - getGif() { - return gif; - }, - drawCurrentFrame(context: CanvasRenderingContext2D, x: number = 0, y: number = 0) { - if(frames.length !== 0 && play) { - context.drawImage(frames[i].data, x, y); - } - }, - frames: frames, - loop: options.loop || false, - currentFrameIndex: i, - currentFrame: frames[i], - currentFrameCompletion: 0, - pause() { - play = false; - }, - resume() { - play = true; - }, - play() { - play = true; - }, - restart() { - i = 0; - play = true; - }, - goToFrame(frameNumber: number) { - i = frameNumber; - }, - } + return { + getGif() { + return gif; + }, + drawCurrentFrame(context: CanvasRenderingContext2D, x: number = 0, y: number = 0) { + if(frames.length !== 0 && play) { + context.drawImage(frames[i].data, x, y); + } + }, + frames: frames, + loop: options.loop || false, + currentFrameIndex: i, + currentFrame: frames[i], + currentFrameCompletion: 0, + pause() { + play = false; + }, + resume() { + play = true; + }, + play() { + play = true; + }, + restart() { + i = 0; + play = true; + }, + goToFrame(frameNumber: number) { + i = frameNumber; + }, + } } +/** + * Load a gif. + * @param url - gif url + */ async function load(url: string) { - return await fetch(url) - .then((res) => res.arrayBuffer()); + return await fetch(url) + .then((res) => res.arrayBuffer()); } -function getFrames({ gif, width, height, compressed = false }: { - gif: Gif, - width: number, - height: number, - compressed?: boolean, -}){ - return gif.split(compressed).map(frame => { - const blob = gifken.GifPresenter.writeToBlob(frame.writeToArrayBuffer()) - const url = URL.createObjectURL(blob).toString(); - const img = document.createElement('img'); - img.width = width; - img.height = height; - img.src = url; - return new AnimationFrame(img, { - duration: 0, - }); +/** + * Get all frames of a gif. + * For the moment, it must use a tmp canvas to get each frames due to a bug of gitken. + * See this issues: https://github.com/aaharu/gifken/issues/22 + */ +async function getFrames({ gif, width, height }: { + gif: Gif, + width?: number, + height?: number, +}): Promise[]> { + const canvas: HTMLCanvasElement = document.createElement("canvas"); + const ctx = canvas.getContext("2d") as CanvasRenderingContext2D ; + canvas.width = width || gif.width; + canvas.height = height || gif.height; + ctx.clearRect(0, 0, gif.width, gif.height); + + // get all frames as img + const tmpImgs: HTMLImageElement[] = await Promise.all(gif.split(false).map((splited) => { + return new Promise((resolve, reject) => { + const img = new Image(width || gif.width, height || gif.height); + img.onload = () => resolve(img); + img.onerror = (e) => reject(e); + img.src = gifken.GifPresenter.writeToDataUrl(splited.writeToArrayBuffer()); + }) + })); + + // draw frame to the tmp canvas and create the final frames. + return tmpImgs.map((img: HTMLImageElement) => { + ctx.drawImage(img, 0, 0); + const newImg = new Image(); + newImg.src = canvas.toDataURL("image/gif"); + return new AnimationFrame(newImg, { + duration: 0, }); + }) } \ No newline at end of file From 4287271e67c7efe3d160aab2b1e8943a44f4d083 Mon Sep 17 00:00:00 2001 From: Emilien Date: Mon, 1 Jun 2020 16:30:40 +0200 Subject: [PATCH 06/10] Add currentFrameIndex, currentFrameComplexion and currentFrame properties --- packages/2d/src/Components/GIF.ts | 37 +++++++++++++++++++------------ 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/2d/src/Components/GIF.ts b/packages/2d/src/Components/GIF.ts index b3efbb99..42b8e2b7 100644 --- a/packages/2d/src/Components/GIF.ts +++ b/packages/2d/src/Components/GIF.ts @@ -18,7 +18,7 @@ export default function GIF(options: { let gif: Gif = new Gif(); let frames: AnimationFrame[] = []; let play: boolean = false; - let i = 0; + let currentFrameIndex: number = 0; load(options.url).then(async arrayBuffer => { gif = gifken.Gif.parse(arrayBuffer); @@ -29,46 +29,55 @@ export default function GIF(options: { }) setInterval(() => { - if (frames.length - 1 > i && play) { - i++; + if (frames.length - 1 > currentFrameIndex && play) { + currentFrameIndex ++; } - if(frames.length - 1 <= i && options.loop && play) { - i = 0; + if(frames.length - 1 <= currentFrameIndex && options.loop && play) { + currentFrameIndex = 0; } }, 1000 / (options.fps || 25)) }) - return { getGif() { return gif; }, drawCurrentFrame(context: CanvasRenderingContext2D, x: number = 0, y: number = 0) { - if(frames.length !== 0 && play) { - context.drawImage(frames[i].data, x, y); + if(frames.length !== 0) { + context.drawImage(frames[currentFrameIndex].data, x, y); } }, frames: frames, loop: options.loop || false, - currentFrameIndex: i, - currentFrame: frames[i], - currentFrameCompletion: 0, + get currentFrameIndex() { + return currentFrameIndex; + }, + get currentFrame() { + return frames[currentFrameIndex]; + }, + get currentFrameCompletion() { + if(frames.length !== 0) { + return currentFrameIndex / frames.length; + } + + return 1; + }, pause() { play = false; }, resume() { - play = true; + this.play(); }, play() { play = true; }, restart() { - i = 0; + currentFrameIndex = 0; play = true; }, goToFrame(frameNumber: number) { - i = frameNumber; + currentFrameIndex = frameNumber; }, } } From 11caadf0517e717ba84fe4d42567fc8b37898664 Mon Sep 17 00:00:00 2001 From: Emilien Date: Mon, 1 Jun 2020 16:41:40 +0200 Subject: [PATCH 07/10] Add doc and example for GIF component --- packages/2d/src/Components/GIF.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/2d/src/Components/GIF.ts b/packages/2d/src/Components/GIF.ts index 42b8e2b7..ae7ae8bc 100644 --- a/packages/2d/src/Components/GIF.ts +++ b/packages/2d/src/Components/GIF.ts @@ -7,6 +7,29 @@ interface GIFInterface extends AnimationAPI { drawCurrentFrame(context: CanvasRenderingContext2D, x?: number, y?: number): void; } +/** + * A component which able you to play and manipulate gifs into hex-engine. + * @example + * import someGifFile from "./your.gif"; + * + * export default function MyGif() { + * useType(MyGif); + * + * const gif = useNewComponent(() => GIF({ + * url: someGifFile, + * width: 200, + * height: 200, + * fps: 20, + * loop: true + * })); + * + * gif.play() + * + * useDraw((context) => { + * gif.drawCurrentFrame(context); + * }); + * } + */ export default function GIF(options: { url: string, width: number, From aba743c6aab140c6ef0346ec9636264578f5e87b Mon Sep 17 00:00:00 2001 From: Emilien Date: Mon, 13 Jul 2020 14:58:39 +0200 Subject: [PATCH 08/10] Update description of the GIF component --- packages/2d/src/Components/GIF.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/2d/src/Components/GIF.ts b/packages/2d/src/Components/GIF.ts index ae7ae8bc..8afa6b62 100644 --- a/packages/2d/src/Components/GIF.ts +++ b/packages/2d/src/Components/GIF.ts @@ -8,7 +8,7 @@ interface GIFInterface extends AnimationAPI { } /** - * A component which able you to play and manipulate gifs into hex-engine. + * A component which enables you to play and manipulate gifs in Hex Engine. * @example * import someGifFile from "./your.gif"; * From 79b3fcfc8ac5af686318407dd4d322fd86520667 Mon Sep 17 00:00:00 2001 From: Emilien Date: Mon, 13 Jul 2020 15:10:05 +0200 Subject: [PATCH 09/10] Adding Preloader to GIF component --- packages/2d/src/Components/GIF.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/2d/src/Components/GIF.ts b/packages/2d/src/Components/GIF.ts index 8afa6b62..f80d572f 100644 --- a/packages/2d/src/Components/GIF.ts +++ b/packages/2d/src/Components/GIF.ts @@ -1,6 +1,7 @@ import { AnimationAPI, AnimationFrame } from './Animation'; import { useType } from '@hex-engine/core'; import gifken, { Gif } from 'gifken'; +import { Preloader } from '..'; interface GIFInterface extends AnimationAPI { getGif(): Gif, @@ -43,7 +44,7 @@ export default function GIF(options: { let play: boolean = false; let currentFrameIndex: number = 0; - load(options.url).then(async arrayBuffer => { + const loadPromise = load(options.url).then(async arrayBuffer => { gif = gifken.Gif.parse(arrayBuffer); frames = await getFrames({ gif, @@ -62,6 +63,8 @@ export default function GIF(options: { }, 1000 / (options.fps || 25)) }) + Preloader.addTask(() => loadPromise); + return { getGif() { return gif; From 2639e621a5d38aedd23b02efda394d704e52babe Mon Sep 17 00:00:00 2001 From: Emilien Date: Mon, 13 Jul 2020 16:47:31 +0200 Subject: [PATCH 10/10] Update Preloader import --- packages/2d/src/Components/GIF.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/2d/src/Components/GIF.ts b/packages/2d/src/Components/GIF.ts index f80d572f..050170b6 100644 --- a/packages/2d/src/Components/GIF.ts +++ b/packages/2d/src/Components/GIF.ts @@ -1,7 +1,7 @@ import { AnimationAPI, AnimationFrame } from './Animation'; import { useType } from '@hex-engine/core'; import gifken, { Gif } from 'gifken'; -import { Preloader } from '..'; +import Preloader from '../Preloader'; interface GIFInterface extends AnimationAPI { getGif(): Gif,