{"id":21304935,"url":"https://github.com/maximilianmairinger/tweenobject","last_synced_at":"2026-07-15T19:02:16.620Z","repository":{"id":57382590,"uuid":"227814291","full_name":"maximilianMairinger/tweenObject","owner":"maximilianMairinger","description":"Tween any object deeply while disregarding non numeric values","archived":false,"fork":false,"pushed_at":"2020-05-09T12:36:37.000Z","size":167,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-19T07:47:19.603Z","etag":null,"topics":["animate","interpolate","keyframes","numeric-values","object","tween"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/tween-object","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maximilianMairinger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-13T10:27:04.000Z","updated_at":"2020-05-09T12:36:40.000Z","dependencies_parsed_at":"2022-09-01T04:40:40.839Z","dependency_job_id":null,"html_url":"https://github.com/maximilianMairinger/tweenObject","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/maximilianMairinger/tweenObject","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maximilianMairinger%2FtweenObject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maximilianMairinger%2FtweenObject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maximilianMairinger%2FtweenObject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maximilianMairinger%2FtweenObject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maximilianMairinger","download_url":"https://codeload.github.com/maximilianMairinger/tweenObject/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maximilianMairinger%2FtweenObject/sbom","scorecard":{"id":629370,"data":{"date":"2025-08-11","repo":{"name":"github.com/maximilianMairinger/tweenObject","commit":"e3c0aa1b3ea8b3a15e418a0632dee1fc334e868b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.6,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-21T07:24:27.483Z","repository_id":57382590,"created_at":"2025-08-21T07:24:27.483Z","updated_at":"2025-08-21T07:24:27.483Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35517479,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-15T02:00:06.706Z","response_time":131,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["animate","interpolate","keyframes","numeric-values","object","tween"],"created_at":"2024-11-21T16:16:24.881Z","updated_at":"2026-07-15T19:02:16.566Z","avatar_url":"https://github.com/maximilianMairinger.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tween object\n\nTween any object deeply while disregarding non numeric values\n\n## Example\n\n### Basic Usage\n\nGive any equivilant primitive (same type) or object (same structure) as from an to parameter. Objects will be recursively parsed and every found number will be interpolated. Any non numeric values will be ignored. \n\n```js\nimport TweenObject from \"tween-object\"\n\nlet from = 0\nlet to = 100\n// optional: \nlet duration = 1\nlet easing = x =\u003e x\n\nlet tween = new TweenObject(from, to, duration, easing)\n```\n\nEasing must be given as a function recieving a number between `0` and `1` and returning one between `0` and `1`. To generate such functions from bezier easing declarations programatically, have a look at [bezier-easing](https://www.npmjs.com/package/bezier-easing).\n\n-----------------\n\nYou can control a tween instance via `update(at?: number)`. `at` spans from 0 to `duration`, thus in this case 0 .. 1. Parameters exceeding this limit will be kept at the maximum. \n\nWhen omiting the `at` parameter, the time delta from the inital update will be taken to calculate the progress.\n\nResults can be recieved directly as returned property on the update call, or via a registered `onUpdate` listener. The unregister call `offUpdate(func)`.\n\n```js\ntween.onUpdate((val) =\u003e {\n  console.log(val)\n})\n\nconsole.log(tween.update(.5))\n```\n\n-----------------\n\nTo use Tween object as an animation interpolator use [animation-frame-delta](https://www.npmjs.com/package/animation-frame-delta) as animation loop handler, as it has been extensively tested to work well in combination.\n\n```js\nlet from = 250\nlet to = 500\nlet duration = 1000\nlet tween = new TweenObject(from, to, duration)\n\nanimationFrameDelta((progress) =\u003e {\n  tween.update(progress)\n}, duration)\n\n// or the more cool but less readable version\n\nanimationFrameDelta(tween.update.bind(tween), duration)\n```\n\n### Advanced Options\n\nInstead of the shortcuts duration and easing, a object containing more advanced options can be given. \n\n```js\nlet opions = {\n  start: 0,\n  end: 1,\n  easing: a =\u003e a, \n  iterations: 1,\n  fill: true\n}\n\nlet tween = new TweenObject(from, to, options)\n```\n\nThe values displayed above are the defaults, you may omit any of the properties to fall back to these values. \n\n-----------------\n\nThe used config can be read like so\n\n\u003e Note that this is a readonly object. You are unable to change an instantiated tween.\n\n```js\nlet usedOptions = tween.options\n\nconsole.log(\"This tween has \" + usedOptions.iterations + \" iterations.\")\n```\n\nThese are the given options merged with the default config. This is potentually interesting if youd like to rely on the built in option resolution of tween object as it supports `duration` \u0026 `easing` as standalone properties.\n\n### Multiple Keyframes\n\nTo interpolate over multiple keyframes set the first constructor argument to true and give a list of keyframes as second.\n\n```js\nlet multipleKeyframes = true\nlet keyframes = [\n  {prop: 100, offset: 0},\n  {prop: 250, offset: .3},\n  {prop: 300},\n  {prop: 500, offset: 1}\n]\n\nlet tween = new TweenObject(multipleKeyframes, keyframes)\n```\n\nThe offset property on a keyframes indecates the position in the animation (0 beeing the begin and 1 the end), similar to the [WAAPI](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Keyframe_Formats) implementation. Offset can be omit as it will be filled by equally distributed values (thus, `0.65` for the above example).\n\n-----------------\n\nSide Note: Single property keyframes can be given not nested inside a explicit object. This notation (useing wrapped primitives) works as well\n\n```js\nlet multipleKeyframes = true\nlet specialPropWithOffset = new Number(250)\nspecialPropWithOffset.offset = .3\nlet keyframes = [\n  100\n  specialPropWithOffset\n  300,\n  500\n]\n\nlet tween = new TweenObject(multipleKeyframes, keyframes)\n```\n\n\n### Extention\n\nA use case for Tween object could be the interpolation of svg paths. There is already a library with a more polished implementation of what will be shown here ([tween-svg-path](https://www.npmjs.com/package/tween-svg-path)) out there, but for the sake of getting familiar with the concept I will stick to this use case.\n\nAs you want to abstract the inner workings of tweening as far as possible, in order to give the user / developer using it an as easy to understand as possible interface to work with, we will now absract the parsing from the svg-path string to a interpolatable object structure.\n\n\u003e Note: the actual parsing to an object structure will be done by the libraries [parse-svg-path](https://www.npmjs.com/package/parse-svg-path), [abs-svg-path](https://www.npmjs.com/package/abs-svg-path) and [normalize-svg-path](https://www.npmjs.com/package/normalize-svg-path).\n\n\u003e Note: This example uses typescript to display the point of parsing better.\n\n```ts\n// The default export (TweenObject) used in the last examples is an implementation of the abstract class Tween without any parsing.\nimport { Tween } from \"tween-object\"\n\nimport * as parse from \"parse-svg-path\"\nimport * as abs from \"abs-svg-path\"\nimport * as normalize from \"normalize-svg-path\"\n\ntype Face = string\ntype Interior = (string | number)[][]\n\nclass TweenSvgPath extends Tween\u003cFace, Interior\u003e {\n  // override the parseIn method to automatically convert all input to the Interior type (object structure)\n  protected parseIn(face: Face): Interior {\n    return normalize(abs(parse(face)))\n  }\n  // override the parseOut method to automatically convert all output to the Face type (svg-path)\n  protected parseOut(interior: Interior): Face {\n    let s = \"\"\n    for (let i = 0; i \u003c interior.length; i++) {\n      s += interior[i].join(\" \") + \" \"\n    }\n    s = s.substr(0, s.length-1)\n    return s\n  }\n}\n```\n\nThis way `TweenSvgPath` can be interacted with just by using svgpaths, and never having to parse anything manually.\n\n```ts\nlet from = \"todo\"\nlet to = \"todo\"\nlet duration = 2000\nlet tween = new TweenSvgPath(from, to, duration)\n\nanimationFrameDelta(tween.update.bind(tween), duration)\n\n// Note to select to path\nconst svgPathElem = document.querySelector(\"svg#tweeny path\")\ntween.onUpdate((path) =\u003e {\n  svgPathElem.setAttribute(\"d\", path)\n})\n```\n\n\n## Conribute\n\nAll feedback is appreciated. Create an pull request or write an issue.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaximilianmairinger%2Ftweenobject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaximilianmairinger%2Ftweenobject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaximilianmairinger%2Ftweenobject/lists"}