mirror of
https://github.com/slidevjs/rough-notation.git
synced 2026-01-20 20:21:35 +01:00
chore: apply eslint
This commit is contained in:
299
src/render.ts
299
src/render.ts
@@ -1,22 +1,21 @@
|
||||
import type { OpSet, ResolvedOptions } from 'roughjs/bin/core'
|
||||
import { ellipse, line, linearPath, rectangle } from 'roughjs/bin/renderer'
|
||||
import { RoughGenerator } from 'roughjs/bin/generator'
|
||||
import type { Point } from 'roughjs/bin/geometry'
|
||||
import type { BracketType, FullPadding, Rect, RoughAnnotationConfig } from './types'
|
||||
import { SVG_NS } from './constants'
|
||||
import { Rect, RoughAnnotationConfig, FullPadding, BracketType } from "./types";
|
||||
import { ResolvedOptions, OpSet } from 'roughjs/bin/core';
|
||||
import { line, rectangle, ellipse, linearPath } from 'roughjs/bin/renderer';
|
||||
import { RoughGenerator } from 'roughjs/bin/generator';
|
||||
import { Point } from 'roughjs/bin/geometry';
|
||||
|
||||
type RoughOptionsType = 'highlight' | 'single' | 'double';
|
||||
type RoughOptionsType = 'highlight' | 'single' | 'double'
|
||||
|
||||
let defaultOptions: ResolvedOptions | null = null;
|
||||
let defaultOptions: ResolvedOptions | null = null
|
||||
function getDefaultOptions(): ResolvedOptions {
|
||||
if (!defaultOptions) {
|
||||
const gen = new RoughGenerator();
|
||||
defaultOptions = gen.defaultOptions;
|
||||
const gen = new RoughGenerator()
|
||||
defaultOptions = gen.defaultOptions
|
||||
}
|
||||
return defaultOptions;
|
||||
return defaultOptions
|
||||
}
|
||||
|
||||
|
||||
function getOptions(type: RoughOptionsType, seed: number): ResolvedOptions {
|
||||
return {
|
||||
...getDefaultOptions(),
|
||||
@@ -38,241 +37,235 @@ function getOptions(type: RoughOptionsType, seed: number): ResolvedOptions {
|
||||
// combineNestedSvgPaths: false,
|
||||
disableMultiStroke: type !== 'double',
|
||||
disableMultiStrokeFill: false,
|
||||
seed
|
||||
};
|
||||
seed,
|
||||
}
|
||||
}
|
||||
|
||||
function parsePadding(config: RoughAnnotationConfig): FullPadding {
|
||||
const p = config.padding;
|
||||
const p = config.padding
|
||||
if (p || (p === 0)) {
|
||||
if (typeof p === 'number') {
|
||||
return [p, p, p, p];
|
||||
} else if (Array.isArray(p)) {
|
||||
const pa = p as number[];
|
||||
return [p, p, p, p]
|
||||
}
|
||||
else if (Array.isArray(p)) {
|
||||
const pa = p as number[]
|
||||
if (pa.length) {
|
||||
switch (pa.length) {
|
||||
case 4:
|
||||
return [...pa] as FullPadding;
|
||||
return [...pa] as FullPadding
|
||||
case 1:
|
||||
return [pa[0], pa[0], pa[0], pa[0]];
|
||||
return [pa[0], pa[0], pa[0], pa[0]]
|
||||
case 2:
|
||||
return [...pa, ...pa] as FullPadding;
|
||||
return [...pa, ...pa] as FullPadding
|
||||
case 3:
|
||||
return [...pa, pa[1]] as FullPadding;
|
||||
return [...pa, pa[1]] as FullPadding
|
||||
default:
|
||||
return [pa[0], pa[1], pa[2], pa[3]];
|
||||
return [pa[0], pa[1], pa[2], pa[3]]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [5, 5, 5, 5];
|
||||
return [5, 5, 5, 5]
|
||||
}
|
||||
|
||||
export function renderAnnotation(svg: SVGSVGElement, rect: Rect, config: RoughAnnotationConfig, animationGroupDelay: number, animationDuration: number, seed: number) {
|
||||
const opList: OpSet[] = [];
|
||||
let strokeWidth = config.strokeWidth || 2;
|
||||
const padding = parsePadding(config);
|
||||
const animate = (config.animate === undefined) ? true : (!!config.animate);
|
||||
const iterations = config.iterations || 2;
|
||||
const rtl = config.rtl ? 1 : 0;
|
||||
const o = getOptions('single', seed);
|
||||
const opList: OpSet[] = []
|
||||
let strokeWidth = config.strokeWidth || 2
|
||||
const padding = parsePadding(config)
|
||||
const animate = (config.animate === undefined) ? true : (!!config.animate)
|
||||
const iterations = config.iterations || 2
|
||||
const rtl = config.rtl ? 1 : 0
|
||||
const o = getOptions('single', seed)
|
||||
|
||||
switch (config.type) {
|
||||
case 'underline': {
|
||||
const y = rect.y + rect.h + padding[2];
|
||||
const y = rect.y + rect.h + padding[2]
|
||||
for (let i = rtl; i < iterations + rtl; i++) {
|
||||
if (i % 2) {
|
||||
opList.push(line(rect.x + rect.w, y, rect.x, y, o));
|
||||
} else {
|
||||
opList.push(line(rect.x, y, rect.x + rect.w, y, o));
|
||||
}
|
||||
if (i % 2)
|
||||
opList.push(line(rect.x + rect.w, y, rect.x, y, o))
|
||||
else
|
||||
opList.push(line(rect.x, y, rect.x + rect.w, y, o))
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
case 'strike-through': {
|
||||
const y = rect.y + (rect.h / 2);
|
||||
const y = rect.y + (rect.h / 2)
|
||||
for (let i = rtl; i < iterations + rtl; i++) {
|
||||
if (i % 2) {
|
||||
opList.push(line(rect.x + rect.w, y, rect.x, y, o));
|
||||
} else {
|
||||
opList.push(line(rect.x, y, rect.x + rect.w, y, o));
|
||||
}
|
||||
if (i % 2)
|
||||
opList.push(line(rect.x + rect.w, y, rect.x, y, o))
|
||||
else
|
||||
opList.push(line(rect.x, y, rect.x + rect.w, y, o))
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
case 'box': {
|
||||
const x = rect.x - padding[3];
|
||||
const y = rect.y - padding[0];
|
||||
const width = rect.w + (padding[1] + padding[3]);
|
||||
const height = rect.h + (padding[0] + padding[2]);
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
opList.push(rectangle(x, y, width, height, o));
|
||||
}
|
||||
break;
|
||||
const x = rect.x - padding[3]
|
||||
const y = rect.y - padding[0]
|
||||
const width = rect.w + (padding[1] + padding[3])
|
||||
const height = rect.h + (padding[0] + padding[2])
|
||||
for (let i = 0; i < iterations; i++)
|
||||
opList.push(rectangle(x, y, width, height, o))
|
||||
|
||||
break
|
||||
}
|
||||
case 'bracket': {
|
||||
const brackets: BracketType[] = Array.isArray(config.brackets) ? config.brackets : (config.brackets ? [config.brackets] : ['right']);
|
||||
const lx = rect.x - padding[3] * 2;
|
||||
const rx = rect.x + rect.w + padding[1] * 2;
|
||||
const ty = rect.y - padding[0] * 2;
|
||||
const by = rect.y + rect.h + padding[2] * 2;
|
||||
const brackets: BracketType[] = Array.isArray(config.brackets) ? config.brackets : (config.brackets ? [config.brackets] : ['right'])
|
||||
const lx = rect.x - padding[3] * 2
|
||||
const rx = rect.x + rect.w + padding[1] * 2
|
||||
const ty = rect.y - padding[0] * 2
|
||||
const by = rect.y + rect.h + padding[2] * 2
|
||||
for (const br of brackets) {
|
||||
let points: Point[];
|
||||
let points: Point[]
|
||||
switch (br) {
|
||||
case 'bottom':
|
||||
points = [
|
||||
[lx, rect.y + rect.h],
|
||||
[lx, by],
|
||||
[rx, by],
|
||||
[rx, rect.y + rect.h]
|
||||
];
|
||||
break;
|
||||
[rx, rect.y + rect.h],
|
||||
]
|
||||
break
|
||||
case 'top':
|
||||
points = [
|
||||
[lx, rect.y],
|
||||
[lx, ty],
|
||||
[rx, ty],
|
||||
[rx, rect.y]
|
||||
];
|
||||
break;
|
||||
[rx, rect.y],
|
||||
]
|
||||
break
|
||||
case 'left':
|
||||
points = [
|
||||
[rect.x, ty],
|
||||
[lx, ty],
|
||||
[lx, by],
|
||||
[rect.x, by]
|
||||
];
|
||||
break;
|
||||
[rect.x, by],
|
||||
]
|
||||
break
|
||||
case 'right':
|
||||
points = [
|
||||
[rect.x + rect.w, ty],
|
||||
[rx, ty],
|
||||
[rx, by],
|
||||
[rect.x + rect.w, by]
|
||||
];
|
||||
break;
|
||||
}
|
||||
if (points) {
|
||||
opList.push(linearPath(points, false, o));
|
||||
[rect.x + rect.w, by],
|
||||
]
|
||||
break
|
||||
}
|
||||
if (points)
|
||||
opList.push(linearPath(points, false, o))
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
case 'crossed-off': {
|
||||
const x = rect.x;
|
||||
const y = rect.y;
|
||||
const x2 = x + rect.w;
|
||||
const y2 = y + rect.h;
|
||||
const x = rect.x
|
||||
const y = rect.y
|
||||
const x2 = x + rect.w
|
||||
const y2 = y + rect.h
|
||||
for (let i = rtl; i < iterations + rtl; i++) {
|
||||
if (i % 2) {
|
||||
opList.push(line(x2, y2, x, y, o));
|
||||
} else {
|
||||
opList.push(line(x, y, x2, y2, o));
|
||||
}
|
||||
if (i % 2)
|
||||
opList.push(line(x2, y2, x, y, o))
|
||||
else
|
||||
opList.push(line(x, y, x2, y2, o))
|
||||
}
|
||||
for (let i = rtl; i < iterations + rtl; i++) {
|
||||
if (i % 2) {
|
||||
opList.push(line(x, y2, x2, y, o));
|
||||
} else {
|
||||
opList.push(line(x2, y, x, y2, o));
|
||||
}
|
||||
if (i % 2)
|
||||
opList.push(line(x, y2, x2, y, o))
|
||||
else
|
||||
opList.push(line(x2, y, x, y2, o))
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
case 'circle': {
|
||||
const doubleO = getOptions('double', seed);
|
||||
const width = rect.w + (padding[1] + padding[3]);
|
||||
const height = rect.h + (padding[0] + padding[2]);
|
||||
const x = rect.x - padding[3] + (width / 2);
|
||||
const y = rect.y - padding[0] + (height / 2);
|
||||
const fullItr = Math.floor(iterations / 2);
|
||||
const singleItr = iterations - (fullItr * 2);
|
||||
for (let i = 0; i < fullItr; i++) {
|
||||
opList.push(ellipse(x, y, width, height, doubleO));
|
||||
}
|
||||
for (let i = 0; i < singleItr; i++) {
|
||||
opList.push(ellipse(x, y, width, height, o));
|
||||
}
|
||||
break;
|
||||
const doubleO = getOptions('double', seed)
|
||||
const width = rect.w + (padding[1] + padding[3])
|
||||
const height = rect.h + (padding[0] + padding[2])
|
||||
const x = rect.x - padding[3] + (width / 2)
|
||||
const y = rect.y - padding[0] + (height / 2)
|
||||
const fullItr = Math.floor(iterations / 2)
|
||||
const singleItr = iterations - (fullItr * 2)
|
||||
for (let i = 0; i < fullItr; i++)
|
||||
opList.push(ellipse(x, y, width, height, doubleO))
|
||||
|
||||
for (let i = 0; i < singleItr; i++)
|
||||
opList.push(ellipse(x, y, width, height, o))
|
||||
|
||||
break
|
||||
}
|
||||
case 'highlight': {
|
||||
const o = getOptions('highlight', seed);
|
||||
strokeWidth = rect.h * 0.95;
|
||||
const y = rect.y + (rect.h / 2);
|
||||
const o = getOptions('highlight', seed)
|
||||
strokeWidth = rect.h * 0.95
|
||||
const y = rect.y + (rect.h / 2)
|
||||
for (let i = rtl; i < iterations + rtl; i++) {
|
||||
if (i % 2) {
|
||||
opList.push(line(rect.x + rect.w, y, rect.x, y, o));
|
||||
} else {
|
||||
opList.push(line(rect.x, y, rect.x + rect.w, y, o));
|
||||
}
|
||||
if (i % 2)
|
||||
opList.push(line(rect.x + rect.w, y, rect.x, y, o))
|
||||
else
|
||||
opList.push(line(rect.x, y, rect.x + rect.w, y, o))
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (opList.length) {
|
||||
const pathStrings = opsToPath(opList);
|
||||
const lengths: number[] = [];
|
||||
const pathElements: SVGPathElement[] = [];
|
||||
let totalLength = 0;
|
||||
const setAttr = (p: SVGPathElement, an: string, av: string) => p.setAttribute(an, av);
|
||||
const pathStrings = opsToPath(opList)
|
||||
const lengths: number[] = []
|
||||
const pathElements: SVGPathElement[] = []
|
||||
let totalLength = 0
|
||||
const setAttr = (p: SVGPathElement, an: string, av: string) => p.setAttribute(an, av)
|
||||
|
||||
for (const d of pathStrings) {
|
||||
const path = document.createElementNS(SVG_NS, 'path');
|
||||
setAttr(path, 'd', d);
|
||||
setAttr(path, 'fill', 'none');
|
||||
setAttr(path, 'stroke', config.color || 'currentColor');
|
||||
setAttr(path, 'stroke-width', `${strokeWidth}`);
|
||||
const path = document.createElementNS(SVG_NS, 'path')
|
||||
setAttr(path, 'd', d)
|
||||
setAttr(path, 'fill', 'none')
|
||||
setAttr(path, 'stroke', config.color || 'currentColor')
|
||||
setAttr(path, 'stroke-width', `${strokeWidth}`)
|
||||
if (animate) {
|
||||
const length = path.getTotalLength();
|
||||
lengths.push(length);
|
||||
totalLength += length;
|
||||
const length = path.getTotalLength()
|
||||
lengths.push(length)
|
||||
totalLength += length
|
||||
}
|
||||
svg.appendChild(path);
|
||||
pathElements.push(path);
|
||||
svg.appendChild(path)
|
||||
pathElements.push(path)
|
||||
}
|
||||
|
||||
if (animate) {
|
||||
let durationOffset = 0;
|
||||
let durationOffset = 0
|
||||
for (let i = 0; i < pathElements.length; i++) {
|
||||
const path = pathElements[i];
|
||||
const length = lengths[i];
|
||||
const duration = totalLength ? (animationDuration * (length / totalLength)) : 0;
|
||||
const delay = animationGroupDelay + durationOffset;
|
||||
const style = path.style;
|
||||
style.strokeDashoffset = `${length}`;
|
||||
style.strokeDasharray = `${length}`;
|
||||
style.animation = `rough-notation-dash ${duration}ms ease-out ${delay}ms forwards`;
|
||||
durationOffset += duration;
|
||||
const path = pathElements[i]
|
||||
const length = lengths[i]
|
||||
const duration = totalLength ? (animationDuration * (length / totalLength)) : 0
|
||||
const delay = animationGroupDelay + durationOffset
|
||||
const style = path.style
|
||||
style.strokeDashoffset = `${length}`
|
||||
style.strokeDasharray = `${length}`
|
||||
style.animation = `rough-notation-dash ${duration}ms ease-out ${delay}ms forwards`
|
||||
durationOffset += duration
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function opsToPath(opList: OpSet[]): string[] {
|
||||
const paths: string[] = [];
|
||||
const paths: string[] = []
|
||||
for (const drawing of opList) {
|
||||
let path = '';
|
||||
let path = ''
|
||||
for (const item of drawing.ops) {
|
||||
const data = item.data;
|
||||
const data = item.data
|
||||
switch (item.op) {
|
||||
case 'move':
|
||||
if (path.trim()) {
|
||||
paths.push(path.trim());
|
||||
}
|
||||
path = `M${data[0]} ${data[1]} `;
|
||||
break;
|
||||
if (path.trim())
|
||||
paths.push(path.trim())
|
||||
|
||||
path = `M${data[0]} ${data[1]} `
|
||||
break
|
||||
case 'bcurveTo':
|
||||
path += `C${data[0]} ${data[1]}, ${data[2]} ${data[3]}, ${data[4]} ${data[5]} `;
|
||||
break;
|
||||
path += `C${data[0]} ${data[1]}, ${data[2]} ${data[3]}, ${data[4]} ${data[5]} `
|
||||
break
|
||||
case 'lineTo':
|
||||
path += `L${data[0]} ${data[1]} `;
|
||||
break;
|
||||
path += `L${data[0]} ${data[1]} `
|
||||
break
|
||||
}
|
||||
}
|
||||
if (path.trim()) {
|
||||
paths.push(path.trim());
|
||||
}
|
||||
if (path.trim())
|
||||
paths.push(path.trim())
|
||||
}
|
||||
return paths;
|
||||
return paths
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user