Render SVG from URL for animation with Velocity.js

1k Views Asked by At

I am building an interface to preview animations applied to SVGs. I am trying to target an SVG's internal paths for animation with the Velocity.js library, in React. I'm using create-react-app.

I am able to drag-and-drop a .svg file into a react-dropzone input, which provides a callback with a File object. I can convert the File object to a base64 encoded DataURL, which I can then provide to an <object> tag. This renders the SVG with the full, expanded xml markup.

return (
    <object style={{
        width: '100%'
    }}
    id={`svg-${uuid}`} 
    type="image/svg+xml" data={dataURL}
/>)

Any attempt to access the contentDocument of the SVG, using javascript, (for the purposes of targeting g and path elements directly for animation) results in the following error:

document.getElementById('svg-665790ae-2a36-446e-9ea9-856442e8b1dc').contentDocument

Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLObjectElement': 
Blocked a frame with origin "http://localhost:3000" from accessing a cross-origin frame.

I am working on a local development environment, serving the React application on localhost:3000.

As a possible workaround, I uploaded the SVG in question to a remote AWS S3 storage bucket. I then retrieve the SVG via the remote URL, and am trying to render that, in some way. I have tried the following approaches:

React SVG <use> tag

<svg>
     <use xlinkHref={url}></use>
</svg>

This results in the following markup, but no visible SVG:

<svg>
    <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://s3.amazonaws.com/.../example.svg">
    </use>
</svg>

<object> tag

<object
    style={{
        width: '100%',
    }}
    id={`svg-${uuid}`} type="image/svg+xml" data={url}
/>

This results in the following markup, without a visible SVG, and it causes the SVG to be immediately downloaded as a file by the browser.

<object id="svg-..." type="image/svg+xml" data="https://s3.amazonaws.com/.../example.svg" style="width: 100%;"></object>

react-inlinesvg library

This library makes an XHR request to retrieve the SVG and then embeds it into the DOM.

<Isvg src={url}/>

This results in the following error:

XMLHttpRequest cannot load https://s3.amazonaws.com/.../example.svg. 
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://localhost:3000' is therefore not allowed access.

SVGInjector

First I mounted a simple <img> tag with the SVG url:

return (
    <img
        ref={node => this.testSVG = node}
        src={url} id="svg" type="image/svg+xml" />
)

This also results in the above XMLHttpRequest error. Attempts to call SVGInjector, from componentDidMount, with either the node ref (i.e., SVGInjector(this.testSVG)), or in this manner:

const el = document.getElementById('svg');
SVGInjector(el);

causes the warning to disappear but results in only a broken image tag displaying.

Any help in resolving these CORS warnings or guidance on the correct way to embed SVG in the DOM, in React, for the purposes of animation, would be greatly appreciated. Perhaps what I need to do is download the image via the server, and temporarily serve it directly from the server filesystem, e.g., from the /public folder. This is not my preferred approach, as I am already using AWS S3 for image hosting.

1

There are 1 best solutions below

0
On

The answer was to enable CORS in the AWS S3 console. See stackoverflow.com/questions/17533888/s3-access-control-allow-origin-header. The CORS configuration does not appear to be enabled by default, so I had to save it for it to take effect.

<!-- Sample policy -->
<CORSConfiguration>
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Thank you to sideshowbarker for this recommendation. As he mentioned, if you are serving images from another domain, which you do not have access to, you may need to use a proxy. See stackoverflow.com/a/43881141/441757, under the section How to use a CORS proxy to get around “No Access-Control-Allow-Origin header” problems, for details.