Skip to content

Commit

Permalink
Added arrow element
Browse files Browse the repository at this point in the history
  • Loading branch information
graphieros committed Jan 21, 2024
1 parent b974320 commit 9893bb4
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 154 deletions.
20 changes: 19 additions & 1 deletion playground/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { chartXy, radialGradient, svg, circle, chartDonut, chartGauge, clipPath, path, use, findArcMidpoint } from "savyg";
import { chartXy, arrow, chartDonut, chartGauge, clipPath, path, use, findArcMidpoint } from "savyg";

// const parent = document.getElementById("svg") as HTMLElement
const div = document.getElementById("div") as HTMLElement
Expand Down Expand Up @@ -35,6 +35,7 @@ chartGauge({
},
parent: div
})

let gauge = chartGauge({
dataset: {
value: 4.56,
Expand All @@ -59,6 +60,23 @@ let gauge = chartGauge({
parent: div
})

arrow({
options: {
x1: 10,
y1: 10,
x2: 60,
y2: 100,
marker: "both",
stroke: "black",
"stroke-linecap": "round",
"stroke-width": 1,
size: 12
},
parent: gauge.chart
})



let xy = chartXy({
dataset: [
{
Expand Down
2 changes: 1 addition & 1 deletion savyg/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "savyg",
"private": false,
"version": "1.1.6",
"version": "1.1.7",
"description": "A savvy library to create svg elements and charts with ease",
"author": "Alec Lloyd Probert",
"repository": {
Expand Down
58 changes: 0 additions & 58 deletions savyg/src/utils_common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { BaseDatasetItem } from "./utils_chart_xy";
import { DrawingArea } from "./utils_svg_types";


export function getSvgDimensions(viewBox: string) {
const dimensions = viewBox.split(' ');
Expand Down Expand Up @@ -249,76 +247,20 @@ export function makeDonut({
return ratios;
}

export function createDonutMarker({ drawingArea, element, offset }: { drawingArea: DrawingArea, element: any, offset: number }) {
const dx = drawingArea.centerX - element.center.endX;
const dy = drawingArea.centerY - element.center.endY;

const length = Math.sqrt(dx * dx + dy * dy);
const endX = element.center.endX + (dx / length) * (length - offset);
const endY = element.center.endY + (dy / length) * (length - offset);

return {
x1: element.center.endX,
y1: element.center.endY,
x2: endX,
y2: endY
}
}

export function positionDonutLabel({ drawingArea, element, offset = 0 }: { drawingArea: DrawingArea, element: any, offset?: number }) {
let position = {
x: element.center.endX,
y: element.center.endY + offset,
textAnchor: "middle"
};

if (element.center.endX - 12 > drawingArea.centerX) {
position.textAnchor = "start";
position.x += 12;
}

if (element.center.endX + 12 < drawingArea.centerX) {
position.textAnchor = "end";
position.x -= 12;
}

if (element.center.endX === drawingArea.centerX) {
position.textAnchor = "middle";
if (element.center.endY > drawingArea.centerY) {
position.y += 12;
}
if (element.center.endY < drawingArea.centerY) {
position.y -= 12;
}
}

if (element.center.endY - 6 < (drawingArea.top + drawingArea.height / 4)) {
position.y = createDonutMarker({ drawingArea, element, offset: drawingArea.width / 3 }).y2 + offset;
}

if (element.center.endY + 6 > drawingArea.height - (drawingArea.height / 4)) {
position.y = createDonutMarker({ drawingArea, element, offset: drawingArea.width / 3 }).y2 + offset;
}

return position;
}

export function fordinum(n: number, r: number = 0, s: string = '', p: string = ''): string {
if (isNaN(n)) return n as unknown as string
return p + (Number(n).toFixed(r)).toLocaleString() + s
}

const utils_commons = {
calculateNiceScale,
createDonutMarker,
createUid,
fordinum,
getClosestDecimal,
getMaxSerieLength,
getMinMaxInDatasetItems,
getSvgDimensions,
makeDonut,
positionDonutLabel,
ratioToMax,
}

Expand Down
113 changes: 113 additions & 0 deletions savyg/src/utils_svg.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Coordinates, GradientStop, Shape, SvgItem, SvgOptions } from "./utils_svg_types"
import { CONSTANT } from "./constants"
import { createUid } from "./utils_common";

/**
*
Expand Down Expand Up @@ -171,6 +172,117 @@ export function text(attrs: {
})
}

/**
*
* @description Creates a svg marker element.
* @returns a marker svg element
*/
export function marker(attrs: {
options: SvgOptions[SvgItem.MARKER],
parent?: SVGElement | HTMLElement,
}) {
return element({
el: SvgItem.MARKER,
options: attrs.options,
parent: attrs.parent
})
}

export function arrow(attrs: {
options: SvgOptions[SvgItem.ARROW],
parent?: SVGElement | HTMLElement
}) {
const uid = createUid();

const g = element({
el: SvgItem.G,
options: {
className: "savyg-arrow"
},
})

const defs = element({
el: SvgItem.DEFS,
options: {},
parent: g
})
const baseSize = attrs.options.size ?? 10;
const viewBox = `0 0 ${baseSize} ${baseSize}`
const refX = baseSize / 2
const refY = baseSize / 2
const markerWidth = refX * 1.2
const markerHeight = refY * 1.2

if (['end', 'both'].includes(attrs.options.marker)) {
const markerEnd = marker({
options: {
id: `marker-end-${uid}`,
orient: "auto",
viewBox,
refX,
refY,
markerHeight: attrs.options.markerHeight ?? markerHeight,
markerWidth: attrs.options.markerWidth ?? markerWidth
},
parent: defs
})

path({
options: {
d: `M 0 0 L ${baseSize} ${refX} L 0 ${baseSize} z`,
fill: attrs.options.stroke
},
parent: markerEnd
})
}
if (['start', 'both'].includes(attrs.options.marker)) {
const markerStart = marker({
options: {
id: `marker-start-${uid}`,
orient: "auto-start-reverse",
viewBox,
refX,
refY,
markerHeight: attrs.options.markerHeight ?? markerHeight,
markerWidth: attrs.options.markerWidth ?? markerWidth
},
parent: defs
})
path({
options: {
d: `M 0 0 L ${baseSize} ${refX} L 0 ${baseSize} z`,
fill: attrs.options.stroke
},
parent: markerStart
})
}

line({
options: {
x1: attrs.options.x1,
y1: attrs.options.y1,
x2: attrs.options.x2,
y2: attrs.options.y2,
stroke: attrs.options.stroke,
"stroke-width": attrs.options["stroke-width"],
"stroke-dasharray": attrs.options["stroke-dasharray"],
"stroke-dashoffset": attrs.options["stroke-dashoffset"],
"stroke-linecap": attrs.options["stroke-linecap"],
"stroke-linejoin": attrs.options["stroke-linejoin"],
"shape-rendering": attrs.options["shape-rendering"],
"marker-end": ['end', 'both'].includes(attrs.options.marker) ? `url(#marker-end-${uid})` : '',
"marker-start": ['start', 'both'].includes(attrs.options.marker) ? `url(#marker-start-${uid})` : '',
},
parent: g
})

if (attrs.parent) {
attrs.parent.appendChild(g)
}

return g
}

export function calcPolygonPoints({
centerX,
centerY,
Expand Down Expand Up @@ -408,6 +520,7 @@ const utils_svg = {
freePolygon,
line,
linearGradient,
marker,
offsetFromCenterPoint,
path,
radialGradient,
Expand Down
41 changes: 40 additions & 1 deletion savyg/src/utils_svg_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export enum SvgItem {
TEXT = "text",
CLIP_PATH = "clipPath",
USE = "use",
MARKER = "marker",
ARROW = "arrow"
}

export type ShapeRendering = "auto" | "optimizeSpeed" | "crispEdges" | "geometricPrecision"
Expand Down Expand Up @@ -55,7 +57,7 @@ export type DrawingArea = {
centerY: number;
}

export type Shape = "circle" | "defs" | "g" | "line" | "linearGradient" | "radialGradient" | "path" | "polygon" | "rect" | "stop" | "svg" | "foreignObject" | "text" | "clipPath" | "use"
export type Shape = "circle" | "defs" | "g" | "line" | "linearGradient" | "radialGradient" | "path" | "polygon" | "rect" | "stop" | "svg" | "foreignObject" | "text" | "clipPath" | "use" | "marker"

export type StrokeLinecap = "round" | "butt" | "square"
export type StrokeLinejoin = "arcs" | "bevel" | "miter" | "miter-clip" | "round"
Expand Down Expand Up @@ -84,6 +86,8 @@ export type SvgOptions = {
[SvgItem.TEXT]: Text
[SvgItem.USE]: Use
[SvgItem.CLIP_PATH]: ClipPath
[SvgItem.MARKER]: Marker
[SvgItem.ARROW]: Arrow

};

Expand Down Expand Up @@ -112,6 +116,8 @@ export type Path = StrokeOptions & CommonOptions & {
id?: string
fill?: string
"shape-rendering"?: ShapeRendering
"marker-start"?: string
"marker-end"?: string
}

export type GradientStop = {
Expand Down Expand Up @@ -190,6 +196,8 @@ export type Line = StrokeOptions & CommonOptions & {
y1: number
y2: number
"shape-rendering"?: ShapeRendering
"marker-start"?: string
"marker-end"?: string
}

export type ChartArea = {
Expand All @@ -199,4 +207,35 @@ export type ChartArea = {
bottom: number
centerX?: number
centerY?: number
}

export type PreserveAspectRatioValue =
| 'none'
| 'xMinYMin'
| 'xMidYMin'
| 'xMaxYMin'
| 'xMinYMid'
| 'xMidYMid'
| 'xMaxYMid'
| 'xMinYMax'
| 'xMidYMax'
| 'xMaxYMax'

type PreserveAspectRatio = `${PreserveAspectRatioValue} ${'meet' | 'slice' | ''}`;

export type Marker = CommonOptions & {
id?: string
markerHeight?: number
markerWidth?: number
markerUnits?: 'userSpaceOnUse' | 'strokeWidth'
orient?: 'auto' | 'auto-start-reverse'
preserveAspectRatio?: PreserveAspectRatio
refX?: number | 'left' | 'center' | 'right'
refY?: number | 'top' | 'center' | 'bottom'
viewBox?: string
}

export type Arrow = Line & Marker & {
marker: "start" | "end" | "both",
size?: number
}
Loading

0 comments on commit 9893bb4

Please sign in to comment.