Compare commits

...

5 Commits

Author SHA1 Message Date
zsviczian
e225a86f9f
Update renderElement.ts 2023-01-05 16:23:27 +01:00
zsviczian
d469fe26e2 added library of easing functions 2023-01-05 15:17:36 +01:00
zsviczian
a9c3784f98
Update renderElement.ts 2023-01-05 09:44:29 +01:00
zsviczian
0604542cfb
Update renderElement.ts 2023-01-05 09:15:35 +01:00
zsviczian
a82ce8ce5a
Update renderElement.ts 2023-01-04 20:29:11 +01:00
2 changed files with 216 additions and 11 deletions

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

View File

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