|
| 1 | +'use client'; |
| 2 | + |
| 3 | +import { BackButton } from '@/components/back-button'; |
| 4 | +import { cleanUpLevaParams } from '@/helpers/clean-up-leva-params'; |
| 5 | +import { toHsla } from '@/helpers/to-hsla'; |
| 6 | +import { usePresetHighlight } from '@/helpers/use-preset-highlight'; |
| 7 | +import { setParamsSafe, useResetLevaParams } from '@/helpers/use-reset-leva-params'; |
| 8 | +import { type ShaderFit, ShaderFitOptions, tartanMeta } from '@paper-design/shaders'; |
| 9 | +import { Tartan, tartanPresets } from '@paper-design/shaders-react'; |
| 10 | +import { button, folder, useControls } from 'leva'; |
| 11 | +import Link from 'next/link'; |
| 12 | + |
| 13 | +/** |
| 14 | + * You can copy/paste this example to use Tartan in your app |
| 15 | + */ |
| 16 | +const TartanExample = () => { |
| 17 | + return <Tartan style={{ position: 'fixed', width: '100%', height: '100%' }} />; |
| 18 | +}; |
| 19 | + |
| 20 | +/** |
| 21 | + * This example has controls added so you can play with settings in the example app |
| 22 | + */ |
| 23 | + |
| 24 | +const defaults = tartanPresets[0].params; |
| 25 | + |
| 26 | +const TartanWithControls = () => { |
| 27 | + const [{ count: stripeCount }, setStripeCount] = useControls(() => ({ |
| 28 | + Stripes: folder( |
| 29 | + { |
| 30 | + count: { |
| 31 | + value: defaults.stripeColors.length, |
| 32 | + min: 2, |
| 33 | + max: tartanMeta.maxStripeCount, |
| 34 | + step: 1, |
| 35 | + order: 0, |
| 36 | + }, |
| 37 | + }, |
| 38 | + { order: 1 } |
| 39 | + ), |
| 40 | + })); |
| 41 | + |
| 42 | + const [colors, setColors] = useControls(() => { |
| 43 | + const stripe: Record<string, { value: string; [key: string]: unknown }> = {}; |
| 44 | + |
| 45 | + for (let i = 0; i < stripeCount; i++) { |
| 46 | + stripe[`color${i + 1}`] = { |
| 47 | + value: defaults.stripeColors[i] ? toHsla(defaults.stripeColors[i]) : `hsla(${(40 * i) % 360}, 60%, 50%, 1)`, |
| 48 | + order: 1 + i * 2, |
| 49 | + }; |
| 50 | + } |
| 51 | + |
| 52 | + return { |
| 53 | + Stripes: folder(stripe), |
| 54 | + }; |
| 55 | + }, [stripeCount]); |
| 56 | + |
| 57 | + const [widths, setWidths] = useControls(() => { |
| 58 | + const stripe: Record<string, { value: number; [key: string]: unknown }> = {}; |
| 59 | + |
| 60 | + for (let i = 0; i < stripeCount; i++) { |
| 61 | + stripe[`width${i + 1}`] = { |
| 62 | + value: defaults.stripeWidths[i], |
| 63 | + min: 1, |
| 64 | + max: 400, |
| 65 | + step: 1, |
| 66 | + order: 1 + i * 2 + 1, |
| 67 | + }; |
| 68 | + } |
| 69 | + |
| 70 | + return { |
| 71 | + Stripes: folder(stripe), |
| 72 | + }; |
| 73 | + }, [stripeCount]); |
| 74 | + |
| 75 | + const [params, setParams] = useControls(() => { |
| 76 | + return { |
| 77 | + Transform: folder( |
| 78 | + { |
| 79 | + scale: { value: defaults.scale, min: 0.01, max: 4, order: 400 }, |
| 80 | + rotation: { value: defaults.rotation, min: 0, max: 360, order: 401 }, |
| 81 | + offsetX: { value: defaults.offsetX, min: -1, max: 1, order: 402 }, |
| 82 | + offsetY: { value: defaults.offsetY, min: -1, max: 1, order: 403 }, |
| 83 | + }, |
| 84 | + { |
| 85 | + order: 2, |
| 86 | + collapsed: false, |
| 87 | + } |
| 88 | + ), |
| 89 | + Fit: folder( |
| 90 | + { |
| 91 | + fit: { value: defaults.fit, options: Object.keys(ShaderFitOptions) as ShaderFit[], order: 404 }, |
| 92 | + worldWidth: { value: 1000, min: 0, max: 5120, order: 405 }, |
| 93 | + worldHeight: { value: 500, min: 0, max: 5120, order: 406 }, |
| 94 | + originX: { value: defaults.originX, min: 0, max: 1, order: 407 }, |
| 95 | + originY: { value: defaults.originY, min: 0, max: 1, order: 408 }, |
| 96 | + }, |
| 97 | + { |
| 98 | + order: 3, |
| 99 | + collapsed: true, |
| 100 | + } |
| 101 | + ), |
| 102 | + }; |
| 103 | + }); |
| 104 | + |
| 105 | + useControls(() => { |
| 106 | + const presets = Object.fromEntries( |
| 107 | + tartanPresets.map(({ name, params: { worldWidth, worldHeight, ...preset } }) => [ |
| 108 | + name, |
| 109 | + button(() => { |
| 110 | + const { stripeColors, stripeWidths, ...presetParams } = preset; |
| 111 | + setStripeCount({ count: stripeColors.length }); |
| 112 | + setColors( |
| 113 | + Object.fromEntries(stripeColors.map((value, i) => [`color${i + 1}`, toHsla(value)])) as unknown as Record< |
| 114 | + string, |
| 115 | + { value: string; [key: string]: unknown } |
| 116 | + > |
| 117 | + ); |
| 118 | + setWidths(Object.fromEntries(stripeWidths.map((value, i) => [`width${i + 1}`, value]))); |
| 119 | + setParamsSafe(params, setParams, presetParams); |
| 120 | + }), |
| 121 | + ]) |
| 122 | + ); |
| 123 | + return { |
| 124 | + Presets: folder(presets, { order: -1 }), |
| 125 | + }; |
| 126 | + }); |
| 127 | + |
| 128 | + // Reset to defaults on mount, so that Leva doesn't show values from other |
| 129 | + // shaders when navigating (if two shaders have a color1 param for example) |
| 130 | + useResetLevaParams(params, setParams, defaults); |
| 131 | + usePresetHighlight(tartanPresets, params); |
| 132 | + cleanUpLevaParams(params); |
| 133 | + |
| 134 | + return ( |
| 135 | + <> |
| 136 | + <Link href="/"> |
| 137 | + <BackButton /> |
| 138 | + </Link> |
| 139 | + <Tartan |
| 140 | + {...params} |
| 141 | + stripeColors={Object.values(colors) as unknown as Array<string>} |
| 142 | + stripeWidths={[...Object.values(widths), ...Array(9 - stripeCount).fill(0)]} |
| 143 | + className="fixed size-full" |
| 144 | + /> |
| 145 | + </> |
| 146 | + ); |
| 147 | +}; |
| 148 | + |
| 149 | +export default TartanWithControls; |
0 commit comments