mirror of
https://github.com/excalidraw/excalidraw
synced 2025-07-25 13:58:22 +08:00
change slider to radio
This commit is contained in:
@ -128,6 +128,8 @@ import {
|
||||
ArrowheadCrowfootIcon,
|
||||
ArrowheadCrowfootOneIcon,
|
||||
ArrowheadCrowfootOneOrManyIcon,
|
||||
strokeWidthFixedIcon,
|
||||
strokeWidthVariableIcon,
|
||||
} from "../components/icons";
|
||||
|
||||
import { Fonts } from "../fonts";
|
||||
@ -671,8 +673,8 @@ export const actionChangeStrokeStyle = register({
|
||||
});
|
||||
|
||||
export const actionChangePressureSensitivity = register({
|
||||
name: "changePressureSensitivity",
|
||||
label: "labels.pressureSensitivity",
|
||||
name: "changeStrokeType",
|
||||
label: "labels.strokeType",
|
||||
trackEvent: false,
|
||||
perform: (elements, appState, value) => {
|
||||
return {
|
||||
@ -686,7 +688,47 @@ export const actionChangePressureSensitivity = register({
|
||||
return null;
|
||||
}
|
||||
|
||||
return <PressureSensitivityRange updateData={updateData} app={app} />;
|
||||
const selectedElements = app.scene.getSelectedElements(app.state);
|
||||
const firstElement = selectedElements.find(isFreeDrawElement);
|
||||
const commonPressureSensitivity = selectedElements
|
||||
.filter(isFreeDrawElement)
|
||||
.reduce((acc, element) => {
|
||||
const sensitivity = element.pressureSensitivity ?? 1;
|
||||
if (acc !== null && acc !== sensitivity) {
|
||||
return null; // No common value
|
||||
}
|
||||
return sensitivity;
|
||||
}, firstElement?.pressureSensitivity ?? null);
|
||||
|
||||
const currentValue =
|
||||
commonPressureSensitivity ?? appState.currentItemPressureSensitivity;
|
||||
|
||||
return (
|
||||
<fieldset>
|
||||
<legend>{t("labels.strokeType")}</legend>
|
||||
<div className="buttonList">
|
||||
<RadioSelection
|
||||
group="pressure-sensitivity"
|
||||
options={[
|
||||
{
|
||||
value: 0,
|
||||
text: t("labels.strokeWidthFixed"),
|
||||
icon: strokeWidthFixedIcon,
|
||||
testId: "pressure-fixed",
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
text: t("labels.strokeWidthVariable"),
|
||||
icon: strokeWidthVariableIcon,
|
||||
testId: "pressure-variable",
|
||||
},
|
||||
]}
|
||||
value={currentValue}
|
||||
onChange={(value) => updateData(value)}
|
||||
/>
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@ -1878,78 +1920,3 @@ 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>
|
||||
);
|
||||
};
|
||||
|
@ -69,7 +69,7 @@ export type ActionName =
|
||||
| "changeStrokeStyle"
|
||||
| "changeArrowhead"
|
||||
| "changeArrowType"
|
||||
| "changePressureSensitivity"
|
||||
| "changeStrokeType"
|
||||
| "changeOpacity"
|
||||
| "changeFontSize"
|
||||
| "toggleCanvasMenu"
|
||||
|
@ -172,7 +172,7 @@ export const SelectedShapeActions = ({
|
||||
targetElements.some((element) => element.type === "freedraw")) && (
|
||||
<>
|
||||
{renderAction("changeStrokeShape")}
|
||||
{renderAction("changePressureSensitivity")}
|
||||
{renderAction("changeStrokeType")}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
@ -2269,3 +2269,71 @@ export const elementLinkIcon = createIcon(
|
||||
</g>,
|
||||
tablerIconProps,
|
||||
);
|
||||
|
||||
export const strokeWidthFixedIcon = createIcon(
|
||||
<g>
|
||||
<path
|
||||
d="M4 12 C 5 8, 6 8, 8 12"
|
||||
fill="none"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M8 12 C 9 16, 10 16, 12 12"
|
||||
fill="none"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12 12 C 14 8, 15 8, 16 12"
|
||||
fill="none"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M16 12 C 17 16, 18 16, 19 12"
|
||||
fill="none"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>,
|
||||
tablerIconProps,
|
||||
);
|
||||
|
||||
export const strokeWidthVariableIcon = createIcon(
|
||||
<g>
|
||||
<path
|
||||
d="M4 12 C 5 8, 6 8, 8 12"
|
||||
fill="none"
|
||||
strokeWidth="3.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M8 12 C 9 16, 10 16, 12 12"
|
||||
fill="none"
|
||||
strokeWidth="2.75"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12 12 C 14 8, 15 8, 16 12"
|
||||
fill="none"
|
||||
strokeWidth="2.25"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M16 12 C 17 16, 18 16, 19 12"
|
||||
fill="none"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>,
|
||||
tablerIconProps,
|
||||
);
|
||||
|
@ -32,7 +32,9 @@
|
||||
"strokeStyle_dotted": "Dotted",
|
||||
"sloppiness": "Sloppiness",
|
||||
"opacity": "Opacity",
|
||||
"pressureSensitivity": "Stroke sensitivity",
|
||||
"strokeType": "Stroke Type",
|
||||
"strokeWidthFixed": "Fixed width",
|
||||
"strokeWidthVariable": "Variable width",
|
||||
"textAlign": "Text align",
|
||||
"edges": "Edges",
|
||||
"sharp": "Sharp",
|
||||
|
Reference in New Issue
Block a user