Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Label styles, LabelBackground #78

Merged
merged 10 commits into from
Nov 6, 2023
5 changes: 4 additions & 1 deletion examples/native/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
<h1>Native Examples</h1>
<ul>
<li>
<a href="/src/simple/simple.html">Simple Trellis Example</a>
<a href="/src/perf/perf.html">Perf</a>
</li>
<li>
<a href="/src/labels/labels.html">Label Styles</a>
</li>
</ul>
</div>
Expand Down
120 changes: 120 additions & 0 deletions examples/native/src/labels/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import * as Renderer from '@trellis/renderers/webgl'
import * as Graph from '@trellis/index'
import * as Collide from '@trellis/layout/collide'

const GREEN = '#91AD49'
const GREEN_LIGHT = '#C6D336'
const DARK_GREEN = '#607330'

const TEXT_ICON: Graph.TextIcon = {
type: 'textIcon',
family: 'sans-serif',
color: '#fff',
weight: '400',
text: '!',
size: 14
}

const NODE_STYLE: Graph.NodeStyle = {
color: GREEN,
icon: TEXT_ICON,
stroke: [{ width: 2, color: GREEN_LIGHT }],
label: {
position: 'right',
fontName: 'NodeLabel',
fontFamily: 'Arial, sans-serif',
background: { color: GREEN_LIGHT },
margin: 4
}
}

const NODE_HOVER_STYLE: Graph.NodeStyle = {
color: DARK_GREEN,
icon: TEXT_ICON,
stroke: [{ width: 2, color: GREEN_LIGHT }],
label: {
position: 'right',
fontName: 'NodeLabelHover',
fontFamily: 'Arial, sans-serif',
background: { color: DARK_GREEN },
color: '#FFF',
margin: 4
}
}

const data = [
'Myriel',
'Napoleon',
'Mlle.Baptistine',
'Mme.Magloire',
'CountessdeLo',
'Geborand',
'Champtercier',
'Cravatte',
'Count',
'OldMan',
'Labarre',
'Valjean'
]

const collide = Collide.Layout()

const edges: Graph.Edge[] = []
let nodes = data.map<Graph.Node>((label, index) => ({
radius: 10,
label: index % 2 === 0 ? label + ' 北京' : label,
id: `${index}-${label}`,
style: NODE_STYLE
}))

const layout = collide({ nodes, edges, options: { nodePadding: 50 } })
nodes = layout.nodes

const size = { width: 1250, height: 650 }
const bounds = Graph.getSelectionBounds(nodes, 100)
const viewport = Graph.boundsToViewport(bounds, size)
const container = document.querySelector('#graph') as HTMLDivElement

const options: Renderer.Options = {
...viewport,
...size,
minZoom: 0.025,
onViewportDrag: (event: Renderer.ViewportDragEvent | Renderer.ViewportDragDecelerateEvent) => {
// console.log('viewport drag', `x: ${event.dx}, y: ${event.dy}`)
options.x! += event.dx
options.y! += event.dy
renderer.update({ nodes, edges, options })
},
onViewportWheel: ({ dx, dy, dz }) => {
options.x! += dx
options.y! += dy
options.zoom! += dz
renderer.update({ nodes, edges, options })
},
onNodePointerEnter: (event: Renderer.NodePointerEvent) => {
// console.log('node pointer enter', `x: ${event.x}, y: ${event.y}`)
nodes = nodes.map((node) => (node.id === event.target.id ? { ...node, label: node.label + ' 北京', style: NODE_HOVER_STYLE } : node))
renderer.update({ nodes, edges, options })
},
onNodeDrag: (event: Renderer.NodeDragEvent) => {
// console.log('node drag', `x: ${event.x}, y: ${event.y}`)
nodes = nodes.map((node) =>
node.id === event.target.id ? { ...node, x: (node.x ?? 0) + event.dx, y: (node.y ?? 0) + event.dy } : node
)
renderer.update({ nodes, edges, options })
},
onNodePointerLeave: (event: Renderer.NodePointerEvent) => {
// console.log('node pointer leave', `x: ${event.x}, y: ${event.y}`)
nodes = nodes.map((node) =>
node.id === event.target.id ? { ...node, label: node.label?.slice(0, node.label.length - 3), style: NODE_STYLE } : node
)
renderer.update({ nodes, edges, options })
}
}

const renderer = new Renderer.Renderer({ container, width: options.width, height: options.height, debug: true }).update({
nodes,
edges,
options
})
;(window as any).renderer = renderer
14 changes: 14 additions & 0 deletions examples/native/src/labels/labels.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../index.css" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<title>Trellis Example: Label Style</title>
</head>
<body>
<div id="graph"></div>
<script type="module" src="./index.ts"></script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,28 @@ const sampleCoordinatePlane = function* (count: number, step: number, sample: nu

const PURPLE = '#7A5DC5'
const LIGHT_PURPLE = '#CAD'
const ARIAL_PINK = 'ArialPink'

const NODE_STYLE: Graph.NodeStyle = {
color: PURPLE,
stroke: [{ width: 2, color: LIGHT_PURPLE }],
icon: { type: 'textIcon', text: 'T', family: 'sans-serif', size: 14, color: '#fff', weight: '400' },
label: {
position: 'bottom',
color: LIGHT_PURPLE
position: 'top',
fontName: ARIAL_PINK,
fontFamily: ['Arial', 'sans-serif'],
margin: 2,
background: {
color: '#f66',
opacity: 0.5
}
}
}

const NODE_HOVER_STYLE: Graph.NodeStyle = {
color: '#f66',
stroke: [{ width: 2, color: '#fcc' }],
label: { position: 'bottom' },
label: { position: 'bottom', color: '#fcc' },
icon: { type: 'textIcon', text: 'L', family: 'sans-serif', size: 14, color: '#fff', weight: '400' }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../index.css" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<title>Simple Trellis Example</title>
<title>Trellis Example: Perf</title>
</head>
<body>
<div id="graph"></div>
Expand Down
1 change: 1 addition & 0 deletions src/bindings/react/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type Props = {
group?: 'top' | 'middle' | 'bottom'
title?: string
onClick?: () => void
children?: React.ReactNode
}

const STYLE = {
Expand Down
22 changes: 6 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { TWO_PI } from './renderers/webgl/utils'
import type { LabelStyle } from './renderers/webgl/objects/label'
import type { Stroke } from './types'

export type Node = {
id: string
Expand Down Expand Up @@ -43,22 +45,6 @@ export type ImageIcon = {
offsetY?: number
}

export type Stroke = { color: string; width: number }

export type LabelBackground = { color: string; opacity?: number }

export type LabelPosition = 'bottom' | 'left' | 'top' | 'right'

export type LabelStyle = Partial<{
color: string
fontFamily: string
fontSize: number
maxWidth: number
stroke: Stroke
position: LabelPosition
background: LabelBackground
}>

export type NodeStyle = {
color?: string
icon?: TextIcon | ImageIcon
Expand Down Expand Up @@ -392,3 +378,7 @@ export const angle = (x0: number, y0: number, x1: number, y1: number) => {
const angle = Math.atan2(y0 - y1, x0 - x1)
return angle < 0 ? angle + TWO_PI : angle
}

// exports
export type { Stroke } from './types'
export type { LabelStyle, LabelBackgroundStyle, LabelPosition, FontWeight, TextAlign } from './renderers/webgl/objects/label'
22 changes: 10 additions & 12 deletions src/renderers/webgl/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { FederatedPointerEvent } from 'pixi.js'
import { MIN_LABEL_ZOOM, MIN_INTERACTION_ZOOM, MIN_NODE_STROKE_ZOOM, Renderer, MIN_NODE_ICON_ZOOM } from '.'
import * as Graph from '../..'
import { Label } from './objects/label'
import { positionNodeLabel } from './utils'
import { NodeFill } from './objects/nodeFill'
import { NodeStrokes } from './objects/nodeStrokes'
import { Icon } from './objects/icon'
Expand Down Expand Up @@ -44,15 +43,15 @@ export class NodeRenderer {

update(node: Graph.Node) {
if (this.label === undefined) {
if (node.label) {
this.label = new Label(this.renderer.labelsContainer, node.label)
}
} else {
if (node.label === undefined) {
this.renderer.labelObjectManager.delete(this.label)
this.labelMounted = false
this.label = undefined
if (node.label !== undefined) {
this.label = new Label(this.renderer.labelsContainer, node.label, node.style?.label)
}
} else if (node.label === undefined || node.label.trim() === '') {
this.renderer.labelObjectManager.delete(this.label)
this.labelMounted = false
this.label = undefined
} else if (!this.label.equals(node.label, node.style?.label)) {
this.label.update(node.label, node.style?.label)
}

if (this.icon === undefined) {
Expand Down Expand Up @@ -502,9 +501,8 @@ export class NodeRenderer {

this.fill.update(this.x, this.y, radius, node.style)
this.strokes.update(this.x, this.y, radius, node.style)
if (this.label && node.label) {
const labelPosition = positionNodeLabel(this.x, this.y, node.label, this.strokes.radius, node.style?.label?.position)
this.label.update(node.label, labelPosition[0], labelPosition[1], node.style?.label)
if (this.label !== undefined) {
this.label.moveTo(this.x, this.y, this.strokes.radius)
}
if (this.icon && node.style?.icon) {
this.icon.update(this.x, this.y, node.style.icon)
Expand Down
Loading
Loading