I have the following SVG of dynamically rendered pie chart using react-minimal-pie-chart :-
<svg viewBox="0 0 100 100" width="100%" height="100%"><path d="M 75 50 A 25 25 0 0 1 54.34120444167326 74.6201938253052" fill="none" stroke-width="50" stroke="#8dcd81"><title>Excellent</title></path><path d="M 54.34120444167326 74.6201938253052 A 25 25 0 0 1 26.507684480352285 41.449496416858295" fill="none" stroke-width="50" stroke="#eefa6b"><title>Good</title></path><path d="M 26.507684480352285 41.449496416858295 A 25 25 0 0 1 75 49.99999999999999" fill="none" stroke-width="50" stroke="#FF6382"><title>Weak</title></path><text dominant-baseline="central" x="50" y="50" dx="19.151111077974452" dy="16.06969024216348" text-anchor="middle" style="font-size: 5px;">22 %Excellent</text><text dominant-baseline="central" x="50" y="50" dx="-19.15111107797445" dy="16.069690242163485" text-anchor="middle" style="font-size: 5px;">33 %Good</text><text dominant-baseline="central" x="50" y="50" dx="4.341204441673249" dy="-24.620193825305204" text-anchor="middle" style="font-size: 5px;">44 %Weak</text></svg>
This is my Reactjs code:-
const Element = (props) => {
return (
<text
dominant-baseline="central"
x={props.x}
y={props.y}
dx={props.dx}
dy={props.dy}
text-anchor="middle"
style={{ fontSize: "5px" }}
>
{`${Math.round(props.dataEntry.percentage)} %`}
{props.dataEntry.title}
</text>
);
};
This is codesandbox full Reactjs example:-
https://codesandbox.io/s/throbbing-moon-ejs67?file=/src/App.js
How can i line break between the texts (excellent - good ..) and their percentage .
As Danny says - just stick in a tspan. These settings seem to work ok:
const Element = (props) => {
return (
<text
dominant-baseline="central"
x={props.x}
y={props.y}
dx={props.dx}
dy={props.dy}
text-anchor="middle"
style={{ fontSize: "5px" }}
>
{`${Math.round(props.dataEntry.percentage)} %`}
<tspan dx="-12" dy="5">
{props.dataEntry.title}
</tspan>
</text>
);
};
Related
How can I add an image to the path in my SVG and also rotate the text inside the path and make it closer to the edges of the path?
I am currently using the following code in my React application to create a roulette wheel with multiple sections. Each section is represented by a path that is filled with a specific color. I would like to add an image inside each path and also rotate the text inside the path and make it closer to the edges of the path.
const RouletteWheel = () => {
const items = ["item1", "item2", "item3", "item4"];
const colors = ["blue", "red", "green", "yellow", "brown", "purple"];
const numItems = items.length;
const angle = items.length === 1 ? 0 : 360 / numItems;
return (
<svg viewBox="0 0 100 100" className="svg-wheel">
{items.length > 1 ? (
items.map((item, index) => {
const x1 = 50 + 45 * Math.cos(angle * index * (Math.PI / 180));
const y1 = 50 + 45 * Math.sin(angle * index * (Math.PI / 180));
const x2 = 50 + 45 * Math.cos(angle * (index + 1) * (Math.PI / 180));
const y2 = 50 + 45 * Math.sin(angle * (index + 1) * (Math.PI / 180));
return (
<React.Fragment>
<defs>
<path
id="text-path"
d={`M 50 50 L ${x1} ${y1} A 45 45 0 0 1 ${x2} ${y2} Z`}
/>
</defs>
<path
key={`path-${index}`}
id={`path-${index}`}
d={`M 50 50 L ${x1} ${y1} A 45 45 0 0 1 ${x2} ${y2} Z`}
fill={`${colors[index]}`}
></path>
<text
x={20}
dy={25}
fill="white"
textAnchor="middle"
fontSize={4}
>
<textPath transform={`rotate(100)`} xlinkHref={`#path-${index}`}>{item}</textPath>
</text>
</React.Fragment>
);
})
) : (
<circle key={"circle"} cx="50" cy="50" r="45" fill="url(#pattern1)" />
)}
</svg>
);
};
ReactDOM.render(
RouletteWheel(),
document.getElementById('container')
);
<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="container"></div>
The following examples in plain SVG. In the first example I just implemented a solid color using the stroke of a circle (#c1).
In the second example I replaced the solid colors with images. Here the circle (#c1) acts as mask for the image. The tricky part is to place the image in the right angle together with the mask. In general all the rotations are calculated from a circle (360) split in 5 items. So 72 degrees each.
In both examples the text is placed along a circle (#c2) using a textpath element and the attribute startOffset. The dominant-baseline of the text is "hanging" -- so the text is place "under" the circle/arc. The placement of the text can be adjusted with the radius of circle (#c2).
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" width="300">
<defs>
<circle id="c1" r="23" fill="none" stroke-width="46"
stroke-dasharray="72 360" pathLength="360" />
<circle id="c2" r="45" fill="none" pathLength="360" />
</defs>
<g transform="translate(50 50)">
<use href="#c1" stroke="blue" transform="rotate(0)" />
<use href="#c1" stroke="red" transform="rotate(72)" />
<use href="#c1" stroke="green" transform="rotate(144)" />
<use href="#c1" stroke="yellow" transform="rotate(216)" />
<use href="#c1" stroke="brown" transform="rotate(288)" />
<text font-size="6" fill="white" text-anchor="middle"
dominant-baseline="hanging">
<textPath href="#c2" startOffset="36">item 1</textPath>
<textPath href="#c2" startOffset="108">item 2</textPath>
<textPath href="#c2" startOffset="180">item 3</textPath>
<textPath href="#c2" startOffset="252">item 4</textPath>
<textPath href="#c2" startOffset="324">item 5</textPath>
</text>
</g>
</svg>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" width="300">
<defs>
<mask id="m1">
<circle r="23" fill="none" stroke="white" stroke-width="46"
stroke-dasharray="72 360" pathLength="360" />
</mask>
<circle id="c2" r="45" fill="none" pathLength="360" />
</defs>
<g transform="translate(50 50)">
<g mask="url(#m1)">
<image href="https://via.placeholder.com/300/09f/fff.png" width="60"
x="-30" y="-60" transform="rotate(126)" />
</g>
<g mask="url(#m1)" transform="rotate(72)">
<image href="https://via.placeholder.com/300/080/fff.png" width="60"
x="-30" y="-60" transform="rotate(126)" />
</g>
<g mask="url(#m1)" transform="rotate(144)">
<image href="https://via.placeholder.com/300/800/fff.png" width="60"
x="-30" y="-60" transform="rotate(126)" />
</g>
<g mask="url(#m1)" transform="rotate(216)">
<image href="https://via.placeholder.com/300/f0f/fff.png" width="60"
x="-30" y="-60" transform="rotate(126)" />
</g>
<g mask="url(#m1)" transform="rotate(288)">
<image href="https://via.placeholder.com/300/022/fff.png" width="60"
x="-30" y="-60" transform="rotate(126)" />
</g>
<text font-size="6" fill="white" text-anchor="middle"
dominant-baseline="hanging">
<textPath href="#c2" startOffset="36">item 1</textPath>
<textPath href="#c2" startOffset="108">item 2</textPath>
<textPath href="#c2" startOffset="180">item 3</textPath>
<textPath href="#c2" startOffset="252">item 4</textPath>
<textPath href="#c2" startOffset="324">item 5</textPath>
</text>
</g>
</svg>
So I have this svg as the footer and when I check in the developer tools it shows that it is taking up half of the page. I want the svg to only contain in the expected space without taking up extra space in the page since other elements cannot be positioned due to this issue.
<footer className='absolute bottom-0 w-full'>
<svg
className='fill-green'
viewBox='0 0 500 150'
preserveAspectRatio='none'
xmlns='http://www.w3.org/2000/svg'
>
<defs>
<filter id='shadow'>
<feDropShadow
dx='0'
dy='0'
stdDeviation='2'
flood-color='#159F68'
/>
</filter>
<path
id='footer-shadow'
className='stroke-none'
d='M29.86,179.11 C100.86,200.36 200.94,60.48 450.84,180.09 L363.96,855.44 L0.00,190.00 Z'
></path>
</defs>
<g>
<use href='#footer-shadow' filter='url(#shadow)'></use>
<text
className='fill-modern-green text-sm'
x='52%'
y='95%'
dominant-baseline='middle'
text-anchor='middle'
>
All rights reserved.
</text>
</g>
</svg>
</footer>
You can define width and height through the styles attribute for footer and svg. For example:
{/* Height of footer is 30px */}
<footer className='absolute bottom-0 w-full' style={{width: '100%', height: '30px'}}>
{/* SVG Height based on the footer height */}
<svg
style={{width: '100%', height: '100%'}}
className='fill-green'
viewBox='0 0 500 150'
preserveAspectRatio='none'
xmlns='http://www.w3.org/2000/svg'
>
<defs>
<filter id='shadow'>
<feDropShadow
dx='0'
dy='0'
stdDeviation='2'
flood-color='#159F68'
/>
</filter>
<path
id='footer-shadow'
className='stroke-none'
d='M29.86,179.11 C100.86,200.36 200.94,60.48 450.84,180.09 L363.96,855.44 L0.00,190.00 Z'
></path>
</defs>
<g>
<use href='#footer-shadow' filter='url(#shadow)'></use>
<text
className='fill-modern-green text-sm'
x='52%'
y='95%'
dominant-baseline='middle'
text-anchor='middle'
>
All rights reserved.
</text>
</g>
</svg>
</footer>
I have two components, one will contain a button that will trigger the cart icon change when clicked, while the second component will contain the cart icon to be changed. I have tried but i have not been able to achieve this.Here is an image to explain better
Component A
import React,{useState} from 'react'
const Cart = () => {
const [itemCount, setItemCount] = useState('');
return (
<div>
<button
style={{width: '10%', height: '10%', padding:'20px'}}
onClick={() => {
setItemCount(itemCount + 1);
}}
>
{" "}
</button>
</div>
)
}
export default Cart
Component B
import React from 'react'
import Cart from './Cart'
const CartShow = (props) => {
return (
<div style={{border: '2px solid red', background: 'black', width: '30%', margin: '100px',
padding: '30px'}}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M9 22C9.55228 22 10 21.5523 10 21C10 20.4477 9.55228 20 9 20C8.44772 20 8 20.4477
8 21C8 21.5523 8.44772 22 9 22Z" stroke="white" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round"/>
<path d="M20 22C20.5523 22 21 21.5523 21 21C21 20.4477 20.5523 20 20 20C19.4477 20 19
20.4477 19 21C19 21.5523 19.4477 22 20 22Z" stroke="white" stroke-width="2" stroke-
linecap="round" stroke-linejoin="round"/>
<path d="M1 1H5L7.68 14.39C7.77144 14.8504 8.02191 15.264 8.38755 15.5583C8.75318 15.8526
9.2107 16.009 9.68 16H19.4C19.8693 16.009 20.3268 15.8526 20.6925 15.5583C21.0581 15.264
21.3086 14.8504 21.4 14.39L23 6H6" stroke="white" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round"/>
{props.itemCount}</svg>
<Cart/>
</div>
)
}
export default CartShow
Assuming your code works except the state change. following should work(not tested):
Component A
const Cart = (props) =>
<div>
<button
style={{width: '10%', height: '10%', padding:'20px'}}
onClick={() => props.setItemCount(props.itemCount + 1)}
>
{" "}
</button>
</div>
export default Cart
Component B
import React,{useState} from 'react'
import Cart from './Cart'
const CartShow = () => {
const [itemCount, setItemCount] = useState(0);
return (
<div style={{border: '2px solid red', background: 'black', width: '30%', margin: '100px',
padding: '30px'}}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M9 22C9.55228 22 10 21.5523 10 21C10 20.4477 9.55228 20 9 20C8.44772 20 8 20.4477
8 21C8 21.5523 8.44772 22 9 22Z" stroke="white" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round"/>
<path d="M20 22C20.5523 22 21 21.5523 21 21C21 20.4477 20.5523 20 20 20C19.4477 20 19
20.4477 19 21C19 21.5523 19.4477 22 20 22Z" stroke="white" stroke-width="2" stroke-
linecap="round" stroke-linejoin="round"/>
<path d="M1 1H5L7.68 14.39C7.77144 14.8504 8.02191 15.264 8.38755 15.5583C8.75318 15.8526
9.2107 16.009 9.68 16H19.4C19.8693 16.009 20.3268 15.8526 20.6925 15.5583C21.0581 15.264
21.3086 14.8504 21.4 14.39L23 6H6" stroke="white" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round"/>
{itemCount}</svg>
<Cart itemCount={itemCount} setItemCount={setItemCount} />
</div>
)
}
export default CartShow
Edit: to show the numbers on on the cart, check this link: How to put the number at top right corner of cart icon?
Since Cart is a child of CartShow you should move the state to CartShow ( or have a different parent component that contains both of them ). Then pass a func down to Cart to update itemCount
I am trying to develop a simple screen in react native where it displays an svg image. I am trying to make the svg image use the full device width and height (100%). But the problem is, it is not consistent across different devices. For example, it is rendering properly in Iphone 8 Plus but not scaling properly in Ipad or Iphone 8. I heard the SVG files are scalable across all devices without losing quality. But I am not sure what I am missing here. I have posted the code and images below for reference.
Before posting here, I have did a considerable amount of research in Github and here as well.
I have also followed the steps mentioned in one of the similar posts (How to find correct values for width, height and viewBox with react-native-svg) but its not working. Can any please guide me on the right direction to fix the issue.
React Native Screen Component Code
import * as React from "react";
import Svg, { Defs, G, Path, Text, TSpan, Circle } from "react-native-svg";
/* SVGR has dropped some elements not supported by react-native-svg: style */
import { Dimensions, View } from "react-native";
function SvgComponent(props) {
const originalWidth = 1080;
const originalHeight = 1920;
const aspectRatio = originalWidth / originalHeight;
const windowWidth = Dimensions.get("window").width;
return (
<View style={{ width: windowWidth, aspectRatio }}>
<Svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1080 1920"
width="100%"
height="100%"
>
<Defs></Defs>
<G id="prefix__Layer_2" data-name="Layer 2">
<G id="prefix__Layer_1-2" data-name="Layer 1">
<Path fill="#006837" d="M.5.5h1079v1919H.5z" />
<Path d="M1079 1v1918H1V1h1078m1-1H0v1920h1080V0z" />
<Text
transform="matrix(1.5 0 0 1 13 148.09)"
fontFamily="ArialMT,Arial"
fill="#fff"
fontSize={106}
>
<TSpan className="prefix__cls-3">{"T"}</TSpan>
<TSpan x={62.83} y={0}>
{"O"}
</TSpan>
<TSpan x={145.28} y={0} letterSpacing="-.02em">
{"P"}
</TSpan>
<TSpan x={214.07} y={0} />
<TSpan x={243.52} y={0} letterSpacing="-.07em">
{"P"}
</TSpan>
<TSpan x={306.35} y={0} letterSpacing={0}>
{"AGE"}
</TSpan>
<TSpan className="prefix__cls-3" x={530.21} y={0} />
<TSpan x={557.74} y={0}>
{"TS"}
</TSpan>
</Text>
<Text
transform="matrix(1.5 0 0 1 13 1888.09)"
fontSize={79}
fontFamily="ArialMT,Arial"
fill="#fff"
>
{"BOT"}
<TSpan className="prefix__cls-8" x={162.4} y={0}>
{"T"}
</TSpan>
<TSpan x={209.23} y={0}>
{"OM "}
</TSpan>
<TSpan x={358.43} y={0} letterSpacing="-.07em">
{"P"}
</TSpan>
<TSpan x={405.26} y={0}>
{"AGE"}
</TSpan>
<TSpan className="prefix__cls-8" x={572.09} y={0} />
<TSpan x={592.62} y={0}>
{"TS"}
</TSpan>
</Text>
<Circle className="prefix__cls-10" cx={514.5} cy={396.5} r={129} />
<Path d="M514.5 268a128.51 128.51 0 11-90.86 37.64A127.66 127.66 0 01514.5 268m0-1A129.5 129.5 0 10644 396.5 129.5 129.5 0 00514.5 267z" />
<Circle className="prefix__cls-10" cx={179.5} cy={729.5} r={129} />
<Path d="M179.5 601a128.51 128.51 0 11-90.86 37.64A127.66 127.66 0 01179.5 601m0-1A129.5 129.5 0 10309 729.5 129.5 129.5 0 00179.5 600z" />
<Circle className="prefix__cls-10" cx={852.5} cy={748.5} r={129} />
<Path d="M852.5 620a128.51 128.51 0 11-90.86 37.64A127.66 127.66 0 01852.5 620m0-1A129.5 129.5 0 10982 748.5 129.5 129.5 0 00852.5 619z" />
<Circle className="prefix__cls-10" cx={514.5} cy={1007.5} r={129} />
<Path d="M514.5 879a128.51 128.51 0 11-90.86 37.64A127.66 127.66 0 01514.5 879m0-1A129.5 129.5 0 10644 1007.5 129.5 129.5 0 00514.5 878z" />
<Path
className="prefix__cls-10"
d="M36.99 1244.17l-28.95-91.2 64.51-70.67 93.46 20.53 28.95 91.2-64.51 70.67-93.46-20.53z"
/>
<Path d="M72.72 1082.85l92.9 20.4 28.77 90.65-64.11 70.25-92.9-20.4-28.77-90.65 64.11-70.25m-.34-1.1l-64.9 71.1 29.12 91.75 94 20.65 64.9-71.1-29.1-91.75-94-20.65z" />
<Path
className="prefix__cls-10"
d="M863.74 1280.56l-66.18-120.49 71.26-117.56 137.44 2.93 66.18 120.49-71.26 117.56-137.44-2.93z"
/>
<Path d="M869.1 1043l136.86 2.92 65.9 120-71 117.07-136.86-2.92-65.9-120 71-117.07m-.56-1l-71.56 118 66.46 121 138 3 71.56-118-66.46-121-138-3z" />
<Path
className="prefix__cls-10"
d="M293.27 1553.17l35.61-150.02 147.73-44.18 112.12 105.86-35.61 150.02-147.73 44.18-112.12-105.86z"
/>
<Path d="M476.48 1359.53L588.18 1465l-35.48 149.46-147.18 44L293.82 1553l35.48-149.47 147.18-44m.26-1.12l-148.28 44.34-35.75 150.59 112.55 106.25 148.28-44.34 35.75-150.59-112.55-106.25z" />
<Path
className="prefix__cls-10"
d="M708.09 1692.49a21.08 21.08 0 01-20.4-26l54.77-230.76a21 21 0 0120.54-16.16 20.61 20.61 0 0114 5.37l185.77 166a21.07 21.07 0 01-8.55 36l-240.55 64.78a21.15 21.15 0 01-5.58.77z"
/>
<Path d="M763 1419.07v1a20.16 20.16 0 0113.61 5.24l185.77 166a20.55 20.55 0 01-8.35 35.18l-240.55 64.79a21 21 0 01-5.43.73 20.58 20.58 0 01-19.92-25.33L743 1435.9a20.54 20.54 0 0120-15.83v-1m0 0a21.53 21.53 0 00-21 16.59l-54.8 230.76a21.49 21.49 0 0026.58 25.8l240.55-64.78a21.56 21.56 0 008.76-36.9l-185.77-166a21.27 21.27 0 00-14.29-5.5z" />
</G>
</G>
</Svg>
</View>
);
}
export default SvgComponent;
Perfect Rendering in Iphone 8 Plus
Bad Rendering in IPad
I want to animate all three circles using this one script.
But to achieve this, I need to create 3 variables like 'c1' right?
I got the code to work by duplicating everything:
http://jsfiddle.net/JwkYm/339/
It has to be easier then this. Thanks for your help in advance.
(function () {
// math trick 2*pi*57 = 358, must be less than 360 degree
var circleArr = ['orange-halo','orange-halo2','orange-halo3'];
var myTimerArr = ['myTimer','myTimer2','myTimer3'];
var interval = 30;
var angle = [0,0,0];
var angle_increment = 6;
var c1 = setInterval(function () {
var x = 0;
while(x >= 2)
{
document.getElementById(circleArr[x]).setAttribute("stroke-dasharray", angle[x] + ", 20000");
document.getElementById(myTimerArr[x]).innerHTML = parseInt(angle[x]/360*100) + '%';
if (angle[x] >= 270)
{
c1.clearInterval(c1.timer);
}
angle[x] += angle_increment;
x++;
}
}.bind(this), interval);
#myTimer, #myTimer2, #myTimer3 {
fill: #000099;
font-size: 40px;
font-family: Myriad light;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 300 300" preserveAspectRatio="none" style="width:300; height:300; top:0; left:0;">
<circle cx="100" cy="100" r="73" id="blue-halo" fill="none" stroke="#000099" stroke-width="30" />
<circle cx="100" cy="100" r="73" id="orange-halo" fill="none" stroke="#FD6400" stroke-width="31" stroke-dasharray="0,20000" transform="rotate(-90,100,100)" />
<text id="myTimer" text-anchor="middle" x="100" y="110">0%</text>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 300 300" preserveAspectRatio="none" style="width:300; height:300; top:0; left:0;">
<circle cx="100" cy="100" r="73" id="blue-halo2" fill="none" stroke="#000099" stroke-width="30" />
<circle cx="100" cy="100" r="73" id="orange-halo2" fill="none" stroke="#FD6400" stroke-width="31" stroke-dasharray="0,20000" transform="rotate(-90,100,100)" />
<text id="myTimer2" text-anchor="middle" x="100" y="110">0%</text>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 300 300" preserveAspectRatio="none" style="width:300; height:300; top:0; left:0;">
<circle cx="100" cy="100" r="73" id="blue-halo2" fill="none" stroke="#000099" stroke-width="30" />
<circle cx="100" cy="100" r="73" id="orange-halo2" fill="none" stroke="#FD6400" stroke-width="31" stroke-dasharray="0,20000" transform="rotate(-90,100,100)" />
<text id="myTimer3" text-anchor="middle" x="100" y="110">0%</text>
</svg>
http://jsfiddle.net/JwkYm/341/