|
|
@ -211,7 +211,7 @@ function getSubjectProjection(viewMode: LightingViewMode) { |
|
|
left: 50, |
|
|
left: 50, |
|
|
top: 50, |
|
|
top: 50, |
|
|
rotateX: -8, |
|
|
rotateX: -8, |
|
|
rotateY: 58, |
|
|
rotateY: -58, |
|
|
rotateZ: 0, |
|
|
rotateZ: 0, |
|
|
scale: 0.98, |
|
|
scale: 0.98, |
|
|
width: 45, |
|
|
width: 45, |
|
|
@ -451,6 +451,7 @@ function LightingColorPicker({ |
|
|
const [open, setOpen] = useState(false); |
|
|
const [open, setOpen] = useState(false); |
|
|
const hsv = useMemo(() => hexToHsv(value), [value]); |
|
|
const hsv = useMemo(() => hexToHsv(value), [value]); |
|
|
const [hue, setHue] = useState(hsv.h); |
|
|
const [hue, setHue] = useState(hsv.h); |
|
|
|
|
|
const pickerRef = useRef<HTMLDivElement>(null); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
|
if (hsv.s > 0) { |
|
|
if (hsv.s > 0) { |
|
|
@ -458,6 +459,19 @@ function LightingColorPicker({ |
|
|
} |
|
|
} |
|
|
}, [hsv.h, hsv.s]); |
|
|
}, [hsv.h, hsv.s]); |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
if (!open) return; |
|
|
|
|
|
|
|
|
|
|
|
const handlePointerDown = (event: PointerEvent) => { |
|
|
|
|
|
const target = event.target; |
|
|
|
|
|
if (target instanceof Node && pickerRef.current?.contains(target)) return; |
|
|
|
|
|
setOpen(false); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
document.addEventListener("pointerdown", handlePointerDown); |
|
|
|
|
|
return () => document.removeEventListener("pointerdown", handlePointerDown); |
|
|
|
|
|
}, [open]); |
|
|
|
|
|
|
|
|
const updateSaturationValue = (event: ReactPointerEvent<HTMLDivElement>) => { |
|
|
const updateSaturationValue = (event: ReactPointerEvent<HTMLDivElement>) => { |
|
|
const rect = event.currentTarget.getBoundingClientRect(); |
|
|
const rect = event.currentTarget.getBoundingClientRect(); |
|
|
const s = clamp((event.clientX - rect.left) / rect.width, 0, 1); |
|
|
const s = clamp((event.clientX - rect.left) / rect.width, 0, 1); |
|
|
@ -483,7 +497,7 @@ function LightingColorPicker({ |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
return ( |
|
|
return ( |
|
|
<div className="relative"> |
|
|
<div ref={pickerRef} className="relative"> |
|
|
<button |
|
|
<button |
|
|
type="button" |
|
|
type="button" |
|
|
onClick={() => setOpen((current) => !current)} |
|
|
onClick={() => setOpen((current) => !current)} |
|
|
@ -494,7 +508,10 @@ function LightingColorPicker({ |
|
|
</button> |
|
|
</button> |
|
|
|
|
|
|
|
|
{open && ( |
|
|
{open && ( |
|
|
<div className="absolute left-0 top-10 z-[70] w-44 rounded-lg border border-neutral-600 bg-neutral-900 p-2 shadow-2xl shadow-black/60"> |
|
|
<div |
|
|
|
|
|
data-testid="lighting-color-picker-popover" |
|
|
|
|
|
className="absolute left-0 top-10 z-[70] w-44 rounded-lg border border-neutral-600 bg-neutral-900 p-2 shadow-2xl shadow-black/60" |
|
|
|
|
|
> |
|
|
<div |
|
|
<div |
|
|
className="relative h-28 cursor-crosshair overflow-hidden rounded-md" |
|
|
className="relative h-28 cursor-crosshair overflow-hidden rounded-md" |
|
|
style={{ |
|
|
style={{ |
|
|
@ -737,13 +754,13 @@ export function LightingEffectModal({ |
|
|
</div> |
|
|
</div> |
|
|
</label> |
|
|
</label> |
|
|
|
|
|
|
|
|
<label className="flex items-center justify-between gap-3"> |
|
|
<div className="flex items-center justify-between gap-3"> |
|
|
<span className="text-neutral-400">颜色</span> |
|
|
<span className="text-neutral-400">颜色</span> |
|
|
<LightingColorPicker |
|
|
<LightingColorPicker |
|
|
value={settings.color} |
|
|
value={settings.color} |
|
|
onChange={(color) => patchSettings({ color })} |
|
|
onChange={(color) => patchSettings({ color })} |
|
|
/> |
|
|
/> |
|
|
</label> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div className="space-y-2"> |
|
|
<div className="space-y-2"> |
|
|
<span className="text-neutral-400">主光源</span> |
|
|
<span className="text-neutral-400">主光源</span> |
|
|
|