From c20ab0e5473ed510e898f44bca63ee5cfce11315 Mon Sep 17 00:00:00 2001 From: Alex Prokop Date: Fri, 28 Apr 2023 17:11:53 +0100 Subject: [PATCH 1/8] separate out transformations as hooks and export from library --- src/Transform2d.tsx | 27 ++++++++++++++++++++++++--- src/Transform3d.tsx | 29 ++++++++++++++++++++++++++--- src/index.ts | 5 +++-- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/Transform2d.tsx b/src/Transform2d.tsx index d2bc273..8034896 100644 --- a/src/Transform2d.tsx +++ b/src/Transform2d.tsx @@ -73,14 +73,13 @@ export function apply2dTransforms({ } } -const Transform2d = ({ - children, +export function use2dTransformations({ parentMatrixWorld, translate, scale, rotate, multiplicationOrder = 'POST', -}: Transform2dProps) => { +}: Omit) { const safeParentMatrixWorld = useFactoryRef( () => parentMatrixWorld || mat2d.create(), ); @@ -101,6 +100,28 @@ const Transform2d = ({ rotate, }); + return { + matrix, + matrixWorld, + }; +} + +const Transform2d = ({ + children, + parentMatrixWorld, + translate, + scale, + rotate, + multiplicationOrder = 'POST', +}: Transform2dProps) => { + const { matrixWorld } = use2dTransformations({ + parentMatrixWorld, + translate, + scale, + rotate, + multiplicationOrder, + }); + const render = useRender({ cssMatrixPrefix: 'matrix', matrixWorld, diff --git a/src/Transform3d.tsx b/src/Transform3d.tsx index 852dab3..4975f4c 100644 --- a/src/Transform3d.tsx +++ b/src/Transform3d.tsx @@ -80,15 +80,14 @@ export function apply3dTransforms({ } } -const Transform3d = ({ - children, +export function use3dTransformations({ parentMatrixWorld, translate, scale, rotate, rotateAxis, multiplicationOrder = 'POST', -}: Transform3dProps) => { +}: Omit) { const safeParentMatrixWorld = useFactoryRef( () => parentMatrixWorld || mat4.create(), ); @@ -112,6 +111,30 @@ const Transform3d = ({ rotateAxis, }); + return { + matrix, + matrixWorld, + }; +} + +const Transform3d = ({ + children, + parentMatrixWorld, + translate, + scale, + rotate, + rotateAxis, + multiplicationOrder = 'POST', +}: Transform3dProps) => { + const { matrixWorld } = use3dTransformations({ + parentMatrixWorld, + translate, + scale, + rotate, + rotateAxis, + multiplicationOrder, + }); + const render = useRender({ cssMatrixPrefix: 'matrix3d', matrixWorld, diff --git a/src/index.ts b/src/index.ts index fc5b662..c21e44e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ export * from './propTypes'; export { useFactoryRef } from './useFactoryRef'; -export { default as Transform2d } from './Transform2d'; -export { default as Transform3d } from './Transform3d'; +export { default as Transform2d, use2dTransformations } from './Transform2d'; +export { default as Transform3d, use3dTransformations } from './Transform3d'; export type { Transform2dProps } from './Transform2d'; export type { Transform3dProps } from './Transform3d'; +export type { GLMatrixType, Vec2Object, Vec3Object } from './types'; From 266629b8a24057041a5acd67b13fe3a637508774 Mon Sep 17 00:00:00 2001 From: Alex Prokop Date: Fri, 28 Apr 2023 17:13:56 +0100 Subject: [PATCH 2/8] add snapshot --- test/__snapshots__/index.test.ts.snap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/__snapshots__/index.test.ts.snap b/test/__snapshots__/index.test.ts.snap index 77a2d47..afc7e6c 100644 --- a/test/__snapshots__/index.test.ts.snap +++ b/test/__snapshots__/index.test.ts.snap @@ -7,6 +7,8 @@ exports[`index export modules for react-css-transform 1`] = ` "mat2dGlMatrix": [Function], "mat4GlMatrix": [Function], "mat4GlMatrixValidator": [Function], + "use2dTransformations": [Function], + "use3dTransformations": [Function], "useFactoryRef": [Function], "vec2GlMatrix": [Function], "vec2Obj": [Function], From ef5a22b5b5a72dbf46da0396992c9d392f04c45d Mon Sep 17 00:00:00 2001 From: Alex Prokop Date: Fri, 28 Apr 2023 17:17:06 +0100 Subject: [PATCH 3/8] 2.1.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 107b1ed..b978b4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-css-transform", - "version": "2.0.1", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "react-css-transform", - "version": "2.0.1", + "version": "2.1.0", "license": "ISC", "devDependencies": { "@rollup/plugin-alias": "^5.0.0", diff --git a/package.json b/package.json index a2bd369..2734731 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-css-transform", - "version": "2.0.1", + "version": "2.1.0", "description": "A React component to help handle complex nested 2d and 3d css transformations ", "keywords": [ "React", From bf2fea7275a416beccc66bdab2054bfcfb2f25c6 Mon Sep 17 00:00:00 2001 From: Alex Prokop Date: Sun, 30 Apr 2023 11:47:46 +0100 Subject: [PATCH 4/8] * expose more internal utilities from the library * improve cubes example with reusable useFlatShading hook --- examples/3d-cubes/src/components/Cube.tsx | 68 +++++----- examples/3d-cubes/src/components/Face.tsx | 51 +++++++ .../3d-cubes/src/components/FlatMaterial.tsx | 128 ------------------ .../3d-cubes/src/components/GlobalContext.tsx | 69 ++++++++++ examples/3d-cubes/src/index.tsx | 19 +-- examples/3d-cubes/src/useFlatShading.ts | 86 ++++++++++++ examples/3d-cubes/src/utils.ts | 69 ++++++++++ src/index.ts | 7 + src/useRender.tsx | 8 +- src/utils.ts | 35 +++++ test/__snapshots__/index.test.ts.snap | 5 + 11 files changed, 373 insertions(+), 172 deletions(-) create mode 100644 examples/3d-cubes/src/components/Face.tsx delete mode 100644 examples/3d-cubes/src/components/FlatMaterial.tsx create mode 100644 examples/3d-cubes/src/components/GlobalContext.tsx create mode 100644 examples/3d-cubes/src/useFlatShading.ts create mode 100644 examples/3d-cubes/src/utils.ts diff --git a/examples/3d-cubes/src/components/Cube.tsx b/examples/3d-cubes/src/components/Cube.tsx index e28c48e..2cbb99a 100644 --- a/examples/3d-cubes/src/components/Cube.tsx +++ b/examples/3d-cubes/src/components/Cube.tsx @@ -6,7 +6,7 @@ import { Transform3dProps, useFactoryRef, } from 'react-css-transform'; -import { FlatMaterial } from './FlatMaterial'; +import { Face } from './Face'; import { halfPi, yAxis, xAxis } from '../constants'; const propTypes = { @@ -46,36 +46,42 @@ export const Cube = ({ faceSize, color, ...otherProps }: CubeProps) => { return ( - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - + + + + + + ); }; diff --git a/examples/3d-cubes/src/components/Face.tsx b/examples/3d-cubes/src/components/Face.tsx new file mode 100644 index 0000000..6be5ff5 --- /dev/null +++ b/examples/3d-cubes/src/components/Face.tsx @@ -0,0 +1,51 @@ +import { use3dTransformations } from 'react-css-transform'; +import { useGlobalContext } from './GlobalContext'; +import { useFlatShading } from '../useFlatShading'; +import { vec3 } from 'gl-matrix'; +import { useMemo } from 'react'; +import { blendHex, mergeMatrixWithStyles } from '../utils'; + +import type { CSSProperties } from 'react'; +import type { Transform3dProps } from 'react-css-transform'; + +type FaceProps = Transform3dProps & { + color: string; + style: CSSProperties; +}; + +// local position is always 0, outer transform will apply positioning +const objectLocalPosition = vec3.fromValues(0, 0, 0); +// local normal is always z: 1, outer transform will apply positioning +const objectLocalNormal = vec3.fromValues(0, 0, 1); + +export const Face = ({ color, style, ...transform3dProps }: FaceProps) => { + const { matrixWorld } = use3dTransformations(transform3dProps); + + const { lightPosition, lightColor } = useGlobalContext(); + + const { diffuseColor, emissiveColor } = useMemo(() => { + return { + diffuseColor: blendHex(color, '#ffffff', 0.1), + emissiveColor: blendHex(color, '#000000', 0.333), + }; + }, [color]); + + const { outputColorString } = useFlatShading({ + diffuseColor, + emissiveColor, + lightColor, + objectLocalPosition, + objectLocalNormal, + lightWorldPosition: lightPosition, + parentMatrixWorld: matrixWorld.current, + }); + + const faceStyle = useMemo(() => { + return mergeMatrixWithStyles(matrixWorld.current, { + ...style, + backgroundColor: outputColorString, + }); + }, [matrixWorld, style, outputColorString]); + + return
; +}; diff --git a/examples/3d-cubes/src/components/FlatMaterial.tsx b/examples/3d-cubes/src/components/FlatMaterial.tsx deleted file mode 100644 index 8ae5f5e..0000000 --- a/examples/3d-cubes/src/components/FlatMaterial.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { cloneElement, useMemo } from 'react'; -import PropTypes from 'prop-types'; -import { - mat4GlMatrix, - Transform3dProps, - useFactoryRef, -} from 'react-css-transform'; -import { vec3, mat3 } from 'gl-matrix'; -import { useDimensionsContext } from "./DimensionsContext"; - -function hexStringToRgb(hexString: string) { - let parsedHexString; - - if (hexString.length === 4) { - parsedHexString = - hexString[1] + - hexString[1] + - hexString[2] + - hexString[2] + - hexString[3] + - hexString[3]; - } else { - parsedHexString = hexString.substring(1); - } - - return [ - parseInt(parsedHexString[0] + parsedHexString[1], 16), - parseInt(parsedHexString[2] + parsedHexString[3], 16), - parseInt(parsedHexString[4] + hexString[5], 16), - ]; -} - -function blendRgbArrayColors( - color1: number[], - color2: number[], - percentage: number, -) { - return [ - (1 - percentage) * color1[0] + percentage * color2[0], - (1 - percentage) * color1[1] + percentage * color2[1], - (1 - percentage) * color1[2] + percentage * color2[2], - ]; -} - -function rgbArrayToString(rgb: number[]) { - // safari won't process floats in rgb - return `rgb(${rgb.map((val) => Math.round(val)).join(',')})`; -} - -function clamp(val: number, min: number, max: number) { - return Math.min(Math.max(val, min), max); -} - -const propTypes = { - color: PropTypes.string.isRequired, - parentMatrixWorld: mat4GlMatrix, - style: PropTypes.object, - children: PropTypes.node.isRequired, -}; - -type FlatMaterialProps = Transform3dProps & { - color: string; - children: React.ReactNode; - style?: React.CSSProperties; -}; - -export const FlatMaterial = ({ - color, - children, - parentMatrixWorld, - style, -}: FlatMaterialProps) => { - const { width, height } = useDimensionsContext(); - - const position = useFactoryRef(() => vec3.create()); - const normal = useFactoryRef(() => vec3.fromValues(0, 0, 1)); - - const positionToLight = useFactoryRef(() => vec3.create()); - const worldNormal = useFactoryRef(() => vec3.create()); - - const normalMatrix = useFactoryRef(() => mat3.create()); - - const lightPos = useFactoryRef(() => vec3.create()); - - const { colorRgb, shadowColorRgb } = useMemo(() => { - const colorRgb = hexStringToRgb(color); - const whiteColor = blendRgbArrayColors(colorRgb, [255, 255, 255], 0.1); - - return { - colorRgb: whiteColor, - shadowColorRgb: blendRgbArrayColors(colorRgb, [0, 0, 0], 0.333), - }; - }, [color]); - - vec3.set(lightPos.current, width / 3, height / 3, 0); - - mat3.normalFromMat4(normalMatrix.current, parentMatrixWorld!); - vec3.transformMat3(worldNormal.current, normal.current, normalMatrix.current); - vec3.transformMat4( - positionToLight.current, - position.current, - parentMatrixWorld!, - ); - - vec3.sub(positionToLight.current, lightPos.current, positionToLight.current); - - vec3.normalize(positionToLight.current, positionToLight.current); - - const cosAngle = clamp( - vec3.dot(worldNormal.current, positionToLight.current), - 0, - 1, - ); - - const outputColor = blendRgbArrayColors(shadowColorRgb, colorRgb, cosAngle); - // @ts-ignore - allow props check whatever child type - const childStyle = children?.props?.style ?? {}; - const outputStyle = { - ...childStyle, - ...(style ?? {}), - backgroundColor: rgbArrayToString(outputColor), - }; - - // @ts-ignore - TODO: bug in react types? allow all ReactNode types to cloneElement - return cloneElement(children, { style: outputStyle }); -}; - -FlatMaterial.propTypes = propTypes; diff --git a/examples/3d-cubes/src/components/GlobalContext.tsx b/examples/3d-cubes/src/components/GlobalContext.tsx new file mode 100644 index 0000000..2105ee5 --- /dev/null +++ b/examples/3d-cubes/src/components/GlobalContext.tsx @@ -0,0 +1,69 @@ +import { createContext, useContext, useEffect, useMemo, useState } from 'react'; +import { vec3 } from 'gl-matrix'; +import type { ReactNode } from 'react'; + +type GlobalState = { + width: number; + height: number; + perspective: number; + lightPosition: vec3; + lightColor: string; +}; + +const getDefaultState = (width: number, height: number): GlobalState => ({ + width, + height, + perspective: (500 / 750) * height, + lightPosition: vec3.fromValues(width / 3, height / 3, 0), + lightColor: '#ffffff', +}); + +const updateGlobalStateDimensions = ( + width: number, + height: number, + state: GlobalState, +): GlobalState => { + return { + ...state, + width, + height, + perspective: (500 / 750) * height, + lightPosition: vec3.set(state.lightPosition, width / 3, height / 3, 0), + }; +}; + +const GlobalContext = createContext( + getDefaultState(window.innerWidth, window.innerHeight), +); + +export const useGlobalContext = () => useContext(GlobalContext); + +type DimensionsProviderProps = { + children: ReactNode; +}; +export const GlobalStateProvider = ({ children }: DimensionsProviderProps) => { + const [globalState, setGlobalState] = useState(() => + getDefaultState(window.innerWidth, window.innerHeight), + ); + + useEffect(() => { + const handleResize = () => { + setGlobalState((state) => + updateGlobalStateDimensions( + window.innerWidth, + window.innerHeight, + state, + ), + ); + }; + + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + return ( + + {children} + + ); +}; diff --git a/examples/3d-cubes/src/index.tsx b/examples/3d-cubes/src/index.tsx index 0765e02..0bcbc60 100644 --- a/examples/3d-cubes/src/index.tsx +++ b/examples/3d-cubes/src/index.tsx @@ -1,17 +1,12 @@ -import { - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { createRoot } from 'react-dom/client'; import { Transform3d, useFactoryRef } from 'react-css-transform'; import { vec3 } from 'gl-matrix'; import { CubeGroup } from './components/CubeGroup'; import { - DimensionsProvider, - useDimensionsContext, -} from './components/DimensionsContext'; + GlobalStateProvider, + useGlobalContext, +} from './components/GlobalContext'; import { yAxis, zAxis } from './constants'; import './styles.css'; @@ -19,7 +14,7 @@ import './styles.css'; const frameTime = 1000 / 60; const BaseApp = () => { - const { width, height, perspective } = useDimensionsContext(); + const { width, height, perspective } = useGlobalContext(); const appStyle = useMemo( () => ({ perspective, @@ -94,9 +89,9 @@ const BaseApp = () => { const App = () => { return ( - + - + ); }; diff --git a/examples/3d-cubes/src/useFlatShading.ts b/examples/3d-cubes/src/useFlatShading.ts new file mode 100644 index 0000000..de8ca41 --- /dev/null +++ b/examples/3d-cubes/src/useFlatShading.ts @@ -0,0 +1,86 @@ +import { useMemo } from 'react'; +import { useFactoryRef } from 'react-css-transform'; +import { vec3, mat3, mat4 } from 'gl-matrix'; +import { clamp, hexToVec3, vec3ToRgbString } from './utils'; + +type UseFlatShadingParameters = { + diffuseColor: string; + emissiveColor: string; + lightColor: string; + objectLocalPosition: vec3; + objectLocalNormal: vec3; + lightWorldPosition: vec3; + parentMatrixWorld: mat4; +}; + +const useVec3Color = (hexColor: string) => + useMemo(() => hexToVec3(hexColor), [hexColor]); + +const useRgbColor = (hexColor: string) => + useMemo(() => hexToVec3(hexColor, 1), [hexColor]); + +export const useFlatShading = ({ + diffuseColor, + emissiveColor, + lightColor, + objectLocalPosition, + objectLocalNormal, + lightWorldPosition, + parentMatrixWorld, +}: UseFlatShadingParameters) => { + const vOutputColor = useFactoryRef(() => vec3.create()); + const vObjectToLightWorldPosition = useFactoryRef(() => vec3.create()); + const vWorldNormal = useFactoryRef(() => vec3.create()); + const mNormalMatrix = useFactoryRef(() => mat3.create()); + + vec3.zero(vOutputColor.current); + vec3.zero(vObjectToLightWorldPosition.current); + vec3.zero(vWorldNormal.current); + mat3.identity(mNormalMatrix.current); + + const vDiffuseColor = useVec3Color(diffuseColor); + const vEmissiveColor = useVec3Color(emissiveColor); + const vLightColor = useVec3Color(lightColor); + + // transform local normal to world coords + mat3.normalFromMat4(mNormalMatrix.current, parentMatrixWorld); + vec3.transformMat3( + vWorldNormal.current, + objectLocalNormal, + mNormalMatrix.current, + ); + // transform local position to world coords + vec3.transformMat4( + vObjectToLightWorldPosition.current, + objectLocalPosition, + parentMatrixWorld, + ); + + // compute vector from object to light in world coords and normalize + vec3.sub( + vObjectToLightWorldPosition.current, + lightWorldPosition, + vObjectToLightWorldPosition.current, + ); + vec3.normalize( + vObjectToLightWorldPosition.current, + vObjectToLightWorldPosition.current, + ); + + // compute flat shaded color + const diffuseLight = clamp( + vec3.dot(vWorldNormal.current, vObjectToLightWorldPosition.current), + 0, + 1, + ); + + vec3.multiply(vOutputColor.current, vDiffuseColor, vLightColor); + vec3.scale(vOutputColor.current, vOutputColor.current, diffuseLight); + + vec3.add(vOutputColor.current, vOutputColor.current, vEmissiveColor); + + return { + outputColor: vOutputColor.current, + outputColorString: vec3ToRgbString(vOutputColor.current), + }; +}; diff --git a/examples/3d-cubes/src/utils.ts b/examples/3d-cubes/src/utils.ts new file mode 100644 index 0000000..cad2a2c --- /dev/null +++ b/examples/3d-cubes/src/utils.ts @@ -0,0 +1,69 @@ +import { mat4, vec3 } from 'gl-matrix'; +import { CSSProperties } from 'react'; + +export function hexToRgb(hexString: string) { + let parsedHexString; + + if (hexString.length === 4) { + parsedHexString = + hexString[1] + + hexString[1] + + hexString[2] + + hexString[2] + + hexString[3] + + hexString[3]; + } else { + parsedHexString = hexString.substring(1); + } + + return [ + parseInt(parsedHexString[0] + parsedHexString[1], 16), + parseInt(parsedHexString[2] + parsedHexString[3], 16), + parseInt(parsedHexString[4] + hexString[5], 16), + ]; +} + +export function rgbToHex(r: number, g: number, b: number) { + return '#' + ((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1); +} + +const NORMALIZE_RGB_FACTOR = 1 / 255; + +export function hexToVec3( + hexString: string, + scale: number = NORMALIZE_RGB_FACTOR, +) { + const [r, g, b] = hexToRgb(hexString); + const v = vec3.fromValues(r, g, b); + return vec3.scale(v, v, scale); +} + +export function blendRgb( + color1: number[], + color2: number[], + percentage: number, +) { + return [ + (1 - percentage) * color1[0] + percentage * color2[0], + (1 - percentage) * color1[1] + percentage * color2[1], + (1 - percentage) * color1[2] + percentage * color2[2], + ]; +} + +export function blendHex(color1: string, color2: string, percentage: number) { + const color1Rgb = hexToRgb(color1); + const color2Rgb = hexToRgb(color2); + const resultRgb = blendRgb(color1Rgb, color2Rgb, percentage); + return rgbToHex( + ...(resultRgb.map((v) => Math.round(v)) as [number, number, number]), + ); +} + +export function vec3ToRgbString(rgb: vec3) { + // safari won't process floats in rgb + return `rgb(${rgb.map((val) => Math.round(val * 255)).join(',')})`; +} + +export function clamp(val: number, min: number, max: number) { + return Math.min(Math.max(val, min), max); +} diff --git a/src/index.ts b/src/index.ts index c21e44e..3b5f23f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,12 @@ export * from './propTypes'; export { useFactoryRef } from './useFactoryRef'; +export { + get2dCSSMatrixString, + get3dCSSMatrixString, + getCSSMatrixString, + merge2dMatrixWithStyles, + merge3dMatrixWithStyles, +} from './utils'; export { default as Transform2d, use2dTransformations } from './Transform2d'; export { default as Transform3d, use3dTransformations } from './Transform3d'; export type { Transform2dProps } from './Transform2d'; diff --git a/src/useRender.tsx b/src/useRender.tsx index fa7f37f..c134320 100644 --- a/src/useRender.tsx +++ b/src/useRender.tsx @@ -7,6 +7,7 @@ import type { TransformChildProps, TransformChildren, } from './types'; +import { getCSSMatrixString } from './utils'; type UseRenderChildParams = { matrixString: string; @@ -57,8 +58,13 @@ export const useRender = ({ matrixWorld, multiplicationOrder, }: UseRenderParams) => { - const matrixString = `${cssMatrixPrefix}(${matrixWorld.current?.join(',')})`; + const matrixString = getCSSMatrixString( + cssMatrixPrefix, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + matrixWorld.current!, + ); + // TODO: profile whether the useCallbacks in useRenderChild and below are actually a perf hindrance... const renderChild = useRenderChild({ matrixWorld, matrixString, diff --git a/src/utils.ts b/src/utils.ts index 2122a8e..7c2a89d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,10 +1,12 @@ import { vec2, vec3, glMatrix, mat2d, mat4 } from 'gl-matrix'; +import type { CSSProperties } from 'react'; import type { GLMatrixType, Vec2Object, Vec3Object, Vec2SettableProperty, Vec3SettableProperty, + CSSMatrixPrefix, } from './types'; export const isNil = (val: any) => val === null || val === undefined; @@ -105,3 +107,36 @@ export function setVec3FromProp( return vec3.set(v, x, y, z); } + +export const getCSSMatrixString = ( + matrixPrefix: CSSMatrixPrefix, + matrix: Matrix, +): string => { + return `${matrixPrefix}(${matrix.join(',')})`; +}; + +export const get2dCSSMatrixString = (matrix: mat2d): string => + getCSSMatrixString('matrix', matrix); + +export const get3dCSSMatrixString = (matrix: mat4): string => + getCSSMatrixString('matrix3d', matrix); + +export function merge2dMatrixWithStyles( + matrix: mat2d, + styles: CSSProperties, +): CSSProperties { + return { + ...styles, + transform: get2dCSSMatrixString(matrix), + }; +} + +export function merge3dMatrixWithStyles( + matrix: mat4, + styles: CSSProperties, +): CSSProperties { + return { + ...styles, + transform: get3dCSSMatrixString(matrix), + }; +} diff --git a/test/__snapshots__/index.test.ts.snap b/test/__snapshots__/index.test.ts.snap index afc7e6c..3a85eab 100644 --- a/test/__snapshots__/index.test.ts.snap +++ b/test/__snapshots__/index.test.ts.snap @@ -4,9 +4,14 @@ exports[`index export modules for react-css-transform 1`] = ` { "Transform2d": [Function], "Transform3d": [Function], + "get2dCSSMatrixString": [Function], + "get3dCSSMatrixString": [Function], + "getCSSMatrixString": [Function], "mat2dGlMatrix": [Function], "mat4GlMatrix": [Function], "mat4GlMatrixValidator": [Function], + "merge2dMatrixWithStyles": [Function], + "merge3dMatrixWithStyles": [Function], "use2dTransformations": [Function], "use3dTransformations": [Function], "useFactoryRef": [Function], From c9504fd0fc1de5029fb16a8a2acdc7643b8ea53a Mon Sep 17 00:00:00 2001 From: Alex Prokop Date: Sun, 30 Apr 2023 11:50:24 +0100 Subject: [PATCH 5/8] fix example import --- examples/3d-cubes/src/components/Face.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/3d-cubes/src/components/Face.tsx b/examples/3d-cubes/src/components/Face.tsx index 6be5ff5..aa15f31 100644 --- a/examples/3d-cubes/src/components/Face.tsx +++ b/examples/3d-cubes/src/components/Face.tsx @@ -1,9 +1,9 @@ -import { use3dTransformations } from 'react-css-transform'; +import { merge3dMatrixWithStyles, use3dTransformations } from 'react-css-transform'; import { useGlobalContext } from './GlobalContext'; import { useFlatShading } from '../useFlatShading'; import { vec3 } from 'gl-matrix'; import { useMemo } from 'react'; -import { blendHex, mergeMatrixWithStyles } from '../utils'; +import { blendHex } from '../utils'; import type { CSSProperties } from 'react'; import type { Transform3dProps } from 'react-css-transform'; @@ -41,7 +41,7 @@ export const Face = ({ color, style, ...transform3dProps }: FaceProps) => { }); const faceStyle = useMemo(() => { - return mergeMatrixWithStyles(matrixWorld.current, { + return merge3dMatrixWithStyles(matrixWorld.current, { ...style, backgroundColor: outputColorString, }); From 6aee6630153ffac5dfa6c159eaac59e87effde19 Mon Sep 17 00:00:00 2001 From: Alex Prokop Date: Sun, 30 Apr 2023 12:27:45 +0100 Subject: [PATCH 6/8] tweak light position --- .../src/components/DimensionsContext.tsx | 50 ------------------- .../3d-cubes/src/components/GlobalContext.tsx | 15 ++++-- examples/3d-cubes/src/index.tsx | 4 +- 3 files changed, 12 insertions(+), 57 deletions(-) delete mode 100644 examples/3d-cubes/src/components/DimensionsContext.tsx diff --git a/examples/3d-cubes/src/components/DimensionsContext.tsx b/examples/3d-cubes/src/components/DimensionsContext.tsx deleted file mode 100644 index 562ee27..0000000 --- a/examples/3d-cubes/src/components/DimensionsContext.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { createContext, useContext, useEffect, useMemo, useState } from 'react'; -import type { ReactNode } from 'react'; - -type DimensionsState = { - width: number; - height: number; - perspective: number; -}; - -const getDimensionsState = ( - width: number, - height: number, -): DimensionsState => ({ - width, - height, - perspective: (500 / 750) * height, -}); - -const DimensionsContext = createContext( - getDimensionsState(window.innerWidth, window.innerHeight), -); - -export const useDimensionsContext = () => - useContext(DimensionsContext); - -type DimensionsProviderProps = { - children: ReactNode; -}; -export const DimensionsProvider = ({ children }: DimensionsProviderProps) => { - const [dimensionsState, setDimensionsState] = useState(() => - getDimensionsState(window.innerWidth, window.innerHeight), - ); - - useEffect(() => { - const handleResize = () => { - setDimensionsState( - getDimensionsState(window.innerWidth, window.innerHeight), - ); - }; - - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, []); - - return ( - - {children} - - ); -}; diff --git a/examples/3d-cubes/src/components/GlobalContext.tsx b/examples/3d-cubes/src/components/GlobalContext.tsx index 2105ee5..f41fa08 100644 --- a/examples/3d-cubes/src/components/GlobalContext.tsx +++ b/examples/3d-cubes/src/components/GlobalContext.tsx @@ -1,4 +1,4 @@ -import { createContext, useContext, useEffect, useMemo, useState } from 'react'; +import { createContext, useContext, useEffect, useState } from 'react'; import { vec3 } from 'gl-matrix'; import type { ReactNode } from 'react'; @@ -10,11 +10,16 @@ type GlobalState = { lightColor: string; }; +const updatePerspective = (height: number) => (500 / 750) * height; + +const updateLightPosition = (width: number, height: number, v: vec3) => + vec3.set(v, width / 4, height / 4, 100); + const getDefaultState = (width: number, height: number): GlobalState => ({ width, height, - perspective: (500 / 750) * height, - lightPosition: vec3.fromValues(width / 3, height / 3, 0), + perspective: updatePerspective(height), + lightPosition: updateLightPosition(width, height, vec3.create()), lightColor: '#ffffff', }); @@ -27,8 +32,8 @@ const updateGlobalStateDimensions = ( ...state, width, height, - perspective: (500 / 750) * height, - lightPosition: vec3.set(state.lightPosition, width / 3, height / 3, 0), + perspective: updatePerspective(height), + lightPosition: updateLightPosition(width, height, state.lightPosition), }; }; diff --git a/examples/3d-cubes/src/index.tsx b/examples/3d-cubes/src/index.tsx index 0bcbc60..47c48f1 100644 --- a/examples/3d-cubes/src/index.tsx +++ b/examples/3d-cubes/src/index.tsx @@ -22,6 +22,8 @@ const BaseApp = () => { [perspective], ); + const [time, setTime] = useState(() => performance.now()); + const [playing, setPlaying] = useState(true); const togglePlaying = useCallback(() => { setPlaying((isPlaying) => !isPlaying); @@ -51,8 +53,6 @@ const BaseApp = () => { requestAnimationFrame(update); }, [playing]); - const [time, setTime] = useState(() => performance.now()); - const translateToCentre = useFactoryRef(() => vec3.create()); const cubeGroup1Translate = useFactoryRef(() => vec3.create()); const cubeGroup2Translate = useFactoryRef(() => vec3.create()); From 88b4e15b309416a00bf0bc6961b6bb442d5101c3 Mon Sep 17 00:00:00 2001 From: Alex Prokop Date: Sun, 30 Apr 2023 12:29:21 +0100 Subject: [PATCH 7/8] tweak shading params --- examples/3d-cubes/src/components/Face.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/3d-cubes/src/components/Face.tsx b/examples/3d-cubes/src/components/Face.tsx index aa15f31..fcde40d 100644 --- a/examples/3d-cubes/src/components/Face.tsx +++ b/examples/3d-cubes/src/components/Face.tsx @@ -26,7 +26,7 @@ export const Face = ({ color, style, ...transform3dProps }: FaceProps) => { const { diffuseColor, emissiveColor } = useMemo(() => { return { diffuseColor: blendHex(color, '#ffffff', 0.1), - emissiveColor: blendHex(color, '#000000', 0.333), + emissiveColor: blendHex(color, '#000000', 0.5), }; }, [color]); From 1aad5762f23e076316e4ace9d43f038e07ce9177 Mon Sep 17 00:00:00 2001 From: Alex Prokop Date: Sun, 30 Apr 2023 12:31:48 +0100 Subject: [PATCH 8/8] 2.1.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b978b4d..f847dc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-css-transform", - "version": "2.1.0", + "version": "2.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "react-css-transform", - "version": "2.1.0", + "version": "2.1.1", "license": "ISC", "devDependencies": { "@rollup/plugin-alias": "^5.0.0", diff --git a/package.json b/package.json index 2734731..aec5d0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-css-transform", - "version": "2.1.0", + "version": "2.1.1", "description": "A React component to help handle complex nested 2d and 3d css transformations ", "keywords": [ "React",