add stroke sensivity action

This commit is contained in:
Ryan Di
2025-06-02 16:44:30 +10:00
parent ab6af41d33
commit 1c611d6c4f
6 changed files with 106 additions and 2 deletions

View File

@ -47,6 +47,7 @@ import {
isArrowElement,
isBoundToContainer,
isElbowArrow,
isFreeDrawElement,
isLinearElement,
isLineElement,
isTextElement,
@ -669,6 +670,26 @@ export const actionChangeStrokeStyle = register({
),
});
export const actionChangePressureSensitivity = register({
name: "changePressureSensitivity",
label: "labels.pressureSensitivity",
trackEvent: false,
perform: (elements, appState, value) => {
return {
elements,
appState: { ...appState, currentItemPressureSensitivity: value },
captureUpdate: CaptureUpdateAction.IMMEDIATELY,
};
},
PanelComponent: ({ app, appState, updateData }) => {
if (appState.activeTool.type !== "freedraw") {
return null;
}
return <PressureSensitivityRange updateData={updateData} app={app} />;
},
});
export const actionChangeOpacity = register({
name: "changeOpacity",
label: "labels.opacity",
@ -1857,3 +1878,78 @@ export const actionChangeArrowType = register({
);
},
});
const PressureSensitivityRange = ({
updateData,
app,
}: {
updateData: (value: number) => void;
app: AppClassProperties;
}) => {
const rangeRef = useRef<HTMLInputElement>(null);
const valueRef = useRef<HTMLDivElement>(null);
const selectedElements = app.scene.getSelectedElements(app.state);
let hasCommonPressureSensitivity = true;
const firstElement = selectedElements.find(isFreeDrawElement);
const leastCommonPressureSensitivity = selectedElements
.filter(isFreeDrawElement)
.reduce((acc, element) => {
const sensitivity = element.pressureSensitivity ?? 1;
if (acc != null && acc !== sensitivity) {
hasCommonPressureSensitivity = false;
}
if (acc == null || acc > sensitivity) {
return sensitivity;
}
return acc;
}, firstElement?.pressureSensitivity ?? null);
const value = Math.round(
(leastCommonPressureSensitivity ??
app.state.currentItemPressureSensitivity) * 100,
);
useEffect(() => {
if (rangeRef.current && valueRef.current) {
const rangeElement = rangeRef.current;
const valueElement = valueRef.current;
const inputWidth = rangeElement.offsetWidth;
const thumbWidth = 15;
const position =
(value / 100) * (inputWidth - thumbWidth) + thumbWidth / 2;
valueElement.style.left = `${position}px`;
rangeElement.style.background = `linear-gradient(to right, var(--color-slider-track) 0%, var(--color-slider-track) ${value}%, var(--button-bg) ${value}%, var(--button-bg) 100%)`;
}
}, [value]);
return (
<label className="control-label">
{t("labels.pressureSensitivity")}
<div className="range-wrapper">
<input
style={{
["--color-slider-track" as string]: hasCommonPressureSensitivity
? undefined
: "var(--button-bg)",
}}
ref={rangeRef}
type="range"
min="0"
max="100"
step="10"
onChange={(event) => {
updateData(+event.target.value / 100);
}}
value={value}
className="range-input"
data-testid="pressure-sensitivity"
/>
<div className="value-bubble" ref={valueRef}>
{value !== 0 ? value : null}
</div>
<div className="zero-label">0</div>
</div>
</label>
);
};

View File

@ -13,6 +13,7 @@ export {
actionChangeStrokeWidth,
actionChangeFillStyle,
actionChangeSloppiness,
actionChangePressureSensitivity,
actionChangeOpacity,
actionChangeFontSize,
actionChangeFontFamily,

View File

@ -69,6 +69,7 @@ export type ActionName =
| "changeStrokeStyle"
| "changeArrowhead"
| "changeArrowType"
| "changePressureSensitivity"
| "changeOpacity"
| "changeFontSize"
| "toggleCanvasMenu"

View File

@ -169,8 +169,12 @@ export const SelectedShapeActions = ({
renderAction("changeStrokeWidth")}
{(appState.activeTool.type === "freedraw" ||
targetElements.some((element) => element.type === "freedraw")) &&
renderAction("changeStrokeShape")}
targetElements.some((element) => element.type === "freedraw")) && (
<>
{renderAction("changeStrokeShape")}
{renderAction("changePressureSensitivity")}
</>
)}
{(hasStrokeStyle(appState.activeTool.type) ||
targetElements.some((element) => hasStrokeStyle(element.type))) && (

View File

@ -7588,6 +7588,7 @@ class App extends React.Component<AppProps, AppState> {
opacity: this.state.currentItemOpacity,
roundness: null,
simulatePressure,
pressureSensitivity: this.state.currentItemPressureSensitivity,
locked: false,
frameId: topLayerFrame ? topLayerFrame.id : null,
points: [pointFrom<LocalPoint>(0, 0)],

View File

@ -32,6 +32,7 @@
"strokeStyle_dotted": "Dotted",
"sloppiness": "Sloppiness",
"opacity": "Opacity",
"pressureSensitivity": "Stroke sensitivity",
"textAlign": "Text align",
"edges": "Edges",
"sharp": "Sharp",