diff --git a/src/client/components/treemap/component.tsx b/src/client/components/treemap/component.tsx index b03ca9c..3dc1a1e 100644 --- a/src/client/components/treemap/component.tsx +++ b/src/client/components/treemap/component.tsx @@ -58,10 +58,17 @@ export const Treemap = forwardRef((props: TreemapProps, ref: ForwardedRef 0 + if (isZoomOut) { + // this.zoomOut() + } else { + // + } } }) window.addEventListener('resize', resize) diff --git a/src/client/components/treemap/treemap.ts b/src/client/components/treemap/treemap.ts index 848c33e..03b1421 100644 --- a/src/client/components/treemap/treemap.ts +++ b/src/client/components/treemap/treemap.ts @@ -37,8 +37,11 @@ export class Paint { private layoutNodes: SquarifiedModule[] private colorMapping: Record private options: PaintOptions + private currentNode: SquarifiedModule | null + private animationFrame: number | null constructor(data: Module[]) { this.mainEl = null + this.currentNode = null this.data = data this.layoutNodes = [] this.canvas = document.createElement('canvas') @@ -46,6 +49,7 @@ export class Paint { this.shape = { ...defaultShape } this.colorMapping = {} this.options = {} + this.animationFrame = null } private drawNodeBackground(node: SquarifiedModule) { @@ -66,13 +70,19 @@ export class Paint { private drawNodeForeground(node: SquarifiedModule) { const [x, y, w, h] = node.layout + + if (this.currentNode === node) { + this.context.fillStyle = 'rgba(255,255,255,0.5)' + this.context.fillRect(x, y, w, h) + } + this.context.strokeStyle = '#222' this.context.strokeRect(x + 0.5, y + 0.5, w, h) if (h > STYLES.HEAD_HEIGHT) { this.context.fillStyle = '#000' const maxWidth = w - STYLES.INSET_X - const textY = y + Math.round(STYLES.INSET_Y / 2) + 2 + const textY = y + Math.round(STYLES.INSET_Y / 2) - 2 const ellipsisWidth = 3 * charCodeWidth(this.context, STYLES.DOT_CHAR_CODE) const [text, width] = textOverflowEllipsis(this.context, node.node.label, maxWidth, ellipsisWidth) const textX = x + Math.round((w - width) / 2) @@ -94,8 +104,18 @@ export class Paint { } } + private smoothDrawing() { + if (!this.animationFrame) { + this.animationFrame = window.requestAnimationFrame(() => { + this.draw() + }) + } + } + private draw() { + this.animationFrame = null this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) + this.context.textBaseline = 'middle' for (const node of this.layoutNodes) { this.drawNodeBackground(node) } @@ -104,7 +124,10 @@ export class Paint { } } - private eventHandler(e: MouseEvent | WheelEvent, handler: (event: PaintEvent) => void) { + private eventHandler( + e: T, + handler: (event: PaintEvent) => void + ) { const layout = findRelativeNode(this.canvas, e, this.layoutNodes) const event = { nativeEvent: e, @@ -113,6 +136,13 @@ export class Paint { handler(event) } + private changeHoverNode(node: SquarifiedModule | null) { + if (this.currentNode !== node) { + this.currentNode = node + this.smoothDrawing() + } + } + private updateColorMapping() { const colorMapping: Record = {} const defaultSweepAngle = Math.PI * 2 @@ -142,8 +172,7 @@ export class Paint { this.colorMapping = colorMapping } - zoom(node: SquarifiedModule) { - // + zoom(node: PaintEvent) { } dispose() { @@ -151,10 +180,12 @@ export class Paint { this.mainEl.removeChild(this.canvas) this.canvas = null! this.context = null! + this.currentNode = null this.shape = { ...defaultShape } this.data = [] this.layoutNodes = [] this.colorMapping = {} + this.animationFrame = null } resize() { @@ -188,6 +219,7 @@ export class Paint { this.canvas.onmousemove = (e) => this.eventHandler(e, (evt) => { this.canvas.style.cursor = 'pointer' + if (evt.module) this.changeHoverNode(evt.module) this.options.onMousemove?.call(this, evt) }) this.canvas.onclick = (e) => @@ -195,6 +227,12 @@ export class Paint { this.canvas.style.cursor = 'default' this.options.onClick?.call(this, evt) }) + this.canvas.onwheel = (e) => + this.eventHandler(e, (evt) => { + e.preventDefault() + this.options.onMouseWheel?.call(this, evt) + }) + this.canvas.onmouseout = () => this.changeHoverNode(null) } setup(options: PaintOptions) { @@ -202,10 +240,6 @@ export class Paint { } } -// manifest is the processed Module module but it's only a temporary data struct. -// It will be re desgined after squarify algorithm is implemented. -// In past we used @carrotsearch/Moduletree it neeed Module struct. But now we won't use it anymore. - export function createTreemap(manifest: Module[]) { const panit = new Paint(manifest) return panit