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",