{"id":15061058,"url":"https://github.com/maxpleaner/animation-loader","last_synced_at":"2026-01-02T10:35:45.965Z","repository":{"id":57180174,"uuid":"87272237","full_name":"MaxPleaner/animation-loader","owner":"MaxPleaner","description":"webpack gif manipulator (resize, make transparent, merge, change to webm)","archived":false,"fork":false,"pushed_at":"2017-04-15T18:32:01.000Z","size":2226,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-21T19:47:21.441Z","etag":null,"topics":["coffeescript","gif","imagemagick","webpack-loader"],"latest_commit_sha":null,"homepage":"","language":"CoffeeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MaxPleaner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-05T06:04:36.000Z","updated_at":"2017-12-27T16:37:04.000Z","dependencies_parsed_at":"2022-09-26T16:31:17.762Z","dependency_job_id":null,"html_url":"https://github.com/MaxPleaner/animation-loader","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxPleaner%2Fanimation-loader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxPleaner%2Fanimation-loader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxPleaner%2Fanimation-loader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaxPleaner%2Fanimation-loader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MaxPleaner","download_url":"https://codeload.github.com/MaxPleaner/animation-loader/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243707382,"owners_count":20334619,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["coffeescript","gif","imagemagick","webpack-loader"],"created_at":"2024-09-24T23:08:40.008Z","updated_at":"2026-01-02T10:35:45.914Z","avatar_url":"https://github.com/MaxPleaner.png","language":"CoffeeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"### About\r\n\r\nThis is a webpack loader which takes gifs as its input. Specifically, it takes their path as the value to `require`, and\r\nthe query params of the path are read for further instructions. The output (return value of `require`) is a string - \r\na temporary path of a modified gif or webm.\r\n\r\nOnly gifs can receive modifications (so far), but the modified gifs can be converted to webm (preserving resizing and transparency).\r\n\r\nGifs can also be merged, creating multi-layered animations that make use of\r\nthe transparency.\r\n\r\nThe inspiration for writing with this was the Phaser.js example of webm video\r\nwith an alpha channel. Gifs only have a pseudo alpha channel but there are much\r\nmore accessible tools for manipulating them. Plus with some help I managed to\r\nconvert transparent gifs to transparent webms. Once I'd gathered shell scripts\r\nto do these things, I wondered how it would work in a webpack loader. Thus\r\nthis project was borne. \r\n\r\n### Building a sample webpack application with animation-loader\r\n\r\n1. _NPM packages_  \r\n\r\n    the package is on NPM.\r\n\r\n    ```sh\r\n    npm install --save webpack webpack-dev-server\r\n    npm install --save jquery coffee-loader # this is optional, but it's used for this guide\r\n    npm install --save animation-loader\r\n    ```\r\n\r\n    also add this in the scripts section of `package.json`:  \r\n\r\n    ```json\r\n    \"scripts\": { \"dev\": \"./node_modules/.bin/webpack-dev-server --content-base . --inline --hot\" }\r\n    ```\r\n\r\n2. _System dependencies_  \r\n\r\n    This loader makes use of childProcess calls and requires the system to have \r\n    these Unix programs (these instructions for Ubuntu, but they're common libraries and should be available \r\n    on most distributions and osX):\r\n    ```sh\r\n    sudo apt-get install ffmpeg imagemagick\r\n    ```\r\n\r\n2. In `webpack.config.js`:  \r\n\r\n    This sets up `loader.coffee` to be the entry point of the application.  \r\n    `bundle.js` is an in-memory concatenation built by webpack.  \r\n    ```js\r\n    module.exports = {\r\n      entry: './loader.coffee',\r\n      output: {\r\n        filename: 'bundle.js'\r\n      },\r\n\r\n      module: {\r\n        loaders: [\r\n          { test: /\\.coffee$/, loader: \"coffee-loader\" },\r\n          { test: /\\.gif$/, loader: ['raw-loader', \"animation-loader\"] },\r\n        ]\r\n      },\r\n\r\n      resolve: {\r\n        extensions: [\".coffee\", \".js\", \".gif\"],\r\n      }\r\n    }\r\n\r\n    ```\r\n\r\n3. Place the following gif saved as `octopus.gif` in the root of the repo:  \r\n\r\n    _note_ I i did not create this, got it by literally searching google images for 'gif'  \r\n\r\n    ![octopus gif](./octopus.gif)\r\n\r\n\r\n4. Add this small `index.html` file:  \r\n\r\n    ```html\r\n    \u003c!doctype html\u003e\r\n    \u003chtml lang=\"en\"\u003e\r\n      \u003chead\u003e\u003c/head\u003e\r\n      \u003cbody\u003e\u003cscript src=\"bundle.js\"\u003e\u003c/script\u003e\u003c/body\u003e\r\n    \u003c/html\u003e\r\n    ```\r\n\r\n5. Populate `loader.coffee` with this:  \r\n\r\n    ```coffee\r\n    $ = require 'jquery'\r\n    $ -\u003e\r\n      webm_path = require \"./octopus.gif?transparent=true\u0026color=00AEFF\u0026resize=150x100\u0026to_webm=true\"\r\n      $(\"body\").append($ \"\"\"\r\n        \u003cvideo autoplay loop src=\"./#{foo}\"\u003e\r\n      \"\"\")\r\n    ```\r\n    \r\n    I should mention that I don't feel this require string is very pretty, but webpack\r\n    requires every string passed to require be statically analyzable and therefore it can have no\r\n    variables. At least it's terse :D\r\n\r\n6. Start the server with `npm run dev` and visit http://localhost:8080\r\n\r\n7. Observe the following image now present on the page:  \r\n\r\n   ![modified gif](./modified-octopus.gif)  \r\n\r\n   It has been resized and also made transparent on the light blue color `#00AEFF`.  \r\n\r\n\r\n### Other notes\r\n\r\nThe `.animation-loader` folder is where the modified assets are stored. It is automatically created.\r\nIt is cleared out every time the server starts. Perhaps in the future it will implement caching that persists across restarts.\r\nIt can be added to `.gitignore` since everything in there is dynamically generated.\r\n\r\nPhaser and HTML have a way to scale videos, so resizing them in a preprocessor is not necessarily needed. However it can improve the performance and also it will likely be buggy to try and merge two images of different sizes.\r\n\r\n### API details\r\n\r\nOnce the webpack setup is in place, there's only one place this loader becomes relevant and that's `require`. With the proper\r\n`test` definition in `webpack.config.js` (shown above), `gif` paths passed to `require` will pass through both the raw-loader and animation-loader.\r\n\r\nIf nothing else is given besides the path, the return value will be a tempoary path pointing to the image\r\n\r\n```coffee\r\nthe_path = require './octopus.gif'\r\nconsole.log the_path \r\n# =\u003e \".animation-loader/kasmdaknsduiasdkasdm.gif\"\r\n```\r\n\r\nWith the exception of `merge`, everything else is found in the query params. Here is the full list of keys:\r\n\r\n- `transparent` if this is truthy then the following keys are checked  \r\n    - `color` a hex code like `000000` (black) or `FFFFFF` (white). Defaults to black.\r\n    - `fuzz` the percent leniency when turning color into transparency. Defaults to 25\r\n- `resize` the value is a width/height such as `\"1400x1400\"`\r\n- `to_webm` a boolean. the output path will be webm \r\n- `name` - used to identify a gif so it can be merged with another.  \r\n  If provided, this also becomes the final filename, i.e.\r\n  `.animation-loader/foo.gif` if `foo` is the name.\r\n\r\nMerging:\r\n\r\nConceptually this takes two existing files and creates a new one, combing them.\r\nHowever, Webpack doesn't make it easy to write a loader which is passed a filepath that _doesnt yet exist_.\r\n(I think it is possible with a plugin, but that will require some work). To get around this, the loader\r\nautomatically creates an empty `.merge.gif` file at the root of the project that can be required to start a merge command.\r\nThis can be added to `.gitignore` of course.\r\n\r\nIf a source file is in the `lib/` directory, for example, the require path will need to be modified:\r\n`require '../.merge.gif'` and if the source file is in the root of the repo, it would be `require '.merge.gif'`\r\n\r\nIf this file is passed to require, then three query params need to be present as well:\r\n\r\n- `size` width/height e.g. \"500x500\". Imagemagick needs this info as part of the merge command, even if both gifs are the same size. Note this is **not** used to resize. If smaller than the original, it will **crop** an area starting at the top left.\r\n- `background` - the `name` of the image with the lower z-index\r\n- `foreground` - the `name` of the image with higher z-index (probably the transparent one)\r\n\r\nThis loader can't merge webm videos or doing any manipulations on them, so conversion to webm should happen\r\nas the last step.\r\n\r\nThe merge command can be passed a 'name' as well. That way multiple merges can be layered.\r\n\r\nKeep in mind here that if image C is a merged version of images A and B, it will\r\nnot auto-update when either of these have changed. The only way to force that\r\nto happen without restarting the server is to change the value of `name` for\r\nthe merge asset.\r\n\r\n### Examples\r\n\r\n**turn all green in an image to transparent (like a greenscreen)**\r\n\r\n`gif_path = require './my_image.gif?transparent=true\u0026color=00FF00'`\r\n\r\n**make only a very specific color transparent (set fuzz to 0)**\r\n\r\n`gif_path = require './my_image.gif?transparent=true\u0026color=F3F4F5\u0026fuzz=0'`\r\n\r\n**resize a gif**\r\n\r\n`gif_path = require './my_image.gif?resize=500x500'`\r\n\r\n**convert to webm (note that this works with any of the other commands)**\r\n\r\n`webm_path = require './my_image.gif?to_webm'`\r\n\r\n**referencing a dynamically generated gif by setting its `name`**\r\n\r\nThe path will be `\"./.animation-loader/\u003cname\u003e.\u003cext\u003e\"`,\r\nwhere `\u003cext\u003e` is gif unless `to_webm` was specified, in which case it's webm\r\n\r\nHowever this loader does not yet implement any transformations for webm, so webm paths are\r\nnever passed to require. \r\n\r\n```coffee\r\ngif_version = require './my_image.gif?resize=100x200\u0026name=gif_version'\r\nwebm_version = require './.animation-loader/gif_version.gif?to_webm=true'\r\n```\r\n\r\nSince the `.animation-loader` folder is in the root of the repo the path may have to be adjusted depending on the location of the file.\r\n\r\n**merge a background with a partly-transparent foreground**\r\n\r\n```coffee\r\nforeground = require './my_image.gif?transparent=true\u0026name=foreground'\r\nbackground = require './my_image.gif?name=background'\r\nmerged = require './.merge.gif?size=200x200\u0026background=background\u0026foreground=foreground\u0026name=merged'\r\n```\r\n\r\n**merge on a merge**\r\n\r\nin this example, img2 is made transparent on black and merge1 is made transparent\r\non green.\r\n\r\n```coffee\r\nimg1 = require './img1.gif?name=img1\u0026resize=100x100'\r\nimg2 = require './img2.gif?name=img2\u0026resize=100x100\u0026transparent=true'\r\nimg3 = require './img3.gif?name=img2\u0026resize=100x100'\r\n\r\nmerge1 = require './.merge.gif?name=merge1\u0026background=img1\u0026foreground=img2\u0026size=100x100\u0026transparent'\r\nmerge2 = require './.merge.gif?name=merge2\u0026background=img3\u0026foreground=merge1\u0026size=100x100'\r\n```\r\n\r\nHere's an example of an image created with this technique (no less than 6 gifs overlaid):\r\n\r\n![crazy image](./merged.gif)\r\n\r\n### Source code\r\n\r\nThis is written in coffeescript, but it's all in one file, so compilation is\r\ndone through `coffee -wc index.coffee` which updates `index.js` (the entry\r\npoint specified in `package.json`).\r\n\r\nThe file has comments, and of course contribution is valued.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxpleaner%2Fanimation-loader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxpleaner%2Fanimation-loader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxpleaner%2Fanimation-loader/lists"}