Как я могу использовать элемент SVG, созданный с помощью JSX, в качестве источника изображения на холсте?

У меня есть приложение ReactJS. Я динамически генерирую изображения SVG как компоненты JSX. Пока все хорошо, но теперь мне нужно использовать SVG в качестве источника изображения в элементе Canvas. Это прекрасно работает со статическими файлами SVG, но как я могу получить динамический SVG на холсте?

Упрощенная версия появится во фрагменте. В методе componentDidMount показаны два подхода к вызову drawImage: создание несмонтированного SvgSource и использование ссылки на источник, смонтированный на странице, но оба они терпят неудачу.

class App extends React.Component {

	// On mount, paint the SVG in the canvas
	componentDidMount(){
  	let ctx = this.canvasRef.getContext("2d")
    
    // THIS DOES NOT WORK:
  	//ctx.drawImage(new SvgSource({fill: "green"}), 50, 50);
    
    // NOR DOES THIS:
    //ctx.drawImage(this.svgRef, 50, 50);
    
    /* TypeError: Argument 1 of CanvasRenderingContext2D.drawImage could not be converted to any of: HTMLImageElement, SVGImageElement, HTMLCanvasElement, HTMLVideoElement, ImageBitmap. */
  }

  render() {
  return (
  <div>     
    {/* This works, inserting the SvgSource element directly - but that's not what I want. */}
    <SvgSource ref = {s => this.svgRef = s} fill = "blue" />
    
    {/* I want to use the SVG as an image source in this canvas. */}
  	<canvas 
      ref = {
      c => this.canvasRef = c 
      /* NB: using older ref syntax because jsFiddle uses React .14 - use CreateRef with version 16 */
      } 
      width = {200} 
      height = {200} />
  </div>
  );
  }
}

// Our JSX SVG component that provides the custom image to use in the canvas
class SvgSource extends React.Component {
  render() {
    return (
      <svg width = {100} height = {100} viewBox = "0 0 100 100">
        <circle cx = {50} cy = {50} r = {25} fill = {this.props.fill || "red"}/>
      </svg>
    );
  }
}

ReactDOM.render(<App />, document.querySelector("#app"))
body {
  background: #20262E;
  padding: 20px;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  min-height: 200px;
}
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id = "app"></div>

Чтобы уточнить: я знаю, как вставлять обычные изображения SVG в элементы холста. Проблема заключается в преобразовании JSX SVG в действительный источник для drawImage в контексте холста, чтобы все это можно было сделать в компонентах React.

Спасибо, я таких не видел, но они мне не особо помогают. Я знаю, как нарисовать SVG на холсте. Это конкретный шаг преобразования объекта JSX, представляющего SVG, во что-то, что контекст примет как изображение, которое ускользает от меня.

y6nH 18.02.2019 15:48

Вам просто нужно преобразовать этот jsx-объект в разметку. Связанные ответы дают вам DOM для разметки uri для частей изображения. И хотя я не реагирующий ниндзя, кажется, у вас уже есть эта разметка под рукой.

Kaiido 18.02.2019 15:56
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
3
2 447
1

Ответы 1

Это не очень изящный способ сделать это, но вот что у меня получилось:

class SvgSource extends React.Component {
    generateSvg() {
        return "data:image/svg+xml;base64," + 
            btoa(`<svg xmlns = "http://www.w3.org/2000/svg" width = "100" height = "100" viewBox = "0 0 100 100"><circle cx = "50" cy = "50" r = "25" fill = "${this.props.fill}" /></svg>`);
    }

    render() {
        return (<img src = {this.generateSvg()} />);
    }
}

Вместо того, чтобы делать SVG объектом JSX, я просто создаю его как строку, кодируя в base64 для использования в качестве URL-адреса данных. Затем я могу вернуть элемент <img> JSX из функции render(), чтобы использовать изображение непосредственно на странице. Когда мне нужно использовать его на холсте, я могу вызвать new SvgSource({fill: "green"}).generateSvg() напрямую, используя возвращаемую строку в атрибуте <img> простого (не JSX) элемента src.

Я не мог найти лучшего способа отобразить SVG-разметку, не размещая ее на странице.

Другие вопросы по теме