{"id":27139814,"url":"https://github.com/noname0310/tw-engine-498tokio","last_synced_at":"2025-09-09T13:40:21.313Z","repository":{"id":49853372,"uuid":"473512030","full_name":"noname0310/tw-engine-498tokio","owner":"noname0310","description":"web game animation example","archived":false,"fork":false,"pushed_at":"2023-06-24T12:27:52.000Z","size":7122,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-22T00:52:23.400Z","etag":null,"topics":["animation","game","game-engine","typescript","web"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/noname0310.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":"2022-03-24T08:10:11.000Z","updated_at":"2022-04-23T10:22:32.000Z","dependencies_parsed_at":"2023-02-03T13:46:33.538Z","dependency_job_id":null,"html_url":"https://github.com/noname0310/tw-engine-498tokio","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/noname0310/tw-engine-498tokio","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noname0310%2Ftw-engine-498tokio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noname0310%2Ftw-engine-498tokio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noname0310%2Ftw-engine-498tokio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noname0310%2Ftw-engine-498tokio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noname0310","download_url":"https://codeload.github.com/noname0310/tw-engine-498tokio/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noname0310%2Ftw-engine-498tokio/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266424023,"owners_count":23926124,"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","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"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":["animation","game","game-engine","typescript","web"],"created_at":"2025-04-08T04:59:41.405Z","updated_at":"2025-07-22T04:04:38.028Z","avatar_url":"https://github.com/noname0310.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tw-engine-498tokio\n![license: MIT](https://img.shields.io/github/license/noname0310/tw-engine-498tokio)\n![version](https://img.shields.io/npm/v/tw-engine-498tokio)\n\nimplementation of a keyframe animation for web game.\n\n## about\n- keyframe animation with audio sync\n- use typescript for web\n- this implementation can be used anywhere (not dependent on the game engine)\n\n## [build](https://noname0310.github.io//tw-engine-498tokio/build/index.html)\n![preview](docs/preview.png)\nAll animations in this project are rendered in CSS (do not use WebGL)\n[reference: 498 Tokio](https://www.youtube.com/watch?v=-lHRRVnoE0Y)\n\n## usage\nfor install,\n```npm\nnpm i tw-engine-498tokio\n```\n\n\n\n**first, you need `requestAnimationFrame` based player**\n\n```typescript\nimport { IAnimationInstance } from \"tw-engine-498tokio\";\n\n//simple animation player that use requestAnimationFrame\nclass AnimationPlayer {\n    public frameRate = 60;\n    private _startTime = 0;\n    private _time = 0;\n    private _animationInstance: IAnimationInstance;\n\n    public constructor(animationInstance: IAnimationInstance) {\n        this._animationInstance = animationInstance;\n    }\n\n    public start() {\n        this._time = performance.now();\n        this._currentTime = 0;\n        this.animate();\n    }\n\n    private animate = () =\u003e {\n        const now = performance.now();\n        const delta = now - this._time;\n        this._time = now;\n        this._currentTime += delta;\n        const frameTime = this._currentTime * this.frameRate;\n        this._animationInstance.process(frameTime);\n        \n        if (track1.endFrame \u003c frameTime) {\n            requestAnimationFrame(this.animate);\n        }\n    }\n}\n```\n\n---\n### Animation Track\n`AnimationTrack` is the smallest unit of container that can store animation.\nYou can create an animation with the code below and bind it to the object you want.\n\n```typescript\n//define track that has scalar key\nexport const track1 = AnimationTrack.createScalarTrack([\n    AnimationKey.createValueType(0, 0, InterpolationKind.Linear), //frame 0, value 0\n    AnimationKey.createValueType(60, 1, InterpolationKind.Linear)  //frame 60, value 1\n])\n\nconst div = document.getElementById(\"animated-div\")!;\n\n//create animation instance with bind info\nconst divAnimInstance = track1.createInstance(value =\u003e div.style.opacity = value.toString());\n\nnew AnimationPlayer(divAnimInstance).start();\n```\n\n---\n### Animation Clip\n`AnimationClip` is an animation container that contains several animation tracks. It's perfect for creating a little bit of a complicated animation.\n\n```typescript\n//animation clip can contain multiple tracks\nconst clip1 = new AnimationClip([\n    {\n        name: \"track1\" as const,\n        // you can create AnimationTrack to use constructor, in this case, AnimationTrack need interpolator\n        track: new AnimationTrack\u003cnumber\u003e([\n            AnimationKey.createValueType(0, 0, InterpolationKind.Linear), //frame 0, value 0\n            AnimationKey.createValueType(60, 1, InterpolationKind.Linear)  //frame 60, value 1\n        ], ScalarInterpolator)\n    },\n    {\n        name: \"track2\" as const,\n        // this track use cubic hermite interpolation\n        track: AnimationTrack.createScalarTrack([\n            AnimationKey.createValueType(0, 0, InterpolationKind.Cubic, 0, 0), //frame 0, value 0\n            AnimationKey.createValueType(60, 1, InterpolationKind.Cubic, 0, 0)  //frame 120, value 1\n        ])\n    }\n])\n\nconst div1 = document.getElementById(\"animated-div1\")!;\nconst div2 = document.getElementById(\"animated-div2\")!;\n\nconst divAnimInstance = clip1.createInstance(new AnimationClipBindInfo([\n    {\n        trackName: \"track1\" as const,\n        target: (value: number) =\u003e div1.style.opacity = value.toString()\n    },\n    {\n        trackName: \"track2\" as const,\n        target: (value: number) =\u003e div2.style.opacity = value.toString()\n    }\n]));\n\nnew AnimationPlayer(divAnimInstance).start();\n```\n\n---\n### Animation Sequence\n`AnimationSequence` can contain animation tracks, animation clips, and themselves recursively.\nThis container is optimized for use with very long animations over 3 minutes.\n\n```typescript\n//animation sequence can contain multiple animation clips, tracks and even them self\nconst sequence1 = new AnimationSequence([\n    new RangedAnimation(track1),\n    new RangedAnimation(clip1, 20, 1, 50), // RangedAnimation that offset 20, start frame 1, end frame 50\n    new RangedAnimation(AnimationTrack.createVector2Track([\n        //Vector2 is class so you must create key by createRefType\n        AnimationKey.createRefType(0, new Vector2(0, 0), InterpolationKind.Linear), //frame 0, value 0, 0\n        AnimationKey.createRefType(60, new Vector2(1, 1), InterpolationKind.Linear)  //frame 60, value 1, 1\n    ]))\n]);\n\nconst div1 = document.getElementById(\"animated-div1\")!;\nconst div2 = document.getElementById(\"animated-div2\")!;\n\nconst divAnimInstance = sequence1.createInstance([\n    (value: number) =\u003e div1.style.opacity = value.toString(),\n    new AnimationClipBindInfo([\n        {\n            trackName: \"track1\" as const,\n            target: (value: number) =\u003e div2.style.opacity = value.toString()\n        },\n        {\n            trackName: \"track2\" as const,\n            target: (value: number) =\u003e div2.style.opacity = value.toString()\n        }\n    ]),\n    (value: Vector2) =\u003e {\n        div1.style.left = value.x.toString();\n        div1.style.top = value.y.toString();\n    }\n]);\n\nnew AnimationPlayer(divAnimInstance).start();\n```\n\n## Credits\n\n- [Babylon.js Animations](https://github.com/BabylonJS/Babylon.js/tree/master/packages/dev/core/src/Animations)\n- [Three.js Animation](https://github.com/mrdoob/three.js/tree/dev/src/animation)\n- [decode-audio-data-fast](https://github.com/soundcut/decode-audio-data-fast)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoname0310%2Ftw-engine-498tokio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoname0310%2Ftw-engine-498tokio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoname0310%2Ftw-engine-498tokio/lists"}