Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Svg component #1091

Merged
merged 11 commits into from Oct 17, 2022
25 changes: 25 additions & 0 deletions .storybook/stories/Svg.stories.tsx
@@ -0,0 +1,25 @@
import { ComponentMeta, ComponentStory } from '@storybook/react'
import * as React from 'react'
import { Suspense } from 'react'
import { Vector3 } from 'three'
import { Svg } from '../../src'
import { Setup } from '../Setup'

export default {
title: 'Abstractions/Svg',
component: Svg,
decorators: [(storyFn) => <Setup cameraPosition={new Vector3(0, 0, 200)}>{storyFn()}</Setup>],
args: {
src: 'https://threejs.org/examples/models/svg/tiger.svg',
skipFill: false,
skipStrokes: false,
strokesWireframe: false,
fillWireframe: false,
},
} as ComponentMeta<typeof Svg>

export const SvgSt: ComponentStory<typeof Svg> = (props) => (
<Suspense fallback={null}>
<Svg {...props} position={[-300, 300, -300]} />
</Suspense>
)
13 changes: 13 additions & 0 deletions README.md
Expand Up @@ -75,6 +75,7 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
<li><a href="#useanimations">useAnimations</a></li>
<li><a href="#marchingcubes">MarchingCubes</a></li>
<li><a href="#decal">Decal</a></li>
<li><a href="#svg">Svg</a></li>
</ul>
<li><a href="#shaders">Shaders</a></li>
<ul>
Expand Down Expand Up @@ -1165,6 +1166,18 @@ If declarative composition is not possible, use the `mesh` prop to define the su
</Decal>
```

#### Svg

[![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/abstractions-svg--svg-st)

Wrapper around the `three` [svg loader](https://threejs.org/examples/?q=sv#webgl_loader_svg) demo.

Accepts an SVG url or svg raw data.

```js
<Svg src={urlOrRawSvgString} />
```

# Shaders

#### MeshReflectorMaterial
Expand Down
100 changes: 100 additions & 0 deletions src/core/Svg.tsx
@@ -0,0 +1,100 @@
import { Object3DProps, useLoader } from '@react-three/fiber'
import * as React from 'react'
import { forwardRef, Fragment, useMemo } from 'react'
import { Color, DoubleSide, MeshBasicMaterial, Object3D } from 'three'
import { SVGLoader } from 'three-stdlib'

export interface SvgProps extends Object3DProps {
/** src can be a URL or SVG data */
src: string
skipFill?: boolean
skipStrokes?: boolean
strokesWireframe?: boolean
fillWireframe?: boolean
}

export const Svg = forwardRef<Object3D, SvgProps>(function R3FSvg(
{ src, skipFill, skipStrokes, strokesWireframe, fillWireframe, ...props },
ref
) {
const svg = useLoader(SVGLoader, !src.startsWith('<svg') ? src : `data:image/svg+xml;utf8,${src}`)

const shapeGroups = useMemo(
() => (skipFill ? [] : svg.paths.map((path) => SVGLoader.createShapes(path))),
[svg, skipFill]
)

const fillMaterials = useMemo(
() =>
skipFill
? []
: svg.paths.map(
(path) =>
new MeshBasicMaterial({
color: new Color()
.setStyle(path.userData!.style.fill === 'none' ? '#000000' : path.userData!.style.fill)
.convertSRGBToLinear(),
opacity: path.userData!.style.fillOpacity,
transparent: true,
side: DoubleSide,
depthWrite: false,
wireframe: fillWireframe,
})
CodyJasonBennett marked this conversation as resolved.
Show resolved Hide resolved
),
[svg, skipFill, fillWireframe]
)

const strokeMaterials = useMemo(
() =>
skipStrokes
? []
: svg.paths.map(
(path) =>
new MeshBasicMaterial({
color: new Color()
.setStyle(path.userData!.style.stroke === 'none' ? '#000000' : path.userData!.style.stroke)
.convertSRGBToLinear(),
opacity: path.userData!.style.strokeOpacity,
transparent: true,
side: DoubleSide,
depthWrite: false,
wireframe: strokesWireframe,
})
),
[svg, skipStrokes, strokesWireframe]
)

const strokeGeometryGroups = useMemo(
() =>
svg.paths.map((path) =>
skipStrokes
? []
: path.subPaths.map((subPath) => SVGLoader.pointsToStroke(subPath.getPoints(), path.userData!.style))
),
[svg, skipStrokes]
)

return (
<object3D ref={ref} {...props}>
<object3D scale={[1, -1, 1]}>
{strokeGeometryGroups.map((geometries, i) =>
geometries.map((geometry, x) =>
!geometry ? (
<Fragment key={`${i}.${x}`} />
) : (
<mesh key={`${i}.${x}`} geometry={geometry} material={strokeMaterials[i]} />
)
)
)}

{shapeGroups.map((shapes, x) =>
shapes.map((shape, y) => (
<mesh key={`${x}.${y}`} material={fillMaterials[x]}>
<shapeGeometry args={[shape]} />
</mesh>
))
)}
</object3D>
</object3D>
)
})
1 change: 1 addition & 0 deletions src/core/index.ts
Expand Up @@ -17,6 +17,7 @@ export * from './ComputedAttribute'
export * from './Clone'
export * from './MarchingCubes'
export * from './Decal'
export * from './Svg'

// Cameras
export * from './OrthographicCamera'
Expand Down