コード例 #1
0
ファイル: SwarmPlotCanvas.js プロジェクト: plouc/nivo
    ({
        pixelRatio,
        width,
        height,
        margin: partialMargin,
        data,
        groups,
        groupBy,
        identity,
        label,
        value,
        valueFormat,
        valueScale,
        size,
        spacing,
        layout,
        gap,
        forceStrength,
        simulationIterations,
        layers,
        renderNode,
        colors,
        colorBy,
        borderWidth,
        borderColor,
        enableGridX,
        gridXValues,
        enableGridY,
        gridYValues,
        axisTop,
        axisRight,
        axisBottom,
        axisLeft,
        annotations,
        isInteractive,
        onMouseEnter,
        onMouseMove,
        onMouseLeave,
        onClick,
        tooltip,
        debugMesh,
    }) => {
        const canvasEl = useRef(null)
        const [currentNode, setCurrentNode] = useState(null)
        const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions(
            width,
            height,
            partialMargin
        )
        const theme = useTheme()

        const { nodes, xScale, yScale } = useSwarmPlot({
            width: innerWidth,
            height: innerHeight,
            data,
            groups,
            groupBy,
            identity,
            label,
            value,
            valueFormat,
            valueScale,
            size,
            spacing,
            layout,
            gap,
            colors,
            colorBy,
            forceStrength,
            simulationIterations,
        })

        const boundAnnotations = useSwarmPlotAnnotations(nodes, annotations)
        const computedAnnotations = useComputedAnnotations({
            annotations: boundAnnotations,
            innerWidth,
            innerHeight,
        })

        const getBorderWidth = useBorderWidth(borderWidth)
        const getBorderColor = useInheritedColor(borderColor, theme)

        const { delaunay, voronoi } = useVoronoiMesh({
            points: nodes,
            width: innerWidth,
            height: innerHeight,
            debug: debugMesh,
        })

        useEffect(() => {
            canvasEl.current.width = outerWidth * pixelRatio
            canvasEl.current.height = outerHeight * pixelRatio

            const ctx = canvasEl.current.getContext('2d')

            ctx.scale(pixelRatio, pixelRatio)

            ctx.fillStyle = theme.background
            ctx.fillRect(0, 0, outerWidth, outerHeight)
            ctx.translate(margin.left, margin.top)

            layers.forEach(layer => {
                if (layer === 'grid' && theme.grid.line.strokeWidth > 0) {
                    ctx.lineWidth = theme.grid.line.strokeWidth
                    ctx.strokeStyle = theme.grid.line.stroke

                    enableGridX &&
                        renderGridLinesToCanvas(ctx, {
                            width: innerWidth,
                            height: innerHeight,
                            scale: xScale,
                            axis: 'x',
                            values: gridXValues,
                        })

                    enableGridY &&
                        renderGridLinesToCanvas(ctx, {
                            width: innerWidth,
                            height: innerHeight,
                            scale: yScale,
                            axis: 'y',
                            values: gridYValues,
                        })
                }

                if (layer === 'axes') {
                    renderAxesToCanvas(ctx, {
                        xScale,
                        yScale,
                        width: innerWidth,
                        height: innerHeight,
                        top: axisTop,
                        right: axisRight,
                        bottom: axisBottom,
                        left: axisLeft,
                        theme,
                    })
                }

                if (layer === 'nodes') {
                    nodes.forEach(node => {
                        renderNode(ctx, {
                            node,
                            getBorderWidth,
                            getBorderColor,
                        })
                    })
                }

                if (layer === 'mesh' && debugMesh === true) {
                    renderVoronoiToCanvas(ctx, voronoi)
                    if (currentNode) {
                        renderVoronoiCellToCanvas(ctx, voronoi, currentNode.index)
                    }
                }

                if (layer === 'annotations') {
                    renderAnnotationsToCanvas(ctx, {
                        annotations: computedAnnotations,
                        theme,
                    })
                }

                if (typeof layer === 'function') {
                    layer(ctx, {
                        nodes,
                        innerWidth,
                        innerHeight,
                        outerWidth,
                        outerHeight,
                        margin,
                        xScale,
                        yScale,
                    })
                }
            })
        }, [
            canvasEl,
            innerWidth,
            innerHeight,
            outerWidth,
            outerHeight,
            margin,
            pixelRatio,
            theme,
            layers,
            nodes,
            xScale,
            yScale,
            getBorderWidth,
            getBorderColor,
            voronoi,
            currentNode,
            computedAnnotations,
        ])

        const [showTooltip, hideTooltip] = useTooltip()
        const showNodeTooltip = useMemo(() => {
            if (tooltip) return (node, event) => showTooltip(tooltip({ node }), event)
            return (node, event) => showTooltip(<SwarmPlotTooltip node={node} />, event)
        }, [showTooltip, tooltip])

        const getNodeFromMouseEvent = useCallback(
            event => {
                const [x, y] = getRelativeCursor(canvasEl.current, event)
                if (!isCursorInRect(margin.left, margin.top, innerWidth, innerHeight, x, y))
                    return null

                const nodeIndex = delaunay.find(x - margin.left, y - margin.top)
                return nodes[nodeIndex]
            },
            [canvasEl, margin, innerWidth, innerHeight, delaunay, setCurrentNode]
        )

        const handleMouseHover = useCallback(
            event => {
                const node = getNodeFromMouseEvent(event)
                setCurrentNode(node)
                onMouseMove && onMouseMove(node, event)
                if (node) {
                    showNodeTooltip(node, event)
                    if ((!currentNode || currentNode.id !== node.id) && onMouseEnter) {
                        onMouseEnter(node, event)
                    }
                    if (currentNode && currentNode.id !== node.id && onMouseLeave) {
                        onMouseLeave(currentNode, event)
                    }
                } else {
                    currentNode && onMouseLeave && onMouseLeave(currentNode, event)
                    hideTooltip()
                }
            },
            [
                getNodeFromMouseEvent,
                currentNode,
                onMouseEnter,
                onMouseLeave,
                showNodeTooltip,
                hideTooltip,
            ]
        )

        const handleMouseLeave = useCallback(
            event => {
                hideTooltip()
                setCurrentNode(null)
                onMouseLeave && onMouseLeave(currentNode, event)
            },
            [hideTooltip, setCurrentNode, currentNode, onMouseLeave]
        )

        const handleClick = useCallback(
            event => {
                const node = getNodeFromMouseEvent(event)
                node && onClick && onClick(node, event)
            },
            [getNodeFromMouseEvent, onClick]
        )

        return (
            <canvas
                ref={canvasEl}
                width={outerWidth * pixelRatio}
                height={outerHeight * pixelRatio}
                style={{
                    width: outerWidth,
                    height: outerHeight,
                    cursor: isInteractive ? 'auto' : 'normal',
                }}
                onMouseEnter={isInteractive ? handleMouseHover : undefined}
                onMouseMove={isInteractive ? handleMouseHover : undefined}
                onMouseLeave={isInteractive ? handleMouseLeave : undefined}
                onClick={isInteractive ? handleClick : undefined}
            />
        )
    }
コード例 #2
0
ファイル: GeoMap.js プロジェクト: plouc/nivo
const GeoMap = memo(props => {
    const {
        width,
        height,
        margin: partialMargin,
        features,
        layers,
        projectionType,
        projectionScale,
        projectionTranslation,
        projectionRotation,
        fillColor,
        borderWidth,
        borderColor,
        enableGraticule,
        graticuleLineWidth,
        graticuleLineColor,
        isInteractive,
        onClick,
        tooltip: Tooltip,
    } = props
    const { margin, outerWidth, outerHeight } = useDimensions(width, height, partialMargin)
    const { graticule, path, getFillColor, getBorderWidth, getBorderColor } = useGeoMap({
        width,
        height,
        projectionType,
        projectionScale,
        projectionTranslation,
        projectionRotation,
        fillColor,
        borderWidth,
        borderColor,
    })

    const theme = useTheme()

    const [showTooltip, hideTooltip] = useTooltip()
    const handleClick = useCallback(
        (feature, event) => isInteractive && onClick && onClick(feature, event),
        [isInteractive, onClick]
    )
    const handleMouseEnter = useCallback(
        (feature, event) =>
            isInteractive && Tooltip && showTooltip(<Tooltip feature={feature} />, event),
        [isInteractive, showTooltip, Tooltip]
    )
    const handleMouseMove = useCallback(
        (feature, event) =>
            isInteractive && Tooltip && showTooltip(<Tooltip feature={feature} />, event),
        [isInteractive, showTooltip, Tooltip]
    )
    const handleMouseLeave = useCallback(() => isInteractive && hideTooltip(), [
        isInteractive,
        hideTooltip,
    ])

    return (
        <SvgWrapper width={outerWidth} height={outerHeight} margin={margin} theme={theme}>
            {layers.map((layer, i) => {
                if (layer === 'graticule') {
                    if (enableGraticule !== true) return null

                    return (
                        <GeoGraticule
                            key="graticule"
                            path={path}
                            graticule={graticule}
                            lineWidth={graticuleLineWidth}
                            lineColor={graticuleLineColor}
                        />
                    )
                }
                if (layer === 'features') {
                    return (
                        <Fragment key="features">
                            {features.map(feature => (
                                <GeoMapFeature
                                    key={feature.id}
                                    feature={feature}
                                    path={path}
                                    fillColor={getFillColor(feature)}
                                    borderWidth={getBorderWidth(feature)}
                                    borderColor={getBorderColor(feature)}
                                    onMouseEnter={handleMouseEnter}
                                    onMouseMove={handleMouseMove}
                                    onMouseLeave={handleMouseLeave}
                                    onClick={handleClick}
                                />
                            ))}
                        </Fragment>
                    )
                }

                return <Fragment key={i}>{layer(props)}</Fragment>
            })}
        </SvgWrapper>
    )
})
コード例 #3
0
ファイル: Choropleth.js プロジェクト: plouc/nivo
    ({
        width,
        height,
        margin: partialMargin,
        features,
        data,
        match,
        label,
        value,
        valueFormat,
        projectionType,
        projectionScale,
        projectionTranslation,
        projectionRotation,
        colors,
        unknownColor,
        borderWidth,
        borderColor,
        enableGraticule,
        graticuleLineWidth,
        graticuleLineColor,
        layers,
        legends,
        isInteractive,
        onClick,
        tooltip: Tooltip,
    }) => {
        const { margin, outerWidth, outerHeight } = useDimensions(width, height, partialMargin)
        const { graticule, path, getBorderWidth, getBorderColor } = useGeoMap({
            width,
            height,
            projectionType,
            projectionScale,
            projectionTranslation,
            projectionRotation,
            fillColor: () => {},
            borderWidth,
            borderColor,
        })
        const { getFillColor, boundFeatures, legendData } = useChoropleth({
            features,
            data,
            match,
            label,
            value,
            valueFormat,
            colors,
            unknownColor,
        })

        const theme = useTheme()

        const [showTooltip, hideTooltip] = useTooltip()
        const handleClick = useCallback(
            (feature, event) => isInteractive && onClick && onClick(feature, event),
            [isInteractive, onClick]
        )
        const handleMouseEnter = useCallback(
            (feature, event) =>
                isInteractive && Tooltip && showTooltip(<Tooltip feature={feature} />, event),
            [isInteractive, showTooltip, Tooltip]
        )
        const handleMouseMove = useCallback(
            (feature, event) =>
                isInteractive && Tooltip && showTooltip(<Tooltip feature={feature} />, event),
            [isInteractive, showTooltip, Tooltip]
        )
        const handleMouseLeave = useCallback(() => isInteractive && hideTooltip(), [
            isInteractive,
            hideTooltip,
        ])

        return (
            <SvgWrapper width={outerWidth} height={outerHeight} margin={margin} theme={theme}>
                {layers.map((layer, i) => {
                    if (layer === 'graticule') {
                        if (enableGraticule !== true) return null

                        return (
                            <GeoGraticule
                                key="graticule"
                                path={path}
                                graticule={graticule}
                                lineWidth={graticuleLineWidth}
                                lineColor={graticuleLineColor}
                            />
                        )
                    }
                    if (layer === 'features') {
                        return (
                            <Fragment key="features">
                                {boundFeatures.map(feature => (
                                    <GeoMapFeature
                                        key={feature.id}
                                        feature={feature}
                                        path={path}
                                        fillColor={getFillColor(feature)}
                                        borderWidth={getBorderWidth(feature)}
                                        borderColor={getBorderColor(feature)}
                                        onMouseEnter={handleMouseEnter}
                                        onMouseMove={handleMouseMove}
                                        onMouseLeave={handleMouseLeave}
                                        onClick={handleClick}
                                    />
                                ))}
                            </Fragment>
                        )
                    }
                    if (layer === 'legends') {
                        return legends.map((legend, i) => {
                            return (
                                <BoxLegendSvg
                                    key={i}
                                    containerWidth={width}
                                    containerHeight={height}
                                    data={legendData}
                                    {...legend}
                                />
                            )
                        })
                    }

                    return <Fragment key={i}>{layer({})}</Fragment>
                })}
            </SvgWrapper>
        )
    }
コード例 #4
0
ファイル: ChoroplethCanvas.js プロジェクト: plouc/nivo
    ({
        width,
        height,
        margin: partialMargin,
        pixelRatio,
        features,
        data,
        match,
        label,
        value,
        valueFormat,
        projectionType,
        projectionScale,
        projectionTranslation,
        projectionRotation,
        colors,
        unknownColor,
        borderWidth,
        borderColor,
        enableGraticule,
        graticuleLineWidth,
        graticuleLineColor,
        layers,
        legends,
        isInteractive,
        onClick,
        onMouseMove,
        tooltip: Tooltip,
    }) => {
        const canvasEl = useRef(null)
        const theme = useTheme()
        const { margin, outerWidth, outerHeight } = useDimensions(width, height, partialMargin)
        const { projection, graticule, path, getBorderWidth, getBorderColor } = useGeoMap({
            width,
            height,
            projectionType,
            projectionScale,
            projectionTranslation,
            projectionRotation,
            fillColor: () => {},
            borderWidth,
            borderColor,
        })
        const { getFillColor, boundFeatures, legendData } = useChoropleth({
            features,
            data,
            match,
            label,
            value,
            valueFormat,
            colors,
            unknownColor,
        })

        useEffect(() => {
            if (!canvasEl) return

            canvasEl.current.width = outerWidth * pixelRatio
            canvasEl.current.height = outerHeight * pixelRatio

            const ctx = canvasEl.current.getContext('2d')

            ctx.scale(pixelRatio, pixelRatio)

            ctx.fillStyle = theme.background
            ctx.fillRect(0, 0, outerWidth, outerHeight)
            ctx.translate(margin.left, margin.top)

            path.context(ctx)

            layers.forEach(layer => {
                if (layer === 'graticule') {
                    if (enableGraticule === true) {
                        ctx.lineWidth = graticuleLineWidth
                        ctx.strokeStyle = graticuleLineColor
                        ctx.beginPath()
                        path(graticule())
                        ctx.stroke()
                    }
                } else if (layer === 'features') {
                    boundFeatures.forEach(feature => {
                        ctx.beginPath()
                        path(feature)
                        ctx.fillStyle = getFillColor(feature)
                        ctx.fill()

                        const borderWidth = getBorderWidth(feature)
                        if (borderWidth > 0) {
                            ctx.strokeStyle = getBorderColor(feature)
                            ctx.lineWidth = borderWidth
                            ctx.stroke()
                        }
                    })
                } else if (layer === 'legends') {
                    legends.forEach(legend => {
                        renderLegendToCanvas(ctx, {
                            ...legend,
                            data: legendData,
                            containerWidth: width,
                            containerHeight: height,
                            theme,
                        })
                    })
                } else {
                    // layer(ctx, {})
                }
            })
        }, [
            canvasEl,
            outerWidth,
            outerHeight,
            margin,
            pixelRatio,
            theme,
            path,
            graticule,
            getFillColor,
            getBorderWidth,
            getBorderColor,
            boundFeatures,
            legends,
            layers,
        ])

        const [showTooltip, hideTooltip] = useTooltip()
        const handleMouseMove = useCallback(() => {
            if (!isInteractive || !Tooltip) return

            const feature = getFeatureFromMouseEvent(
                event,
                canvasEl.current,
                boundFeatures,
                projection
            )
            if (feature) {
                showTooltip(<Tooltip feature={feature} />, event)
            } else {
                hideTooltip()
            }
            onMouseMove && onMouseMove(feature || null, event)
        }, [showTooltip, hideTooltip, isInteractive, Tooltip, canvasEl, boundFeatures, projection])
        const handleMouseLeave = useCallback(() => isInteractive && hideTooltip(), [
            isInteractive,
            hideTooltip,
        ])
        const handleClick = useCallback(() => {
            if (!isInteractive || !onClick) return

            const feature = getFeatureFromMouseEvent(
                event,
                canvasEl.current,
                boundFeatures,
                projection
            )
            if (feature) {
                onClick(feature, event)
            }
        }, [isInteractive, canvasEl, boundFeatures, projection, onClick])

        return (
            <canvas
                ref={canvasEl}
                width={outerWidth * pixelRatio}
                height={outerHeight * pixelRatio}
                style={{
                    width: outerWidth,
                    height: outerHeight,
                    cursor: isInteractive ? 'auto' : 'normal',
                }}
                onMouseMove={handleMouseMove}
                onMouseLeave={handleMouseLeave}
                onClick={handleClick}
            />
        )
    }
コード例 #5
0
ファイル: ChordArc.js プロジェクト: plouc/nivo
    ({
        arc,
        startAngle,
        endAngle,
        borderWidth,
        getBorderColor,
        opacity,
        arcGenerator,
        setCurrent,
        isInteractive,
        onMouseEnter,
        onMouseMove,
        onMouseLeave,
        onClick,
        tooltip,
    }) => {
        const [showTooltip, hideTooltip] = useTooltip()

        const handleMouseEnter = useMemo(() => {
            if (!isInteractive) return undefined
            return event => {
                setCurrent(arc)
                showTooltip(React.createElement(tooltip, { arc }), event)
                onMouseEnter && onMouseEnter(arc, event)
            }
        }, [isInteractive, arc, onMouseEnter])
        const handleMouseMove = useMemo(() => {
            if (!isInteractive) return undefined
            return event => {
                showTooltip(React.createElement(tooltip, { arc }), event)
                onMouseMove && onMouseMove(arc, event)
            }
        }, [isInteractive, arc, onMouseMove])
        const handleMouseLeave = useMemo(() => {
            if (!isInteractive) return undefined
            return event => {
                setCurrent(null)
                hideTooltip()
                onMouseLeave && onMouseLeave(arc, event)
            }
        }, [isInteractive, arc, onMouseLeave])
        const handleClick = useMemo(() => {
            if (!isInteractive || !onClick) return undefined
            return event => onClick(arc, event)
        }, [isInteractive, arc, onClick])

        return (
            <path
                d={arcGenerator({ startAngle, endAngle })}
                fill={arc.color}
                fillOpacity={opacity}
                strokeWidth={borderWidth}
                stroke={getBorderColor(arc)}
                strokeOpacity={opacity}
                onMouseEnter={handleMouseEnter}
                onMouseMove={handleMouseMove}
                onMouseLeave={handleMouseLeave}
                onClick={handleClick}
            />
        )
    }