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

React Native support? #26

Open
keithhilen opened this issue Mar 8, 2023 · 5 comments
Open

React Native support? #26

keithhilen opened this issue Mar 8, 2023 · 5 comments

Comments

@keithhilen
Copy link

The example does not work in React Native because the document object is not available. Is there a workaround?

Code:

import { Context } from "svgcanvas";

export default () => {
  const ctx = new Context(500, 500); // Error occurs here
}

Error:

ERROR  ReferenceError: Property 'document' doesn't exist

This error is located at:
   in _default (created by _default)
   in RCTSafeAreaView (created by _default)
   in _default (created by _default)
   in _default
   in RCTView (created by View)
   in View (created by AppContainer)
   in RCTView (created by View)
   in View (created by AppContainer)
   in AppContainer
   in TestSvg(RootComponent), js engine: hermes

Versions:

    "react": "18.2.0",
    "react-native": "0.71.3",
    "svgcanvas": "^2.5.0"
@arjun-rawal
Copy link

Yes, use react-native-canvas instead of svgcanvas. This is a good tutorial: https://www.atomlab.dev/tutorials/react-native-canvas

@keithhilen
Copy link
Author

Thanks for your response, Arjun. I found react-native-svg is a good solution for my use case. I am generating screens with a mixture of text and vector graphics, and I need the ability to pan and zoom with crisp rendering at any scale. It turns out that converting SVG to JSX is straightforward, and the viewport provides pan and zoom. My one remaining stumbling block is how to wrap text in boxes. This is trivial when using components and styles, but I haven't found a good SVG solution.

@arjun-rawal
Copy link

This may not be the answer you're looking for, but wrapping your text in a view component and styling it with a border might work.

@keithhilen
Copy link
Author

Unfortunately no - SVG and CSS rendering are two different universes right now. I believe I'm going to have to find or create an algorithm for wrapping text. As an experiment, and potentially as part of the solution, I did find a way to measure text width. Bit of a kludge, but it works.

@keithhilen
Copy link
Author

keithhilen commented Mar 14, 2023

/*
  Instantiates a transparent 1x1 canvas to use for measuring text
  Sets a global.measureText function
  Optionally calls a listener when ready

  global.measureText accepts a text string to be measured
  and an optional CSS font specification
  It queries for a metrics object
  This is an async function, so it returns a promise

  To use, include the TextMetrics component somewhere in the screen stack
  Invoke the function as global.measureText(text, font).then((metrics) => { ... })
*/

import React, { useState, useEffect, useRef } from 'react';
import Canvas from 'react-native-canvas';

export default (props) => {
  const ref = useRef(null);
  const [listener, setListener] = useState({f: props.listener});
  const [measureText, setMeasureText] = useState();

  useEffect(() => {
    if (ref.current) {
      if (!measureText) {
        const canvas = ref.current;
        const ctx = canvas.getContext('2d');

        setMeasureText({f: 
          async (text, font) => {
            return new Promise(async (resolve, reject) => {

              try {
                var saveFont = ctx.font;
                if (font) {
                  ctx.font = font;
                }
                ctx.measureText(text).then((metrics) => {
                  ctx.font = saveFont;
                  resolve({text: text, metrics: metrics})
                }).catch(err => {
                  ctx.font = saveFont;
                  reject(err);
                })
              } catch(err) {
                reject(err);
              }
            });
          }}
        )
      }
    }
  }, [ref]);

  useEffect(() => {
    if (measureText) {
      global.measureText = measureText.f;
    }
  }, [measureText]);

  useEffect(() => {
    if (measureText && listener) {
      if (listener.f) {
        listener.f();
      }
    }
  }, [measureText, listener]);

  return (
    <Canvas style={{width: 1, height: 1}} ref={ref}/>
  );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants