{"id":17239339,"url":"https://github.com/baseten/react-css-transform","last_synced_at":"2025-04-14T02:52:54.874Z","repository":{"id":65483010,"uuid":"185840828","full_name":"baseten/react-css-transform","owner":"baseten","description":"Handle multiple nested 2D and 3D CSS Transforms like a wizard ","archived":false,"fork":false,"pushed_at":"2023-04-30T11:31:57.000Z","size":2887,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-27T16:55:56.679Z","etag":null,"topics":["3d","css-transforms","javascript","matrix","matrix-multiplication","react","reactjs"],"latest_commit_sha":null,"homepage":"https://baseten.github.io/react-css-transform/","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/baseten.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-09T17:08:24.000Z","updated_at":"2024-09-21T04:53:14.000Z","dependencies_parsed_at":"2025-02-24T15:38:36.318Z","dependency_job_id":"9f60c078-255e-4065-9a93-0f0be87b1228","html_url":"https://github.com/baseten/react-css-transform","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baseten%2Freact-css-transform","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baseten%2Freact-css-transform/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baseten%2Freact-css-transform/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baseten%2Freact-css-transform/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/baseten","download_url":"https://codeload.github.com/baseten/react-css-transform/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248497891,"owners_count":21113983,"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":["3d","css-transforms","javascript","matrix","matrix-multiplication","react","reactjs"],"created_at":"2024-10-15T05:48:29.592Z","updated_at":"2025-04-14T02:52:54.856Z","avatar_url":"https://github.com/baseten.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm version](https://badge.fury.io/js/react-css-transform.svg)](https://badge.fury.io/js/react-css-transform)\n![dependencies](https://david-dm.org/baseten/react-css-transform.svg)\n![travis build](https://travis-ci.com/baseten/react-css-transform.svg?branch=master)\n[![codecov](https://codecov.io/gh/baseten/react-css-transform/branch/master/graph/badge.svg)](https://codecov.io/gh/baseten/react-css-transform)\n\n# React CSS Transform\n\nA React helper library to handle nested 2D and 3D CSS transforms using matrix multiplication, \ndrastically reducing the number of nested DOM elements required and making complex \ncombinations of nested transformations trivial.\n\n`\u003cTransform2d\u003e` was initially developed while I was working at [Pest Pulse](https://www.pestpulse.com/) \nfor a zoomable/pannable map with markers.\n\n## Install\n\n`react-css-transform` uses the [gl-matrix library](https://github.com/toji/gl-matrix)\nunder the hood. It's a peer dependency so install like so:\n\n`npm install gl-matrix react-css-transform --save`\n\n## Why\n\nWhile it might be a little niche, there have been several projects over the last few years\nwhere I've thought about writing this and ended up going with a \"faster\", hackier\nsolution using nested divs and manual inline calculations. Often the nested divs caused\nme problems with mouse and UI events. I thought I'd do it properly this time and thanks to\nPest Pulse for letting me open source it.\n\nIf you're just doing basic transforms on a single element, you probably don't need this \nlibrary. It is super useful for doing nested transformations and it can be useful on a \nsingle element if you want to ensure consistent application of the transform, scale and \nrotate transformations. If you hadn't noticed the order is important!\n\n## Examples\n\n[3D Cubes Example](https://baseten.github.io/react-css-transform/3d-cubes/index.html)\n\n![3D Cubes GIF](https://baseten.github.io/react-css-transform/3d-cubes.gif)\n\n## Usage\n\nIn general it is best to make sure that the HTML elements you are transforming have styles\nset like so:\n\n```css\nposition: absolute;\nleft: 0;\ntop: 0;\n```\n\nIf you are using 3D Transforms you also most likely want to set the containing HTML Element \nto have `preserve-3d` and some `perspective`:\n\n```css\ntransform-style: preserve-3d;\nperspective: 500px;\n```\n\n### \u0026lt;Transform2d /\u003e\n\nThe styles provided to the `\u003cdiv /\u003e` below will be merged with the computed CSS `matrix` transform\n\n```javascript\n\u003cTransform2d translate={{x:10, y:20}} scale={2} rotate={Math.PI} /\u003e\n  \u003cTransform2d translate={{x: -50, y:-50}}\u003e\n    // ... as much nesting as you like\n    \u003cdiv style={{width: 100, height: 100, margin: -50, background: '#f00'}} /\u003e\n  \u003c/Transform2d\u003e\n\u003c/Transform2d\u003e \n```\n\n#### Props\n\n##### - multiplicationOrder\n\n**Optional**. An enum: either `MULTIPLICATION_ORDER.PRE` or `MULTIPLICATION_ORDER.POST`.\nThis determines the order an object's local matrix is multiplied with it's parent's matrix world.\nThe default is `MULTIPLICATION_ORDER.POST`. You can only set this at the most outer `Transform2d`\ncomponent. `PRE` will mimic how the transforms would be applied if you were doing them as\nactual nested DOM elements. `POST` is much more natural mathematically and way more useful.\nYou should use `POST` :)\n\n##### - translate\n\n**Optional**. An object describing translation. Either a plain JS object or a gl-matrix \n`vec2`. If you pass in a JS object without all dimensions, missing dimensions will be \ngiven `0` as the default value. If nothing is supplied no translation occurs.\n\n##### - scale\n\n**Optional**. The transform's scale. either a number, a plain JS object or a gl-matrix \n`vec2`. A number will apply the same scale to `x` and `y`. If you pass in a JS object \nwithout all dimensions, missing dimensions will be given `1` as the default value. If\nnothing is supplied no scaling occurs. \n\n##### - rotate\n\n**Optional**. The transform's rotation. A number provided in **radians**. If nothing is \nsupplied no rotation occurs.\n\n### \u0026lt;Transform3d /\u003e\n\nThe styles provided to the `\u003cdiv /\u003e` below will be merged with the CSS `matrix3d` transform\n\n```javascript\nconst translateToCentre = {\n  x: window.innerWidth / 2,\n  y: window.innerHeight / 2\n};\n\nconst theta = Math.PI / 2;\nconst yAxis = { x: 0, y: 1, z: 0 };\nconst zAxis = { x: 0, y: 0, z: 1 };\n\n\u003cTransform3d translate={translateToCentre} rotate={theta} rotateAxis={yAxis}\u003e\n  \u003cTransform3d rotate={theta} rotateAxis={zAxis}\u003e\n    // ... as much nesting as you like\n    \u003cdiv style={{width: 100, height: 100, margin: -50, background: '#f00'}} /\u003e\n  \u003c/Transform3d\u003e\n\u003cTransform3d translate={cubeGroup1Translate}\u003e\n```\n\n#### Props\n\n##### - multiplicationOrder\n\n**Optional**. An enum: either `MULTIPLICATION_ORDER.PRE` or `MULTIPLICATION_ORDER.POST`.\nThis determines the order an object's local matrix is multiplied with it's parent's matrix world.\nThe default is `MULTIPLICATION_ORDER.POST`. You can only set this at the most outer `Transform2d`\ncomponent. `PRE` will mimic how the transforms would be applied if you were doing them as\nactual nested DOM elements. `POST` is much more natural mathematically and way more useful.\nYou should use `POST` :)\n\n##### - translate\n\n**Optional**. An object describing translation. Either a plain JS object or a gl-matrix \n`vec3`. If you pass in a JS object without all dimensions, missing dimensions will be \ngiven `0` as the default value. If nothing is supplied no translation occurs.\n\n##### - scale\n\n**Optional**. The transform's scale. either a number, a plain JS object or a gl-matrix \n`vec3`. A number will apply the same scale to `x`, `y` and `z`. If you pass in a JS object \nwithout all dimensions, missing dimensions will be given `1` as the default value. If\nnothing is supplied no scaling occurs. \n\n##### - rotate\n\n**Optional**. The transform's rotation. A number provided in **radians**. If nothing is \nsupplied no rotation occurs.\n\n##### - rotateAxis\n\n**Optional**. The axis to rotate around. Either a plain JS object or a gl-matrix `vec3`.\nIt can be any arbitrary axis, but it must be normalized (a unit vector). If nothing is \nsupplied it defaults to the z-axis: `{x: 0, y: 0, z: 1}`. \n\n## Gotchas\n\n### 2D and 3D Transforms together\n\nMake sure you don't mix `Transform2d` and `Transform3d` components together! You can use\n`Transform3d` almost in exactly the same way as `Transform2d` if you want to force a 3D \nCSS transform, or combine with other `Transform3d`s. The only caveat here is it's better\nto pass scale in as an object rather than a number (otherwise z will be scaled too).\n`Transform3d` sets the default `rotateAxis` as the z axis, so it will rotate like a\n`Transform2d`:\n\n`\u003cTransform3d translate={{x:10, y:10}} scale={{x:2, y:2}} rotate={Math.PI} /\u003e`\n\n### Performance\n\nThe library is generally very performant, mainly as it leverages the brilliant `gl-matrix`\nlibrary and obviously React's virtual DOM. There are a couple of issues it's worth being\naware of though. These are mainly applicable when doing 3D transformations and/or when\ncalling `render` in a `requestAnimationFrame` callback:\n\n* When running in development mode, `propTypes` checking can cause performance bottlenecks.\nChrome seems to suffer from this more than Firefox and Safari. If you're seeing \nperformance issues, try running a production build first before going down any other \nrabbit holes.\n\n* With standard React apps you've probably got used to declaring lambdas and object literals\ninline in your render method. For standard UI where renders are normally limited to user \ninteraction, you most likely won't notice a performance hit doing this. If you're gonna do \n3D transformations or just a complex `render` every frame, you *may* want to reconsider \nthis. Modern browsers can create and dispose of objects and arrays very quickly, but when \nthey're disposed of the garbage collector *may* cause a janky animation. As with all \nperformance optimization don't do it unless you need to. Just sayin' :) \n\n### IE10 and IE11\n\nIE10 and IE11 famously don't support `preserve-3d`. To a certain extent this library can\nhelp with issues here because it doesn't create any nested DOM elements, but computes \nmatrices to use on a single set of children. However, you will still very likely run into \nz order issues as IE will maintain DOM / z-index order over 3D z position. This \nrestriction makes doing complex 3D transformations in these browsers impossible. \nUltimately it depends on your use case and whether you have to support them.\n\n## How it works\n\nUnder the hood `Transform2d` and `Transform3d` work in pretty much exactly the same way.\nThe `gl-matrix` library is used to handle all the vector and matrix maths, calculating\nan object's local matrix based on the supplied props (`translate`, `scale`, `rotate` and \nfor `Transform3d` `rotateAxis`). These are multiplied together in the standard `T * R * S`\norder. This local matrix is multiplied with the `parentMatrixWorld` (which is automatically\npassed down from any parent Transforms) to produce the object's `matrixWorld`. This \n`matrixWorld` can then be used in an HTML element's [CSS transform property](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix), either as\n`matrix` or `matrix3d` depending on the transform. The `matrixWorld` is then passed down\nto any nested Transforms as the next `parentMatrixWorld`.\n\n`Transform2d` and `Transform3d` handle passing down these props internally as well as \nsetting the `style` property (while merging in any predefined styles) on their child HTML \nelements. But if you need to add any custom React Components in between them and the HTML\nelement you wish to transform, you will need to manually pass the props through, as you \ncan see [here](https://github.com/baseten/react-css-transform/blob/master/examples/3d-cubes/src/components/CubeGroup.js), \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbaseten%2Freact-css-transform","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbaseten%2Freact-css-transform","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbaseten%2Freact-css-transform/lists"}