Last active
December 26, 2023 17:54
-
-
Save Hansanghyeon/a295bd02ab1054fa00412c358759c109 to your computer and use it in GitHub Desktop.
simple-flow
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { pipe } from 'fp-ts/function' | |
// jquery `.position()` 컨버팅 | |
function position(elm: HTMLElement): { top: number; left: number } { | |
return { | |
top: elm.offsetTop, | |
left: elm.offsetLeft, | |
} | |
} | |
// jquery `.outerHeight()` 컨버팅 | |
function outerHeight(elm: HTMLElement): number { | |
const height = elm.offsetHeight | |
return height | |
} | |
// jquery `.outerWidth()` 컨버팅 | |
function outerWidth(elm: HTMLElement): number { | |
const width = elm.offsetWidth | |
return width | |
} | |
// 더하기 | |
function add(a: number) { | |
return (b: number) => b + a | |
} | |
// 나누기 | |
function divide(a: number) { | |
return (b: number) => b / a | |
} | |
// 빼기 | |
function subtract(a: number) { | |
return (b: number) => b - a | |
} | |
type PluginOptions = { | |
lineWidth: number | |
lineSpacerWidth: number | |
lineColour: string | |
} | |
type HTMLElementOrElement = HTMLElement | Element | |
export class SimpleFlow { | |
private canvasElm: HTMLElementOrElement | |
private settings: PluginOptions | |
private static readonly defaults: PluginOptions = { | |
lineWidth: 2, | |
lineSpacerWidth: 15, | |
lineColour: '#91acb3', | |
} | |
constructor( | |
canvasElm: HTMLElementOrElement, | |
options?: Partial<PluginOptions>, | |
) { | |
this.canvasElm = canvasElm | |
this.settings = { ...SimpleFlow.defaults, ...options } | |
this.init() | |
} | |
private init(): void { | |
this.drawLines() | |
window.addEventListener('resize', this.drawLines.bind(this)) | |
} | |
private drawLines(): void { | |
// Logic for drawing lines | |
// Replace jQuery methods with vanilla JavaScript | |
} | |
private drawMarker(): SVGDefsElement { | |
// string to element | |
const marker = ` | |
<marker | |
id="up" | |
viewBox="0 0 10 10" | |
refX="5" | |
refY="5" | |
markerWidth="6" | |
markerHeight="6" | |
orient="-90deg"> | |
<path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor" /> | |
</marker> | |
<marker | |
id="right" | |
viewBox="0 0 10 10" | |
refX="5" | |
refY="5" | |
markerWidth="6" | |
markerHeight="6" | |
orient="0deg"> | |
<path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor" /> | |
</marker> | |
<marker | |
id="down" | |
viewBox="0 0 10 10" | |
refX="5" | |
refY="5" | |
markerWidth="6" | |
markerHeight="6" | |
orient="90deg"> | |
<path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor" /> | |
</marker> | |
<marker | |
id="left" | |
viewBox="0 0 10 10" | |
refX="5" | |
refY="5" | |
markerWidth="6" | |
markerHeight="6" | |
orient="180deg"> | |
<path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor" /> | |
</marker> | |
` | |
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs') | |
defs.innerHTML = marker | |
return defs | |
} | |
private drawLine( | |
thisElm: HTMLElementOrElement, | |
nextElm: HTMLElementOrElement, | |
): SVGPathElement { | |
const thisElmParent = thisElm.parentElement | |
const nextElmParent = nextElm.parentElement | |
const thisElmMiddle = pipe(thisElm, outerHeight) | |
// const nextElmMiddle = pipe(nextElm, outerHeight) | |
const thisParentPadding = | |
pipe(thisElmParent, outerHeight) - pipe(thisElm, outerHeight) | |
const thisElmY = | |
thisElmMiddle + pipe(thisElmParent, position).top - thisParentPadding | |
const nextElmY = pipe(nextElmParent, position).top | |
const thisMiddle = pipe( | |
pipe(thisElm, position).left, | |
add(pipe(thisElm, outerWidth, divide(2))), | |
) | |
const nextMiddle = pipe( | |
pipe(nextElm, position).left, | |
add(pipe(nextElm, outerWidth, divide(2))), | |
) | |
const farLeftX = nextMiddle | |
const farRightX = thisMiddle | |
const lineInBetweenY = pipe( | |
nextElmY, | |
subtract(pipe(thisParentPadding, divide(2))), | |
) | |
// TODO: | |
// X축이같고 Y축이 다를 때 점은 2개이다. | |
// X축과 Y축 모두 다를때 점은 4개이다. | |
const coords = `${thisMiddle}, ${thisElmY} ${farRightX}, ${thisElmY} ${farRightX}, ${lineInBetweenY} ${farLeftX}, ${lineInBetweenY} ${farLeftX}, ${nextElmY} ${nextMiddle}, ${nextElmY}` | |
// path string을 elment로 변환 | |
// `<path d="M ${coords} "style="fill:none;stroke: currentColor; stroke-width: ${this.settings['lineWidth']}; stroke-linecap: round; marker-end:url(#arrowhead);" stroke-dasharray="4" />` | |
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path') | |
path.setAttribute('d', `M ${coords} `) | |
path.setAttribute( | |
'style', | |
`fill:none;stroke: currentColor; stroke-width: ${this.settings['lineWidth']}; stroke-linecap: round; marker-end:url(#right);`, | |
) | |
path.setAttribute('stroke-dasharray', '4') | |
return path | |
} | |
public draw(targets: [HTMLElementOrElement, HTMLElementOrElement][]): void { | |
// clean | |
const oldSvg = this.canvasElm.querySelector('svg') | |
if (oldSvg) { | |
oldSvg.remove() | |
} | |
const defs = this.drawMarker() | |
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') | |
svg.setAttribute( | |
'class', | |
'simple-flow-line absolute top-0 left-0 w-full h-full pointer-events-none', | |
) | |
svg.setAttribute('width', '100%') | |
svg.setAttribute('height', '100%') | |
svg.appendChild(defs) | |
targets.forEach(([thisElm, nextElm]) => { | |
const path = this.drawLine(thisElm, nextElm) | |
svg.appendChild(path) | |
}) | |
this.canvasElm.appendChild(svg) | |
} | |
} | |
// Usage example: | |
// const canvas = document.querySelector('.canvas') | |
// const target1 = document.querySelector('.target1') | |
// const target2 = document.querySelectorAll('.target2') | |
// const simpleFlow = new SimpleFlow(canvas, { lineWidth: 3 }) | |
// simpleFlow.draw([[target1, target2[1]]]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://github.com/tdsymonds/simple-flow
simple-flow를 컨버팅