2

I am creating a simple compass with North, East, South, West directions. I created a TextPath so that my Text can curve along the circular compass.

I am facing one major problem: the beginning of the Text is being cut-off. I cannot see the complete "North" label because of the textAnchor being set to middle (which is what I prefer).

How can I make the text fully visible? After it is visible, then I can space the directions accordingly (25%) along the TextPath.

/* eslint-disable react/jsx-key */
import React, { Fragment, useState } from 'react'
import { Dimensions, View, Text } from 'react-native'
import {
    Line,
    Circle,
    Text as SVGText,
    Defs,
    Path,
    Use,
    Svg,
    G,
    TextPath,
    ForeignObject,
    TSpan,
} from 'react-native-svg'
import { ReactNativeZoomableView } from '@openspacelabs/react-native-zoomable-view'


const getPoint = (R = 0, degree = 0) => {
    // Convert the angle to a value between 0 and 360 degrees
    const angle = ((degree % 360) + 360) % 360
    // Calculate the coordinates using Math.sin and Math.cos
    const x = R * Math.cos(angle * (Math.PI / 180))
    const y = R * Math.sin(angle * (Math.PI / 180))
    return [x, y]
}


const Compass = (props) => {
    const [X, setW] = useState(1000)
    const [Y, setH] = useState(1000)
    const CoreRadius = 100
    const PartRadius = 50

    const [slider, setSlider] = useState(0)
    const [point, setPoint] = useState([0, CoreRadius])

    const rings = [
        {
            offset: -45,
            degree: 90,
            parts: [{ text: 'North' }, { text: 'East' }, { text: 'South' }, { text: 'West' }],
        },
    ]

    return (
        <View style={{ flex: 1 }}>
            <View style={{ position: 'absolute', padding: 12, top: 0, left: 0, right: 0, zIndex: 1 }}></View>
            <ReactNativeZoomableView maxZoom={30} bindToBorders={false}>
                <Svg
                    style={{ backgroundColor: '#141414', width: X, height: Y }}
                    viewBox={`-${X / 2} -${Y / 2} ${X} ${Y}`}
                    onLayout={(event) => {
                        const { layout } = event.nativeEvent
                    }}
                >
                    {/* Inside Ring */}
                    <Circle cx="0" cy="0" r={CoreRadius} fill="none" strokeWidth={2} stroke={'white'} />
                    {rings.map((ring, ringIndex) => {
                        //Begin at [0, 1] (bottom of the circle)
                        let totalDegrees = 0

                        const prevRing = CoreRadius + ringIndex * PartRadius
                        const newRing = prevRing + (ringIndex + 1 * PartRadius)

                        //This is the Path for the Text
                        const pathRadius = prevRing + (newRing - prevRing) / 2
                        const centerX = 0
                        const centerY = 0

                        return (
                            <Fragment>
                                <Circle cx="0" cy="0" r={newRing} fill="none" strokeWidth={2} stroke={'white'} />
                                <Defs>
                                    {/* This path is used for text to be displayed along the circular path. */}
                                    <Path
                                        d={`M ${centerX} ${centerY + pathRadius}
                                           A ${pathRadius} ${pathRadius} 0 0 1 ${centerX} ${centerY - pathRadius}
                                           A ${pathRadius} ${pathRadius} 0 0 1 ${centerX} ${centerY + pathRadius}
                                           `}
                                        fill="none"
                                        stroke="green"
                                        strokeWidth={1}
                                        id={`path-${ringIndex}`}
                                    />
                                </Defs>
                                <Use href={`#path-${ringIndex}`} />
                                {ring.parts.map((part, partIndex) => {
                                    const partDegree = part.degree || ring.degree
                                    totalDegrees = totalDegrees + partDegree
                                    const innerStartPoint = getPoint(prevRing, totalDegrees - ring.offset)
                                    const outerStartPoint = getPoint(newRing, totalDegrees - ring.offset)

                                    return (
                                        <Fragment>
                                            <Line
                                                x1={innerStartPoint[0]}
                                                y1={innerStartPoint[1]}
                                                x2={outerStartPoint[0]}
                                                y2={outerStartPoint[1]}
                                                stroke={'white'}
                                                strokeWidth={2}
                                            />

                                            {/* Fix startOffset so that each Direction begins at a different point. */}
                                            <TextPath href={`#path-${ringIndex}`}>
                                                <TSpan
                                                    fill={'white'}
                                                    textAnchor="middle"
                                                    alignmentBaseline="middle"
                                                    verticalAlign={'middle'}
                                                >
                                                    {part.text}
                                                </TSpan>
                                            </TextPath>
                                        </Fragment>
                                    )
                                })}
                            </Fragment>
                        )
                    })}
                </Svg>
            </ReactNativeZoomableView>
        </View>
    )
}

export default Compass

enter image description here

TIMEX
  • 259,804
  • 351
  • 777
  • 1,080

0 Answers0