feat(web): сворачивание блока «Кривая» в инструменте «Свет»
Заголовок «Кривая» стал кнопкой со стрелкой: график с гистограммой и точками сворачивается и разворачивается, кнопки выбора канала остаются видны всегда, чтобы канал можно было переключить и в свёрнутом виде.
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
let busy = $state(false);
|
||||
let error = $state('');
|
||||
let collapsed = $state(false);
|
||||
let curveCollapsed = $state(false);
|
||||
let channel = $state<CurveChannel>('rgb');
|
||||
|
||||
const saved = $derived(readEditParams(photo));
|
||||
@@ -401,7 +402,19 @@
|
||||
|
||||
<div class="border-border border-t pt-3">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="text-text text-xs font-semibold">Кривая</span>
|
||||
<button
|
||||
type="button"
|
||||
class="text-text hover:text-primary flex items-center gap-1 text-xs font-semibold"
|
||||
aria-expanded={!curveCollapsed}
|
||||
aria-controls="light-curve"
|
||||
onclick={() => (curveCollapsed = !curveCollapsed)}
|
||||
>
|
||||
<Icon
|
||||
name="expand_more"
|
||||
class="size-4 transition-transform {curveCollapsed ? '-rotate-90' : ''}"
|
||||
/>
|
||||
Кривая
|
||||
</button>
|
||||
<div class="flex items-center gap-1.5">
|
||||
{#each CURVE_CHANNELS as ch (ch)}
|
||||
<button
|
||||
@@ -420,68 +433,72 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<svg
|
||||
class="border-border mt-2 block aspect-square w-full touch-none rounded border"
|
||||
viewBox="0 0 100 100"
|
||||
role="application"
|
||||
aria-label="Тоновая кривая"
|
||||
onpointerdown={onCurveDown}
|
||||
onpointermove={onPointMove}
|
||||
onpointerup={onPointUp}
|
||||
onpointerleave={onPointUp}
|
||||
>
|
||||
<rect x="0" y="0" width="100" height="100" class="bg-bg" />
|
||||
{#if histogram}
|
||||
{#each visibleChannels as ch (ch)}
|
||||
{#if !curveCollapsed}
|
||||
<div id="light-curve">
|
||||
<svg
|
||||
class="border-border mt-2 block aspect-square w-full touch-none rounded border"
|
||||
viewBox="0 0 100 100"
|
||||
role="application"
|
||||
aria-label="Тоновая кривая"
|
||||
onpointerdown={onCurveDown}
|
||||
onpointermove={onPointMove}
|
||||
onpointerup={onPointUp}
|
||||
onpointerleave={onPointUp}
|
||||
>
|
||||
<rect x="0" y="0" width="100" height="100" class="bg-bg" />
|
||||
{#if histogram}
|
||||
{#each visibleChannels as ch (ch)}
|
||||
<path
|
||||
d={histogramPath(histogramValues(ch))}
|
||||
fill="none"
|
||||
stroke-width="0.4"
|
||||
style="stroke: {CHANNEL_COLORS[ch]}; opacity: {ch === 'rgb' ? 0.45 : 0.7}"
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
{#each [25, 50, 75] as step (step)}
|
||||
<line x1={step} y1="0" x2={step} y2="100" class="stroke-border" stroke-width="0.4" />
|
||||
<line x1="0" y1={step} x2="100" y2={step} class="stroke-border" stroke-width="0.4" />
|
||||
{/each}
|
||||
<path
|
||||
d={histogramPath(histogramValues(ch))}
|
||||
d={curvePath(channel)}
|
||||
fill="none"
|
||||
stroke-width="0.4"
|
||||
style="stroke: {CHANNEL_COLORS[ch]}; opacity: {ch === 'rgb' ? 0.45 : 0.7}"
|
||||
stroke-width="1.4"
|
||||
style="stroke: {CHANNEL_COLORS[channel]}"
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
{#each [25, 50, 75] as step (step)}
|
||||
<line x1={step} y1="0" x2={step} y2="100" class="stroke-border" stroke-width="0.4" />
|
||||
<line x1="0" y1={step} x2="100" y2={step} class="stroke-border" stroke-width="0.4" />
|
||||
{/each}
|
||||
<path
|
||||
d={curvePath(channel)}
|
||||
fill="none"
|
||||
stroke-width="1.4"
|
||||
style="stroke: {CHANNEL_COLORS[channel]}"
|
||||
/>
|
||||
{#each channelPoints(channel) as point, index (index)}
|
||||
<circle
|
||||
cx={point.x * 100}
|
||||
cy={(1 - point.y) * 100}
|
||||
r="2.4"
|
||||
class="cursor-grab"
|
||||
stroke-width="1.2"
|
||||
style="fill: hsl(var(--bg)); stroke: {CHANNEL_COLORS[channel]}"
|
||||
role="slider"
|
||||
tabindex="0"
|
||||
aria-label="Точка кривой {index + 1}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="1"
|
||||
aria-valuenow={point.x}
|
||||
onpointerdown={(e) => onPointDown(e, index)}
|
||||
onkeydown={(e) => onPointKey(e, index)}
|
||||
></circle>
|
||||
{/each}
|
||||
</svg>
|
||||
{#each channelPoints(channel) as point, index (index)}
|
||||
<circle
|
||||
cx={point.x * 100}
|
||||
cy={(1 - point.y) * 100}
|
||||
r="2.4"
|
||||
class="cursor-grab"
|
||||
stroke-width="1.2"
|
||||
style="fill: hsl(var(--bg)); stroke: {CHANNEL_COLORS[channel]}"
|
||||
role="slider"
|
||||
tabindex="0"
|
||||
aria-label="Точка кривой {index + 1}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="1"
|
||||
aria-valuenow={point.x}
|
||||
onpointerdown={(e) => onPointDown(e, index)}
|
||||
onkeydown={(e) => onPointKey(e, index)}
|
||||
></circle>
|
||||
{/each}
|
||||
</svg>
|
||||
|
||||
<div class="mt-2 flex items-center justify-between gap-2">
|
||||
<span class="text-text-muted text-xs">Точечная кривая · линейная</span>
|
||||
<button
|
||||
type="button"
|
||||
class="border-border text-text-muted hover:text-text rounded-md border px-2 py-1 text-xs disabled:opacity-40"
|
||||
disabled={!canEdit || !channelPoints(channel).length}
|
||||
onclick={resetCurve}
|
||||
>
|
||||
Сбросить кривую
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-2 flex items-center justify-between gap-2">
|
||||
<span class="text-text-muted text-xs">Точечная кривая · линейная</span>
|
||||
<button
|
||||
type="button"
|
||||
class="border-border text-text-muted hover:text-text rounded-md border px-2 py-1 text-xs disabled:opacity-40"
|
||||
disabled={!canEdit || !channelPoints(channel).length}
|
||||
onclick={resetCurve}
|
||||
>
|
||||
Сбросить кривую
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if busy}
|
||||
|
||||
Reference in New Issue
Block a user