Compare commits
5 Commits
master
...
zsviczian-
Author | SHA1 | Date | |
---|---|---|---|
|
e225a86f9f | ||
|
d469fe26e2 | ||
|
a9c3784f98 | ||
|
0604542cfb | ||
|
a82ce8ce5a |
151
src/renderer/easingFunctions.ts
Normal file
151
src/renderer/easingFunctions.ts
Normal file
@ -0,0 +1,151 @@
|
||||
//https://github.com/ai/easings.net/blob/master/src/easings/easingsFunctions.ts
|
||||
type EasingFunction = (progress: number) => number;
|
||||
|
||||
interface EasingDictionary {
|
||||
[easing: string]: EasingFunction;
|
||||
}
|
||||
|
||||
const pow = Math.pow;
|
||||
const sqrt = Math.sqrt;
|
||||
const sin = Math.sin;
|
||||
const cos = Math.cos;
|
||||
const PI = Math.PI;
|
||||
const c1 = 1.70158;
|
||||
const c2 = c1 * 1.525;
|
||||
const c3 = c1 + 1;
|
||||
const c4 = (2 * PI) / 3;
|
||||
const c5 = (2 * PI) / 4.5;
|
||||
|
||||
const bounceOut: EasingFunction = function (x) {
|
||||
const n1 = 7.5625;
|
||||
const d1 = 2.75;
|
||||
|
||||
if (x < 1 / d1) {
|
||||
return n1 * x * x;
|
||||
} else if (x < 2 / d1) {
|
||||
return n1 * (x -= 1.5 / d1) * x + 0.75;
|
||||
} else if (x < 2.5 / d1) {
|
||||
return n1 * (x -= 2.25 / d1) * x + 0.9375;
|
||||
}
|
||||
return n1 * (x -= 2.625 / d1) * x + 0.984375;
|
||||
};
|
||||
|
||||
const easingsFunctions: EasingDictionary = {
|
||||
linear: (x) => x,
|
||||
easeInQuad(x) {
|
||||
return x * x;
|
||||
},
|
||||
easeOutQuad(x) {
|
||||
return 1 - (1 - x) * (1 - x);
|
||||
},
|
||||
easeInOutQuad(x) {
|
||||
return x < 0.5 ? 2 * x * x : 1 - pow(-2 * x + 2, 2) / 2;
|
||||
},
|
||||
easeInCubic(x) {
|
||||
return x * x * x;
|
||||
},
|
||||
easeOutCubic(x) {
|
||||
return 1 - pow(1 - x, 3);
|
||||
},
|
||||
easeInOutCubic(x) {
|
||||
return x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2, 3) / 2;
|
||||
},
|
||||
easeInQuart(x) {
|
||||
return x * x * x * x;
|
||||
},
|
||||
easeOutQuart(x) {
|
||||
return 1 - pow(1 - x, 4);
|
||||
},
|
||||
easeInOutQuart(x) {
|
||||
return x < 0.5 ? 8 * x * x * x * x : 1 - pow(-2 * x + 2, 4) / 2;
|
||||
},
|
||||
easeInQuint(x) {
|
||||
return x * x * x * x * x;
|
||||
},
|
||||
easeOutQuint(x) {
|
||||
return 1 - pow(1 - x, 5);
|
||||
},
|
||||
easeInOutQuint(x) {
|
||||
return x < 0.5 ? 16 * x * x * x * x * x : 1 - pow(-2 * x + 2, 5) / 2;
|
||||
},
|
||||
easeInSine(x) {
|
||||
return 1 - cos((x * PI) / 2);
|
||||
},
|
||||
easeOutSine(x) {
|
||||
return sin((x * PI) / 2);
|
||||
},
|
||||
easeInOutSine(x) {
|
||||
return -(cos(PI * x) - 1) / 2;
|
||||
},
|
||||
easeInExpo(x) {
|
||||
return x === 0 ? 0 : pow(2, 10 * x - 10);
|
||||
},
|
||||
easeOutExpo(x) {
|
||||
return x === 1 ? 1 : 1 - pow(2, -10 * x);
|
||||
},
|
||||
easeInOutExpo(x) {
|
||||
return x === 0
|
||||
? 0
|
||||
: x === 1
|
||||
? 1
|
||||
: x < 0.5
|
||||
? pow(2, 20 * x - 10) / 2
|
||||
: (2 - pow(2, -20 * x + 10)) / 2;
|
||||
},
|
||||
easeInCirc(x) {
|
||||
return 1 - sqrt(1 - pow(x, 2));
|
||||
},
|
||||
easeOutCirc(x) {
|
||||
return sqrt(1 - pow(x - 1, 2));
|
||||
},
|
||||
easeInOutCirc(x) {
|
||||
return x < 0.5
|
||||
? (1 - sqrt(1 - pow(2 * x, 2))) / 2
|
||||
: (sqrt(1 - pow(-2 * x + 2, 2)) + 1) / 2;
|
||||
},
|
||||
easeInBack(x) {
|
||||
return c3 * x * x * x - c1 * x * x;
|
||||
},
|
||||
easeOutBack(x) {
|
||||
return 1 + c3 * pow(x - 1, 3) + c1 * pow(x - 1, 2);
|
||||
},
|
||||
easeInOutBack(x) {
|
||||
return x < 0.5
|
||||
? (pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2
|
||||
: (pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
|
||||
},
|
||||
easeInElastic(x) {
|
||||
return x === 0
|
||||
? 0
|
||||
: x === 1
|
||||
? 1
|
||||
: -pow(2, 10 * x - 10) * sin((x * 10 - 10.75) * c4);
|
||||
},
|
||||
easeOutElastic(x) {
|
||||
return x === 0
|
||||
? 0
|
||||
: x === 1
|
||||
? 1
|
||||
: pow(2, -10 * x) * sin((x * 10 - 0.75) * c4) + 1;
|
||||
},
|
||||
easeInOutElastic(x) {
|
||||
return x === 0
|
||||
? 0
|
||||
: x === 1
|
||||
? 1
|
||||
: x < 0.5
|
||||
? -(pow(2, 20 * x - 10) * sin((20 * x - 11.125) * c5)) / 2
|
||||
: (pow(2, -20 * x + 10) * sin((20 * x - 11.125) * c5)) / 2 + 1;
|
||||
},
|
||||
easeInBounce(x) {
|
||||
return 1 - bounceOut(1 - x);
|
||||
},
|
||||
easeOutBounce: bounceOut,
|
||||
easeInOutBounce(x) {
|
||||
return x < 0.5
|
||||
? (1 - bounceOut(1 - 2 * x)) / 2
|
||||
: (1 + bounceOut(2 * x - 1)) / 2;
|
||||
},
|
||||
};
|
||||
|
||||
export default easingsFunctions;
|
@ -46,6 +46,7 @@ import {
|
||||
getContainerElement,
|
||||
} from "../element/textElement";
|
||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||
import easingsFunctions from "./easingFunctions";
|
||||
|
||||
// using a stronger invert (100% vs our regular 93%) and saturate
|
||||
// as a temp hack to make images in dark theme look closer to original
|
||||
@ -237,7 +238,16 @@ const drawElementOnCanvas = (
|
||||
rc.draw(fillShape);
|
||||
}
|
||||
|
||||
context.fillStyle = element.strokeColor;
|
||||
if (element.customData?.strokeOptions?.hasOutline) {
|
||||
context.lineWidth =
|
||||
element.strokeWidth *
|
||||
(element.customData.strokeOptions.outlineWidth ?? 1);
|
||||
context.strokeStyle = element.strokeColor;
|
||||
context.stroke(path);
|
||||
context.fillStyle = element.backgroundColor;
|
||||
} else {
|
||||
context.fillStyle = element.strokeColor;
|
||||
}
|
||||
context.fill(path);
|
||||
|
||||
context.restore();
|
||||
@ -1185,7 +1195,23 @@ export const renderElementToSvg = (
|
||||
);
|
||||
node.setAttribute("stroke", "none");
|
||||
const path = svgRoot.ownerDocument!.createElementNS(SVG_NS, "path");
|
||||
path.setAttribute("fill", element.strokeColor);
|
||||
if (element.customData?.strokeOptions?.hasOutline) {
|
||||
node.setAttribute(
|
||||
"stroke-width",
|
||||
`${
|
||||
element.strokeWidth *
|
||||
(element.customData.strokeOptions.outlineWidth ?? 1)
|
||||
}`,
|
||||
);
|
||||
const outline = svgRoot.ownerDocument!.createElementNS(SVG_NS, "path");
|
||||
outline.setAttribute("fill", "none");
|
||||
outline.setAttribute("stroke", element.strokeColor);
|
||||
outline.setAttribute("d", getFreeDrawSvgPath(element));
|
||||
node.appendChild(outline);
|
||||
path.setAttribute("fill", element.backgroundColor);
|
||||
} else {
|
||||
path.setAttribute("fill", element.strokeColor);
|
||||
}
|
||||
path.setAttribute("d", getFreeDrawSvgPath(element));
|
||||
node.appendChild(path);
|
||||
root.appendChild(node);
|
||||
@ -1328,15 +1354,43 @@ export function getFreeDrawSvgPath(element: ExcalidrawFreeDrawElement) {
|
||||
: [[0, 0, 0.5]];
|
||||
|
||||
// Consider changing the options for simulated pressure vs real pressure
|
||||
const options: StrokeOptions = {
|
||||
simulatePressure: element.simulatePressure,
|
||||
size: element.strokeWidth * 4.25,
|
||||
thinning: 0.6,
|
||||
smoothing: 0.5,
|
||||
streamline: 0.5,
|
||||
easing: (t) => Math.sin((t * Math.PI) / 2), // https://easings.net/#easeOutSine
|
||||
last: !!element.lastCommittedPoint, // LastCommittedPoint is added on pointerup
|
||||
};
|
||||
const customOptions = element.customData?.strokeOptions?.options;
|
||||
const options: StrokeOptions = customOptions
|
||||
? {
|
||||
...customOptions,
|
||||
simulatePressure:
|
||||
customOptions.simulatePressure ?? element.simulatePressure,
|
||||
size: element.strokeWidth * 4.25, //override size with stroke width
|
||||
last: !!element.lastCommittedPoint,
|
||||
easing: easingsFunctions[customOptions.easing] ?? ((t) => t),
|
||||
...(customOptions.start?.easing
|
||||
? {
|
||||
start: {
|
||||
...customOptions.start,
|
||||
easing:
|
||||
easingsFunctions[customOptions.start.easing] ?? ((t) => t),
|
||||
},
|
||||
}
|
||||
: { start: customOptions.start }),
|
||||
...(customOptions.end?.easing
|
||||
? {
|
||||
end: {
|
||||
...customOptions.end,
|
||||
easing:
|
||||
easingsFunctions[customOptions.end.easing] ?? ((t) => t),
|
||||
},
|
||||
}
|
||||
: { end: customOptions.end }),
|
||||
}
|
||||
: {
|
||||
simulatePressure: element.simulatePressure,
|
||||
size: element.strokeWidth * 4.25,
|
||||
thinning: 0.6,
|
||||
smoothing: 0.5,
|
||||
streamline: 0.5,
|
||||
easing: easingsFunctions.easeOutSine,
|
||||
last: !!element.lastCommittedPoint, // LastCommittedPoint is added on pointerup
|
||||
};
|
||||
|
||||
return getSvgPathFromStroke(getStroke(inputPoints as number[][], options));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user