mirror of
https://github.com/excalidraw/excalidraw
synced 2025-07-25 13:58:22 +08:00
add stroke sensivity action
This commit is contained in:
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -13,6 +13,7 @@ export {
|
||||
actionChangeStrokeWidth,
|
||||
actionChangeFillStyle,
|
||||
actionChangeSloppiness,
|
||||
actionChangePressureSensitivity,
|
||||
actionChangeOpacity,
|
||||
actionChangeFontSize,
|
||||
actionChangeFontFamily,
|
||||
|
@ -69,6 +69,7 @@ export type ActionName =
|
||||
| "changeStrokeStyle"
|
||||
| "changeArrowhead"
|
||||
| "changeArrowType"
|
||||
| "changePressureSensitivity"
|
||||
| "changeOpacity"
|
||||
| "changeFontSize"
|
||||
| "toggleCanvasMenu"
|
||||
|
@ -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))) && (
|
||||
|
@ -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)],
|
||||
|
@ -32,6 +32,7 @@
|
||||
"strokeStyle_dotted": "Dotted",
|
||||
"sloppiness": "Sloppiness",
|
||||
"opacity": "Opacity",
|
||||
"pressureSensitivity": "Stroke sensitivity",
|
||||
"textAlign": "Text align",
|
||||
"edges": "Edges",
|
||||
"sharp": "Sharp",
|
||||
|
Reference in New Issue
Block a user