Filters can apply to base or all layers

This commit is contained in:
Brian Beck 2023-06-28 23:26:49 -07:00
parent 8cad00552c
commit 0df14f1288
19 changed files with 145 additions and 32 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
!function(){"use strict";var e,t,n,r,o,u,i,c={},f={};function a(e){var t=f[e];if(void 0!==t)return t.exports;var n=f[e]={exports:{}},r=!0;try{c[e].call(n.exports,n,n.exports,a),r=!1}finally{r&&delete f[e]}return n.exports}a.m=c,e=[],a.O=function(t,n,r,o){if(n){o=o||0;for(var u=e.length;u>0&&e[u-1][2]>o;u--)e[u]=e[u-1];e[u]=[n,r,o];return}for(var i=1/0,u=0;u<e.length;u++){for(var n=e[u][0],r=e[u][1],o=e[u][2],c=!0,f=0;f<n.length;f++)i>=o&&Object.keys(a.O).every(function(e){return a.O[e](n[f])})?n.splice(f--,1):(c=!1,o<i&&(i=o));if(c){e.splice(u--,1);var l=r();void 0!==l&&(t=l)}}return t},a.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(t,{a:t}),t},a.d=function(e,t){for(var n in t)a.o(t,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},a.f={},a.e=function(e){return Promise.all(Object.keys(a.f).reduce(function(t,n){return a.f[n](e,t),t},[]))},a.u=function(e){return"static/chunks/"+(737===e?"fb7d5399":e)+"."+({70:"0ba1ca12d54bca2d",258:"53d25aa602c4d686",354:"3289d8281650c8de",737:"ddae1e8fed13bcfe",990:"ccb4bc1efe5cd94f"})[e]+".js"},a.miniCssF=function(e){return"static/css/ed4f6c5fadb41eb8.css"},a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t={},n="_N_E:",a.l=function(e,r,o,u){if(t[e]){t[e].push(r);return}if(void 0!==o)for(var i,c,f=document.getElementsByTagName("script"),l=0;l<f.length;l++){var s=f[l];if(s.getAttribute("src")==e||s.getAttribute("data-webpack")==n+o){i=s;break}}i||(c=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.setAttribute("data-webpack",n+o),i.src=a.tu(e)),t[e]=[r];var d=function(n,r){i.onerror=i.onload=null,clearTimeout(p);var o=t[e];if(delete t[e],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach(function(e){return e(r)}),n)return n(r)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=d.bind(null,i.onerror),i.onload=d.bind(null,i.onload),c&&document.head.appendChild(i)},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.tt=function(){return void 0===r&&(r={createScriptURL:function(e){return e}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(r=trustedTypes.createPolicy("nextjs#bundler",r))),r},a.tu=function(e){return a.tt().createScriptURL(e)},a.p="/t2-model-skinner/_next/",o={272:0},a.f.j=function(e,t){var n=a.o(o,e)?o[e]:void 0;if(0!==n){if(n)t.push(n[2]);else if(272!=e){var r=new Promise(function(t,r){n=o[e]=[t,r]});t.push(n[2]=r);var u=a.p+a.u(e),i=Error();a.l(u,function(t){if(a.o(o,e)&&(0!==(n=o[e])&&(o[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),u=t&&t.target&&t.target.src;i.message="Loading chunk "+e+" failed.\n("+r+": "+u+")",i.name="ChunkLoadError",i.type=r,i.request=u,n[1](i)}},"chunk-"+e,e)}else o[e]=0}},a.O.j=function(e){return 0===o[e]},u=function(e,t){var n,r,u=t[0],i=t[1],c=t[2],f=0;if(u.some(function(e){return 0!==o[e]})){for(n in i)a.o(i,n)&&(a.m[n]=i[n]);if(c)var l=c(a)}for(e&&e(t);f<u.length;f++)r=u[f],a.o(o,r)&&o[r]&&o[r][0](),o[r]=0;return a.O(l)},(i=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(u.bind(null,0)),i.push=u.bind(null,i.push.bind(i))}();
//# sourceMappingURL=webpack-0bca67f1077ef5ed.js.map

View file

@ -1,2 +0,0 @@
!function(){"use strict";var e,t,n,r,o,u,i,c={},a={};function f(e){var t=a[e];if(void 0!==t)return t.exports;var n=a[e]={exports:{}},r=!0;try{c[e].call(n.exports,n,n.exports,f),r=!1}finally{r&&delete a[e]}return n.exports}f.m=c,e=[],f.O=function(t,n,r,o){if(n){o=o||0;for(var u=e.length;u>0&&e[u-1][2]>o;u--)e[u]=e[u-1];e[u]=[n,r,o];return}for(var i=1/0,u=0;u<e.length;u++){for(var n=e[u][0],r=e[u][1],o=e[u][2],c=!0,a=0;a<n.length;a++)i>=o&&Object.keys(f.O).every(function(e){return f.O[e](n[a])})?n.splice(a--,1):(c=!1,o<i&&(i=o));if(c){e.splice(u--,1);var l=r();void 0!==l&&(t=l)}}return t},f.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(t,{a:t}),t},f.d=function(e,t){for(var n in t)f.o(t,n)&&!f.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},f.f={},f.e=function(e){return Promise.all(Object.keys(f.f).reduce(function(t,n){return f.f[n](e,t),t},[]))},f.u=function(e){return"static/chunks/"+(737===e?"fb7d5399":e)+"."+({70:"0ba1ca12d54bca2d",258:"53d25aa602c4d686",354:"3289d8281650c8de",737:"ddae1e8fed13bcfe",990:"ccb4bc1efe5cd94f"})[e]+".js"},f.miniCssF=function(e){return"static/css/7d1c0438425db85d.css"},f.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),f.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t={},n="_N_E:",f.l=function(e,r,o,u){if(t[e]){t[e].push(r);return}if(void 0!==o)for(var i,c,a=document.getElementsByTagName("script"),l=0;l<a.length;l++){var s=a[l];if(s.getAttribute("src")==e||s.getAttribute("data-webpack")==n+o){i=s;break}}i||(c=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,f.nc&&i.setAttribute("nonce",f.nc),i.setAttribute("data-webpack",n+o),i.src=f.tu(e)),t[e]=[r];var d=function(n,r){i.onerror=i.onload=null,clearTimeout(p);var o=t[e];if(delete t[e],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach(function(e){return e(r)}),n)return n(r)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=d.bind(null,i.onerror),i.onload=d.bind(null,i.onload),c&&document.head.appendChild(i)},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.tt=function(){return void 0===r&&(r={createScriptURL:function(e){return e}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(r=trustedTypes.createPolicy("nextjs#bundler",r))),r},f.tu=function(e){return f.tt().createScriptURL(e)},f.p="/t2-model-skinner/_next/",o={272:0},f.f.j=function(e,t){var n=f.o(o,e)?o[e]:void 0;if(0!==n){if(n)t.push(n[2]);else if(272!=e){var r=new Promise(function(t,r){n=o[e]=[t,r]});t.push(n[2]=r);var u=f.p+f.u(e),i=Error();f.l(u,function(t){if(f.o(o,e)&&(0!==(n=o[e])&&(o[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),u=t&&t.target&&t.target.src;i.message="Loading chunk "+e+" failed.\n("+r+": "+u+")",i.name="ChunkLoadError",i.type=r,i.request=u,n[1](i)}},"chunk-"+e,e)}else o[e]=0}},f.O.j=function(e){return 0===o[e]},u=function(e,t){var n,r,u=t[0],i=t[1],c=t[2],a=0;if(u.some(function(e){return 0!==o[e]})){for(n in i)f.o(i,n)&&(f.m[n]=i[n]);if(c)var l=c(f)}for(e&&e(t);a<u.length;a++)r=u[a],f.o(o,r)&&o[r]&&o[r][0](),o[r]=0;return f.O(l)},(i=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(u.bind(null,0)),i.push=u.bind(null,i.push.bind(i))}();
//# sourceMappingURL=webpack-a09c3f76050eda6f.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
self.__BUILD_MANIFEST={__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":["static/chunks/78e521c3-312593b4f3190cc4.js","static/chunks/95b64a6e-6f3d919198a9be32.js","static/chunks/31664189-f79df42a6a9e05f8.js","static/chunks/545f34e4-f96cf9e26b6b92a5.js","static/chunks/1bfc9850-6b316c8ef06e5170.js","static/chunks/d7eeaac4-06e64d251e2cbda7.js","static/chunks/f580fadb-a8e2c6896615a304.js","static/chunks/50-cece886aa17c1d5e.js","static/chunks/pages/index-dce4039e544712d9.js"],"/_error":["static/chunks/pages/_error-479484f6c157e921.js"],sortedPages:["/","/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
self.__BUILD_MANIFEST={__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":["static/chunks/78e521c3-312593b4f3190cc4.js","static/chunks/95b64a6e-6f3d919198a9be32.js","static/chunks/31664189-f79df42a6a9e05f8.js","static/chunks/545f34e4-f96cf9e26b6b92a5.js","static/chunks/1bfc9850-6b316c8ef06e5170.js","static/chunks/d7eeaac4-06e64d251e2cbda7.js","static/chunks/f580fadb-a8e2c6896615a304.js","static/chunks/50-cece886aa17c1d5e.js","static/chunks/pages/index-63dd7a6559067c0e.js"],"/_error":["static/chunks/pages/_error-479484f6c157e921.js"],sortedPages:["/","/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();

File diff suppressed because one or more lines are too long

View file

@ -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:{" "}

View file

@ -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,

View file

@ -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;
}

View file

@ -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;