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