mirror of
https://github.com/exogen/t2-model-skinner.git
synced 2026-04-29 16:25:33 +00:00
Filters can apply to base or all layers
This commit is contained in:
parent
8cad00552c
commit
0df14f1288
19 changed files with 145 additions and 32 deletions
|
|
@ -40,11 +40,13 @@ export default function CanvasTools() {
|
|||
setSaturation,
|
||||
brightness,
|
||||
setBrightness,
|
||||
layerMode,
|
||||
setLayerMode,
|
||||
activeCanvasType,
|
||||
addImages,
|
||||
exportSkin,
|
||||
} = useTools();
|
||||
const { isDrawingMode, setDrawingMode } = useCanvas(activeCanvas);
|
||||
const { canvas, isDrawingMode, setDrawingMode } = useCanvas(activeCanvas);
|
||||
const [isMac, setIsMac] = useState(false);
|
||||
const commandKeyPrefix = isMac ? "⌘" : "Ctrl ";
|
||||
const shiftKeySymbol = "⇧";
|
||||
|
|
@ -69,10 +71,6 @@ export default function CanvasTools() {
|
|||
],
|
||||
});
|
||||
|
||||
if (isFilterToolsOpen && !selectedObjects.length) {
|
||||
setFilterToolsOpen(false);
|
||||
}
|
||||
|
||||
const isSelectionLocked = selectedObjects.length
|
||||
? selectedObjects.every((object) => lockedObjects.has(object))
|
||||
: false;
|
||||
|
|
@ -179,7 +177,6 @@ export default function CanvasTools() {
|
|||
type="button"
|
||||
ref={setReferenceElement}
|
||||
data-active={isFilterToolsOpen ? "" : undefined}
|
||||
disabled={!selectedObjects.length}
|
||||
aria-label="Filters"
|
||||
title="Filters"
|
||||
onClick={() => {
|
||||
|
|
@ -207,6 +204,60 @@ export default function CanvasTools() {
|
|||
{...attributes.popper}
|
||||
>
|
||||
<div className="Fields">
|
||||
<div className="Field ApplyTo">
|
||||
<label>Layer:</label>
|
||||
<ul>
|
||||
{selectedObjects.length ? (
|
||||
<li>
|
||||
<input
|
||||
type="radio"
|
||||
name="FilterLayer"
|
||||
value="SelectedLayer"
|
||||
id="FilterLayer-SelectedLayer"
|
||||
checked={layerMode === "SelectedLayer"}
|
||||
onChange={() => {
|
||||
setLayerMode("SelectedLayer");
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="FilterLayer-SelectedLayer">
|
||||
selected ({selectedObjects.length.toLocaleString()})
|
||||
</label>
|
||||
</li>
|
||||
) : (
|
||||
<>
|
||||
<li>
|
||||
<input
|
||||
type="radio"
|
||||
name="FilterLayer"
|
||||
value="BaseLayer"
|
||||
id="FilterLayer-BaseLayer"
|
||||
checked={layerMode === "BaseLayer"}
|
||||
onChange={() => {
|
||||
setLayerMode("BaseLayer");
|
||||
}}
|
||||
/>{" "}
|
||||
<label htmlFor="FilterLayer-BaseLayer">base</label>
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
type="radio"
|
||||
name="FilterLayer"
|
||||
value="AllLayers"
|
||||
id="FilterLayer-AllLayers"
|
||||
checked={layerMode === "AllLayers"}
|
||||
onChange={() => {
|
||||
setLayerMode("AllLayers");
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="FilterLayer-AllLayers">
|
||||
all (
|
||||
{canvas?._objects.length.toLocaleString() ?? 0})
|
||||
</label>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="Field">
|
||||
<label>
|
||||
Hue:{" "}
|
||||
|
|
|
|||
|
|
@ -90,16 +90,33 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
const [filterChanges, setFilterChanges] = useState<
|
||||
Array<[fabric.Object, ObjectFilters]>
|
||||
>(() => []);
|
||||
const [layerMode, setLayerMode] = useState("BaseLayer");
|
||||
|
||||
if (selectedObjects.length) {
|
||||
if (layerMode !== "SelectedLayer") {
|
||||
setLayerMode("SelectedLayer");
|
||||
}
|
||||
} else {
|
||||
if (layerMode === "SelectedLayer") {
|
||||
setLayerMode("BaseLayer");
|
||||
}
|
||||
}
|
||||
|
||||
const getFilter = (name: keyof ObjectFilters) => {
|
||||
if (selectedObjects.length) {
|
||||
let applyObjects = selectedObjects;
|
||||
if (layerMode === "AllLayers") {
|
||||
applyObjects = canvas?._objects ?? [];
|
||||
} else if (layerMode === "BaseLayer") {
|
||||
applyObjects = canvas?._objects.slice(0, 1) ?? [];
|
||||
}
|
||||
if (applyObjects.length) {
|
||||
const getValue = (i: number) =>
|
||||
(filterMap.get(selectedObjects[i]) ?? {})[name] ?? 0;
|
||||
(filterMap.get(applyObjects[i]) ?? {})[name] ?? 0;
|
||||
const firstValue = getValue(0);
|
||||
if (
|
||||
selectedObjects
|
||||
applyObjects
|
||||
.slice(1)
|
||||
.every((selectedObject, i) => getValue(i + 1) === firstValue)
|
||||
.every((applyObject, i) => getValue(i + 1) === firstValue)
|
||||
) {
|
||||
return firstValue;
|
||||
}
|
||||
|
|
@ -115,22 +132,24 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
const setFilter = useCallback(
|
||||
(name: keyof ObjectFilters, value: number) => {
|
||||
if (!selectedObjects.length) {
|
||||
setFilterChanges([]);
|
||||
return;
|
||||
}
|
||||
const filterChanges: Array<[fabric.Object, ObjectFilters]> = [];
|
||||
const newFilterMap = new Map(filterMap);
|
||||
for (const selectedObject of selectedObjects) {
|
||||
const existingFilters = filterMap.get(selectedObject) ?? {};
|
||||
let applyObjects = selectedObjects;
|
||||
if (layerMode === "AllLayers") {
|
||||
applyObjects = canvas?._objects ?? [];
|
||||
} else if (layerMode === "BaseLayer") {
|
||||
applyObjects = canvas?._objects.slice(0, 1) ?? [];
|
||||
}
|
||||
for (const applyObject of applyObjects) {
|
||||
const existingFilters = filterMap.get(applyObject) ?? {};
|
||||
const newFilters = { ...existingFilters, [name]: value };
|
||||
newFilterMap.set(selectedObject, newFilters);
|
||||
filterChanges.push([selectedObject, newFilters]);
|
||||
newFilterMap.set(applyObject, newFilters);
|
||||
filterChanges.push([applyObject, newFilters]);
|
||||
}
|
||||
setFilterMap(newFilterMap);
|
||||
setFilterChanges(filterChanges);
|
||||
},
|
||||
[filterMap, selectedObjects]
|
||||
[canvas, layerMode, filterMap, selectedObjects]
|
||||
);
|
||||
|
||||
const setHueRotate = useCallback(
|
||||
|
|
@ -434,6 +453,8 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
setSaturation,
|
||||
brightness,
|
||||
setBrightness,
|
||||
layerMode,
|
||||
setLayerMode,
|
||||
selectedObjects,
|
||||
lockSelection,
|
||||
unlockSelection,
|
||||
|
|
@ -464,6 +485,7 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
hueRotate,
|
||||
saturation,
|
||||
brightness,
|
||||
layerMode,
|
||||
setHueRotate,
|
||||
setSaturation,
|
||||
setBrightness,
|
||||
|
|
|
|||
|
|
@ -385,3 +385,41 @@ select {
|
|||
transform: translate3d(0, -50%, 0);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
h6 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: bold;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.Field.ApplyTo {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-self: flex-start;
|
||||
align-items: center;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.Field.ApplyTo input[type="radio"] {
|
||||
margin: 2px 3px;
|
||||
}
|
||||
|
||||
.Field.ApplyTo ul {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.Field.ApplyTo li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ interface ToolsContextValue {
|
|||
setSaturation: (saturation: number) => void;
|
||||
brightness: number | null;
|
||||
setBrightness: (brightness: number) => void;
|
||||
layerMode: string;
|
||||
setLayerMode: (layerMode: string) => void;
|
||||
deleteSelection: () => void;
|
||||
undo: () => void;
|
||||
redo: () => void;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue