Skip to content

Commit

Permalink
feat: SVG
Browse files Browse the repository at this point in the history
  • Loading branch information
andretchen0 committed Aug 28, 2023
1 parent 0f5d9cf commit 28e3b6a
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 2 deletions.
1 change: 1 addition & 0 deletions playground/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ declare module 'vue' {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
ScrollDemo: typeof import('./src/components/ScrollDemo.vue')['default']
SVGDemo: typeof import('./src/components/SVGDemo.vue')['default']
TheGizmos: typeof import('./src/components/TheGizmos.vue')['default']
UseVideoTextureDemo: typeof import('./src/components/useVideoTextureDemo.vue')['default']
WobbleMaterial: typeof import('./src/components/WobbleMaterial.vue')['default']
Expand Down
66 changes: 66 additions & 0 deletions playground/src/pages/SVGDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script setup lang="ts">
import { TresCanvas, useRenderLoop } from '@tresjs/core'
import { SVG, OrbitControls } from '@tresjs/cientos'
import { NoToneMapping } from 'three'
import { shallowRef } from 'vue'
const svgTriangleString = `<svg width="404" height="80" viewBox="0 0 404 80" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M44.5703 5.71662C46.124 3.12726 49.8767 3.12726 51.4303 5.71662L92.3655 73.942C93.9652
76.6081 92.0447 80 88.9355 80H7.06507C3.95589 80 2.03544 76.6081 3.6351 73.942L44.5703 5.71662Z"
fill="rgb(130,219,197)" stroke="rgb(130,219,197)" />
</svg>`;
const svgSquareString = `<svg width="404" height="80" viewBox="0 0 404 80"
fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="172" y="2.64999" width="74.7" height="74.7" rx="4" fill="rgb(79,79,79)" stroke="rgb(79,79,79)" />
</svg>`;
const svgHeartURL = "https://raw.githubusercontent.com/andretchen0/tresjs_assets/feat/svg/svg/cientos.svg";
const gl = {
clearColor: '#333',
alpha: true,
toneMapping: NoToneMapping,
}
const { onLoop } = useRenderLoop()
const skipFillsA = shallowRef(false);
const skipFillsB = shallowRef(true);
const skipFillsC = shallowRef(false);
let cooldown = 0;
onLoop(({delta}) => {
cooldown -= delta;
while (cooldown <= 0) {
const skipFillsTmp = skipFillsA.value;
skipFillsA.value = skipFillsC.value;
skipFillsC.value = skipFillsB.value;
skipFillsB.value = skipFillsTmp;
cooldown += 1;
}
});
</script>

<template>
<TresCanvas v-bind="gl" ref="canvas">
<TresPerspectiveCamera :position="[0, 2, 10]" />
<TresGridHelper :args="[10, 10]" />
<TresGroup :scale="0.01" :position="[-2.1, 1, 0]">
<Suspense>
<SVG :src="svgTriangleString" :skip-fills="skipFillsA" />
</Suspense>
<Suspense>
<SVG :src="svgSquareString" :skip-fills="skipFillsB" />
</Suspense>
<Suspense>
<SVG :src="svgHeartURL" :skip-fills="skipFillsC" :position="[321.5, -4, 0]" />
</Suspense>
</TresGroup>
<TresAmbientLight />
<TresDirectionalLight />
<OrbitControls />
</TresCanvas>
</template>
2 changes: 1 addition & 1 deletion playground/src/pages/index.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<script setup lang="ts"></script>
<script setup lang="ts"> </script>
<template>
<Suspense>
<GlassMaterialDemo />
Expand Down
5 changes: 5 additions & 0 deletions playground/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ const routes = [
name: 'MouseParallax',
component: () => import('./pages/MouseParallaxDemo.vue'),
},
{
path: '/loaders/svg',
name: 'SVG',
component: () => import('./pages/SVGDemo.vue'),
},
{
path: '/loaders/use-gltf',
name: 'useGLTF',
Expand Down
159 changes: 159 additions & 0 deletions src/core/loaders/SVG/component.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<script setup lang="ts">
import { shallowRef, toRefs, watch, watchEffect } from 'vue'
import { TresOptions, useLoader } from '@tresjs/core'
import { SVGLoader, SVGResultPaths } from 'three-stdlib'
import { Vector3, DoubleSide, ShapeGeometry, MeshBasicMaterialParameters, BufferGeometry } from 'three'
interface SVGProps {
/**
*
* The SVG data or path to an SVG file
*
* @type {string}
* @required
* @memberof SVGProps
*
**/
src: string
/**
*
* Whether to draw strokes
*
* @type {boolean}
* @default false
* @memberof SVGProps
*
**/
skipStrokes?: boolean
/**
*
* Whether to draw fills
*
* @type {boolean}
* @default false
* @memberof SVGProps
*
**/
skipFills?: boolean
/**
*
* Fill material properties
*
* @type {MeshBasicMaterialParameters}
* @default {}
* @memberof SVGProps
*
**/
fillMaterial?: MeshBasicMaterialParameters
/**
*
* Stroke material properties
*
* @type {MeshBasicMaterialParameters}
* @default {}
* @memberof SVGProps
*
**/
strokeMaterial?: MeshBasicMaterialParameters
/**
*
* Fill Mesh properties
*
* @type {TresOptions}
* @default {}
* @memberof SVGProps
*
**/
fillMeshProps?: TresOptions
/**
*
* Stroke Mesh properties
*
* @type {TresOptions}
* @default {}
* @memberof SVGProps
*
**/
strokeMeshProps?: TresOptions
}
const props = withDefaults(defineProps<SVGProps>(),
{ skipStrokes: false, skipFills: false }
);
type SVGLayer = { geometry: BufferGeometry, material: MeshBasicMaterialParameters, key: string, isStroke: boolean };
const { src, skipStrokes, skipFills, fillMaterial, strokeMaterial, fillMeshProps, strokeMeshProps } = toRefs(props);
const groupRef = shallowRef();
const layers = shallowRef([] as SVGLayer[]);
const paths = shallowRef([] as SVGResultPaths[]);
defineExpose({ value: groupRef });
watchEffect(async() => useSVG(src.value).then(SVGResult => paths.value = SVGResult.paths));
watch([skipFills, skipStrokes, fillMaterial, strokeMaterial, paths], updateLayers);
async function useSVG(src: string) {
const srcStr = !src.startsWith('<svg') ? src : encodeURI(`data:image/svg+xml;utf8,${src}`);
return useLoader(SVGLoader, srcStr);
};
function updateLayers() {
const _layers = [];
let i = 0;
for (const path of paths.value) {
const style = path.userData?.style ?? {};
const fillMaterial = (Object.assign({
color: style.fill,
opacity: style.fillOpacity,
transparent: true,
side: DoubleSide,
depthWrite: false
},
props.fillMaterial));
if (!skipFills.value && style.fill !== undefined && style.fill !== 'none') {
for (const shape of SVGLoader.createShapes(path)) {
const geometry = new ShapeGeometry(shape);
geometry.scale(1, -1, 1);
_layers.push({
geometry,
material: fillMaterial,
isStroke: false,
key: '' + i++
})
}
}
if (!skipStrokes.value && style.stroke !== undefined && style.stroke !== 'none') {
const material = (Object.assign({
color: path.userData?.style.stroke,
opacity: path.userData?.style.strokeOpacity,
transparent: true,
side: DoubleSide, depthWrite: false
},
props.strokeMaterial));
for (const subPath of path.subPaths) {
const points = subPath.getPoints().map(v2 => new Vector3(v2.x, -v2.y, 0));
const geometry = SVGLoader.pointsToStroke(points, style || 'none');
_layers.push({
geometry,
material,
isStroke: true,
key: '' + i++
})
}
}
}
layers.value = _layers;
}
</script>

<template>
<TresGroup v-bind="$attrs" ref="groupRef">
<TresMesh v-for="{ geometry, material, isStroke, key } of layers" :key="key"
v-bind="isStroke ? strokeMeshProps : fillMeshProps" :geometry="geometry">
<TresMeshBasicMaterial v-bind="material" />
</TresMesh>
</TresGroup>
</template>
3 changes: 2 additions & 1 deletion src/core/loaders/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import GLTFModel from './useGLTF/component.vue'
import FBXModel from './useFBX/component.vue'
import SVG from './SVG/component.vue'
export * from './useGLTF'
export * from './useFBX'
import { useProgress } from './useProgress'
import { useVideoTexture } from './useVideoTexture'

export { FBXModel, GLTFModel, useProgress, useVideoTexture }
export { FBXModel, GLTFModel, SVG, useProgress, useVideoTexture }

0 comments on commit 28e3b6a

Please sign in to comment.