I am trying to import a local SVG file in React but I keep coming across this error:
My code looks like this:
import React from "react";
import styled from "styled-components";
import { ReactComponent as Logo } from "./images/logo.svg";
const MainImage = styled.div`
height: 400px;
width: 100%;
background: #026857;
`;
const Home = () => {
return (
<div className="home">
<MainImage>
<Logo />
</MainImage>
</div>
);
};
export default Home;
I have tried many solutions offered by others such as importing default as Logo, creating an image prop to contain the local svg (<img src={'./images/logo.svg'}/> ) but none have given me any success so far. I believe I may have to add something to my Webpack config file but I'm not sure what it is and where I should put it, as the config file is nearly 800 lines of code.
Also, I am using SVG files from https://undraw.co/ if the information helps, it seems their illustrations have many tags in them which I have not seen in simple SVG icons.
<svg
id="f6dc6f51-58d1-4328-a543-5a2c5176acea"
dataName="Layer 1"
xmlns="http://www.w3.org/2000/svg"
width="922.18516"
height="747.35665"
viewBox="0 0 922.18516 747.35665"
>
<path
d="M420.91148,313.56734c-2.67984,100.75634,131.62869,203.61052,299.27661,203.61052S1154.817,318.5411,1028.01831,313.56734c-244.32514-9.5838-328.052-110.77046-303.55341-182.5C768.33985,2.60566,426.18809,115.17786,420.91148,313.56734Z"
transform="translate(-138.90742 -76.32167)"
fill="#3f3d56"
/>
<polygon
points="505.004 157.445 502.96 156.144 504.261 154.1 503.703 153.746 502.403 155.79 500.359 154.489 500.004 155.046 502.048 156.347 500.747 158.391 501.305 158.746 502.605 156.702 504.649 158.002 505.004 157.445"
fill="#fcce33"
/>
<polygon
points="657.004 305.445 654.96 304.144 656.261 302.1 655.703 301.746 654.403 303.79 652.359 302.489 652.004 303.046 654.048 304.347 652.747 306.391 653.305 306.746 654.605 304.702 656.649 306.002 657.004 305.445"
fill="#fcce33"
/>
...
assuming you are using Webpack 5, you need to configure your webpack loader to something like this using asset modules:
module: {
rules: [
{
test: /\.svg$/i,
type: 'asset/resource',
},
...
]
}
if you are using webpack 4, you can use file-loader instead of asset/resource:
module: {
rules: [
{
test: /\.svg$/i,
use: [
{
loader: 'file-loader',
},
],
},
...
]
}
then you can import your svg file like this:
import Logo from "./images/logo.svg";
//...
return <img src={Logo} />
I usually always convert each SVG into its own component like so
import * as React from "react";
const ArrowDownSVG = (props: React.SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
strokeWidth={2}
stroke="currentColor"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
viewBox="0 0 24 24"
{...props}
>
<path stroke="none" d="M0 0h24v24H0z" />
<path d="M6 9l6 6 6-6" />
</svg>
);
export default ArrowDownSVG;
Has worked for me in the past year or so
By leaving out the type for the props, you can use this in plain JS as well
Related
I have made a single contour .svg cursor (basically a triangle curved at the bottom) without any fill, and I want to be able to change its fill color. I can't just make few different svgs for this, due to fill color being picked by user.
QtGraphicalEffects library is not supported since Qt6, so all I could find on this topic was of little use. The solution for this might be to just drop my svg over a colored rectangle, but then I'll have to mask out the area outside of my cursor to make it transparent(or vice-versa), and I'm not sure if it's even possible. I also found some shader-based solutions but none of then seem to work for my case (might just be me being new to QML)
SVG:
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #414141;
stroke-width: 44px;
fill-rule: evenodd;
}
</style>
</defs>
<path id="Pointer_1" data-name="Pointer 1" class="cls-1" d="M300.291,48.248L556.347,560.4s-127.234-47.825-255.133-47.825c-128.157,0-256.979,47.825-256.979,47.825Z"/>
</svg>
[MULTIPLE EDITS]
Firstly, the latest builds of Qt6 do support QtGraphicalEffects via Qt5Compat.GraphicalEffects. https://doc.qt.io/qt-6/qtgraphicaleffects5-
The approach I prefer to use is to leverage the fact that most components have an icon property which has a built-in mechanism for recoloring an SVG. You set the following properties:
icon.source
icon.width
icon.height
icon.color
By creating two SVGs we can use the above approach to control the fill and stroke independently.
The typical component I use is Button, but, I strip off the default Button UI/UX and just leverage from the icon property as follows:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
SplitView {
anchors.fill: parent
orientation: Qt.Horizontal
Item {
SplitView.preferredWidth: parent.width / 2
SplitView.fillHeight: true
Item {
anchors.centerIn: parent
width: parent.width * 8 / 10
height: parent.height * 8 / 10
Button {
anchors.centerIn: parent
visible: fillCombo.currentText !== 'none'
background: Item { }
icon.source: "shape-fill.svg"
icon.width: Math.min(parent.width, parent.height)
icon.height: icon.width
icon.color: fillCombo.currentText
}
Button {
anchors.centerIn: parent
visible: strokeCombo.currentText !== 'none'
background: Item { }
icon.source: "shape-outline.svg"
icon.width: Math.min(parent.width, parent.height)
icon.height: icon.width
icon.color: strokeCombo.currentText
}
}
}
Item {
SplitView.fillWidth: true
SplitView.fillHeight: true
ColumnLayout {
anchors.centerIn: parent
Label {
text: qsTr("Stroke")
}
ComboBox {
id: strokeCombo
model: [ "lightsteelblue", "red", "orange", "yellow", "green", "blue" ]
}
Label {
Layout.topMargin: 20
text: qsTr("Fill")
}
ComboBox {
id: fillCombo
model: [ "none", "red", "orange", "yellow", "green", "blue" ]
}
}
}
}
}
// shape-outline.svg
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600">
<path stroke="red" stroke-width="44" fill="none" d="M300.291,48.248L556.347,560.4s-127.234-47.825-255.133-47.825c-128.157,0-256.979,47.825-256.979,47.825Z"/>
</svg>
// shape-fill.svg
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600">
<path stroke="none" fill="black" d="M300.291,48.248L556.347,560.4s-127.234-47.825-255.133-47.825c-128.157,0-256.979,47.825-256.979,47.825Z"/>
</svg>
You can Try it Online!
Alternatively, another approach is you can build your SVG as a data uri by adding a "data:image/svg+xml," prefix to your SVG string. Because you're using a data uri, you can make a function that generates the SVG based on use input:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
SplitView {
anchors.fill: parent
orientation: Qt.Horizontal
Item {
SplitView.preferredWidth: parent.width / 2
SplitView.fillHeight: true
Image {
anchors.centerIn: parent
width: parent.width * 8 / 10
height: parent.height * 8 / 10
source: todatauri(getsvg(strokeCombo.currentText, fillCombo.currentText))
fillMode: Image.PreserveAspectFit
}
}
Item {
SplitView.fillWidth: true
SplitView.fillHeight: true
ColumnLayout {
anchors.centerIn: parent
Label {
text: qsTr("Stroke")
}
ComboBox {
id: strokeCombo
model: [ "black", "red", "orange", "yellow", "green", "blue" ]
}
Label {
Layout.topMargin: 20
text: qsTr("Fill")
}
ComboBox {
id: fillCombo
model: [ "none", "red", "orange", "yellow", "green", "blue" ]
}
}
}
}
function todatauri(svg) {
return "data:image/svg+xml," + svg;
}
function getsvg(stroke, fill) {
return `<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #414141;
stroke-width: 44px;
fill-rule: evenodd;
}
</style>
</defs>
<path id="Pointer_1" data-name="Pointer 1" class="cls-1" stroke="${stroke}" stroke-width="44" fill="${fill}" d="M300.291,48.248L556.347,560.4s-127.234-47.825-255.133-47.825c-128.157,0-256.979,47.825-256.979,47.825Z"/>
</svg>
`;
}
}
You can Try it Online!
I am creating a gauge with a small marker which is a triangle, I already have it working here: https://codesandbox.io/s/blazing-sun-teo9oe?file=/src/Gauge.tsx
Except for the marker transition, I need it to move along the gauge arc, so when the component prop changes from 40 to 80 for example, the marker will move from 40 to 80, right now it always starts at 50 or the middle of the arc and then moves to the final value.
Feel free to change the Gauge prop value in the index.tsx file to see the problem. Transition duration is 10 seconds to see the problem easier.
D3 and React = 300 kB (minified!)
You can do it in native JavaScript:
customElements.define("svg-gauge", class extends HTMLElement {
connectedCallback() {
let arc = "m20 45a35 35 0 1170 0";
let col = this.getAttribute("color")||"green";
this.innerHTML =
`<input type="range" min="0" max="100" step="5" value="0"`+ // delete 2 lines
` oninput="this.parentNode.percent=this.value" /><br>`+ // just for demo
`<svg viewbox="0 0 100 100">
<path d="${arc}" stroke="grey" stroke-width="6" fill="none"
stroke-linecap="round"></path>
<path id="P" d="${arc}" stroke="${col}" stroke-width="8" pathLength="100"
fill="none" stroke-linecap="round" stroke-dasharray="75 35"></path>
<circle cx="0" cy="0" fill="#fff" r="4" stroke="${col}" stroke-width="3">
<animateMotion dur="0.5s" keyPoints="0;0.75"
fill="freeze" keyTimes="0;1" calcMode="linear" path="${arc}">
</animateMotion></circle>
<text x="55%" y="40%" text-anchor="middle"/>
</svg>`;
this.style.display='inline-block';
this.percent = this.getAttribute("value") || "50";
}
set percent(val = 0) {
this.setAttribute("value", val);
let dash = val + " " + (105 - val);
this.querySelector("#P").setAttribute('stroke-dasharray', dash);
this.querySelector("animateMotion").setAttribute('keyPoints', '0;'+val/100);
this.querySelector("text").innerHTML =`${val} %`;
this.querySelector("animateMotion").beginElement();
this.querySelector("input").value = val;
}
})
svg-gauge { width:180px }
<h3>Drag sliders:</h3>
<svg-gauge value="35" color="red"></svg-gauge>
<svg-gauge value="50" color="hotpink"></svg-gauge>
<svg-gauge value="75" color="blue"></svg-gauge>
I have a site using Bootstrap 5 that includes the following input tag:
<input class="form-check-input ms-1" id="validated" name="validated" type="checkbox" checked>
The inclusion of the form-check-input class causes the client to generate the error message:
Refused to load the image 'data:image/svg+xml,%3csvg
xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath
fill='none' stroke='%23fff' stroke-linecap='round'
stroke-linejoin='round' stroke-width='3' d='M6 10l3
3l6-6'/%3e%3c/svg%3e' because it violates the following Content
Security Policy directive: "img-src 'self' www.w3.org".
Can someone please lend me a clue as to why this is being blocked? I have tried all the permutations of data://www.w3.org, http://www.w3.org, *.w3.org, etc., in the CSP and none seem to satisfy the client.
This happens identically with a Chrome and Edge client.
You can also extract the SVGs into separate files using Webpack. For example, see the official documentation at https://getbootstrap.com/docs/5.2/getting-started/webpack/#extracting-svg-files
modules.exports = {
...,
module: {
rules: [
{
mimetype: 'image/svg+xml',
scheme: 'data',
type: 'asset/resource',
generator: {
filename: 'icons/[hash].svg'
}
}
]
}
};
Bootstrap CSS stylesheet contains .form-check-input class with data:-Url images:
.form-check-input:checked[type=checkbox] {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e");
}
.form-check-input:checked[type=radio] {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");
}
.form-check-input[type=checkbox]:indeterminate {
background-color: #0d6efd;
border-color: #0d6efd;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
}
To allow these images you have to add data: scheme-source into img-src directive.
I am trying to configure grunt-svgmin to remove the fill="none" attribute that is automatically added to transparent SVGs when exported from Adobe Illustrator.
I currently have the below configuration:
svgmin: {
options: {
plugins: [
{
removeViewBox: false
},
{
removeUselessStrokeAndFill: {
removeNone: true
}
},
{
removeEmptyAttrs: true
},
{
removeTitle: true
},
{
removeAttrs: {
attrs: ['xmlns', 'id', 'data-name']
}
}
]
},
dist: {
files: [{
expand: true,
cwd: 'static/images/svgs/',
src: ['**/*.svg'],
dest: 'static/images/svgs/'
}]
}
}
I was under the impression that the removeUselessStrokeAndFill plugin with the option of removeNone would be what I needed to have in order to remove fill="none" but it isn't working.
Does anyone have any suggestions / advice on what I need to do to get this working?
Thanks,
Jess
fill="none" is not useless for two reasons:
The default for fill is black.
It is an inheritable property, so if for example you had this fragment
<g fill="none">
<rect width="100" height="100" />
<circle r="50" fill="red" />
</g>
the rect would get a transparent fill, while the circle would be rendered red.
The option you quote has another function: if a grafical element has neither a visible stroke nor a visible fill, the whole element is deleted.
We have a Singe Page Application using AngularJS 1.5 and it works smoothly using Chrome but unfortunately we also support IE 11+ and probably Edge and using these browsers, the performance is close to horrible (more than two seconds delay). It seems that it has something to do with the way we are using css sprites because if I remove the background-image property everything works smoothly again. My experience on css is not very high thus I hope that somebody with a deeper insight has a hint about performance optimizations. The sprite svg file (325KB 2350px x 2340px) looks like this
<?xml version="1.0" encoding="utf-8"?>
<!-- SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 2350 2340" enable-background="new 0 0 2350 2340" xml:space="preserve">
<symbol id="ic_x5F_website_x5F_blk" viewBox="0 -50 50 50">
...
<use xlink:href="#ic_x5F_website_x5F_blk" width="50" height="50" id="XMLID_496_" x="0" y="-50" transform="matrix(1 0 0 -1 284.9999 155)" overflow="visible"/>
...
</svg>
And the relevant css/ less styles are
.ico() {
&:extend(.ic-mixin);
}
.ic {
.ic-mixin;
}
.ic-mixin {
background-image: url('../icons/spritesheet.svg');
background-size: #spritesheet-width #spritesheet-height;
display: inline-block;
width: 50px;
height: 50px;
}
.icon(#column, #row, #color: blk, #offset: 0px, #offsetY: #offset) {
.createIcon(#column, #row, #color, #offset, #offsetY);
}
.createIcon(#column, #row, #color, #offsetX: 0, #offsetY: #offsetX) {
#positionX: #initIconGapX + #iconColumnWidth * (#column - 1);
#positionY: #initIconGapY + #iconOuterSize * (#row - 1);
& when (#color = wht) {
background-position: -(#positionX + #iconOuterSize + #offsetX) -(#positionY + #offsetY);
}
& when (#color = blu) {
background-position: -(#positionX + #iconOuterSize * 2 + #offsetX) -(#positionY + #offsetY);
}
}
.icon-virtual {
&:after {
.ico();
.icon(4, 23, wht, 15px);
content: ' ';
width: 20px;
height: 20px;
background-color: #green;
position: absolute;
top: 2px;
right: 2px;
}
}
Why is Chrome so much faster? Is this a cache problem? Is IE trying to fetch the huge file over and over again? Could there be a problem with the svg file itself? Or is the css to complicated? Are we missing some important properties? Is IE too slow finding the correct background position?
Thanks a lot for any hint!
UPDATE
Exchanging the big file with a different much smaller svg does not help in improving the performance.