Skip to content

Commit

Permalink
feat: add draggable handles
Browse files Browse the repository at this point in the history
  • Loading branch information
eug-vs committed Sep 11, 2024
1 parent 35bd08c commit 2dcbb0b
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 40 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"next": "14.2.9",
"react": "^18",
"react-dom": "^18",
"react-draggable": "^4.4.6",
"react-hook-form": "^7.53.0",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
Expand Down
40 changes: 31 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 67 additions & 14 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { useState } from "react";
import { RadialBackground, type Vector } from "./radialBackground";
import { useForm } from "react-hook-form";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
import {
Form,
Expand All @@ -12,9 +11,10 @@ import {
FormLabel,
} from "@/components/ui/form";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { Slider } from "@/components/ui/slider";
import _ from "lodash";
import Draggable from "react-draggable";
import { cn } from "@/lib/utils";

const vectorSchema = z.tuple([z.number(), z.number()]);

Expand Down Expand Up @@ -49,9 +49,9 @@ export default function Home() {
initialParentAspect: 5 / 2,

debug: false,
showHandles: false,
showHandles: true,
showGrid: false,
runtimeAspectCalculation: false,
runtimeAspectCalculation: true,

originalGradientSize: [0.5, 0.5] as Vector,

Expand All @@ -66,7 +66,7 @@ export default function Home() {
},
});

const { width, height, ...props } = form.watch();
const { width, height, showHandles, ...props } = form.watch();

return (
<main className="min-h-screen flex gap-8 p-20 bg-gradient-to-r from-green-100 to-blue-200 rounded-lg">
Expand All @@ -75,8 +75,8 @@ export default function Home() {
{(
[
"debug",
"showHandles",
"showGrid",
"showHandles",
"runtimeAspectCalculation",
] as const
).map((name) => (
Expand All @@ -88,7 +88,7 @@ export default function Home() {
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
<FormControl>
<Checkbox
value={field.value.toString()}
defaultChecked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
Expand All @@ -108,7 +108,7 @@ export default function Home() {
min={150}
max={800}
value={[field.value]}
onValueChange={field.onChange}
onValueChange={(v) => field.onChange(v[0])}
/>
</FormControl>
<FormLabel>Width</FormLabel>
Expand All @@ -126,7 +126,7 @@ export default function Home() {
min={150}
max={800}
value={[field.value]}
onValueChange={field.onChange}
onValueChange={(v) => field.onChange(v[0])}
/>
</FormControl>
<FormLabel>Height</FormLabel>
Expand All @@ -145,7 +145,7 @@ export default function Home() {
max={1.5}
step={0.01}
value={[field.value]}
onValueChange={field.onChange}
onValueChange={(v) => field.onChange(v[0])}
/>
</FormControl>
<FormLabel>Original ellipse width %</FormLabel>
Expand All @@ -164,26 +164,79 @@ export default function Home() {
max={1.5}
step={0.01}
value={[field.value]}
onValueChange={field.onChange}
onValueChange={(v) => field.onChange(v[0])}
/>
</FormControl>
<FormLabel>Original ellipse width %</FormLabel>
</FormItem>
)}
/>
<FormField
control={form.control}
name="initialParentAspect"
render={({ field }) => (
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
<FormControl>
<Slider
className="max-w-52"
min={0.2}
max={5}
step={0.01}
value={[field.value]}
onValueChange={(v) => field.onChange(v[0])}
/>
</FormControl>
<FormLabel>Initial parent aspect ratio</FormLabel>
</FormItem>
)}
/>
</Form>
<div
className="mt-20 mx-auto relative flex justify-center flex-col items-center col-span-2 rounded-lg border border-gray-500"
style={{
width: `${width}px`,
height: `${height}px`,
width,
height,
}}
>
<RadialBackground className="rounded-lg" {...props} />
<span className="z-10">Hello, world!</span>
<span className="z-10">
{width} x {height}
</span>
{showHandles &&
(["center", "a", "b"] as const).map((name, index) => (
<div className="absolute" key={index}>
<Controller
name={name}
control={form.control}
render={({ field }) => (
<Draggable
positionOffset={{
x: -width / 2,
y: -height / 2,
}}
position={{
x: field.value[0] * width,
y: field.value[1] * height,
}}
onDrag={(_e, data) => {
field.onChange([data.x / width, data.y / height]);
}}
>
<div className="cursor-move z-50">
<div
className={cn(`rounded-full size-4`, {
"bg-black": index === 0,
"bg-red-500": index === 1,
"bg-blue-500": index === 2,
})}
/>
</div>
</Draggable>
)}
></Controller>
</div>
))}
</div>
</div>
<div>
Expand Down
18 changes: 1 addition & 17 deletions src/app/radialBackground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ type Props = {
runtimeAspectCalculation?: boolean;

stops: Stop[];
showHandles?: boolean;
showGrid?: boolean;
debug?: boolean;
} & (
Expand All @@ -65,7 +64,6 @@ export function RadialBackground({
b,
stops,
debug = false,
showHandles = false,
showGrid = false,
originalGradientSize = [1, 1] as Vector,
initialParentAspect = 1,
Expand Down Expand Up @@ -109,11 +107,9 @@ export function RadialBackground({
const A = sub(a, center).map((x) => (x * 2) / originalGradientSize[0]);
const B = sub(b, center).map((x) => (x * 2) / originalGradientSize[1]);

console.log({ A, B });
// Account for aspect ratio (don't ask me why this works)
A[1] = A[1] / parentAspect;
B[0] = B[0] * parentAspect;
console.log("Transformed", { A, B });
const matrixString = `matrix(${A.join(", ")}, ${B.join(", ")}, 0, 0)`;

return (
Expand Down Expand Up @@ -141,22 +137,10 @@ export function RadialBackground({
_.times(4).map((i) => (
<div
key={i}
className="border border-black border-collapse animate-in zoom-in"
className="border border-black/30 animate-in zoom-in"
></div>
))}
</div>
{showHandles &&
[center, a, b].map((point, index) => (
<div
key={index}
className="size-full top-0 left-0 z-50 absolute animate-in zoom-in"
style={{
transform: `translate(${point.map(toPercentageString).join(", ")})`,
}}
>
<div className="bg-black rounded-full size-4 translate-x-[-50%] translate-y-[-50%]"></div>
</div>
))}
</div>
);
}

0 comments on commit 2dcbb0b

Please sign in to comment.