I would like to dynamically, partially fill a react-icons sourced Font Awesome Star using linear gradient. I have tried the following:
React Component with Inline Style - Setting the background of the parent span to gradient and making the SVG transparent. Can't seem to set the border around the SVG star to #FFFFFF so I see the parent span's whole background. See below:
import React from 'react'
import FaStar from 'react-icons/lib/fa/star'
const Stars = () => {
const inlineStyle = (pctFill) => {
return(
{background: 'linear-gradient(90deg, #134758 '+pctFill+'%, #d3d3d3 '+pctFill+'%)'}
)
}
return(
<div>
<span style={inlineStyle(50)}>
<FaStar />
</span>
</div>
)
}
export default Stars
I have also tried creating a linearGradient component and setting the path's fill="url(#component)", but react-icons path is the 3rd child to my parent span which I can't figure out how to access.
Please help
Was having the same issue, here's a simple working solution:
Add a < svg > element wrapping a < gradient > element to the component.
Add an id to the < gradient >
Link the gradient to the "stroke" property of the icon via "url(< id >)":
import { FiCheck } from 'react-icons/fi';
//...
<svg width="0" height="0">
<linearGradient id="blue-gradient" x1="100%" y1="100%" x2="0%" y2="0%">
<stop stopColor="#7a6ded" offset="0%" />
<stop stopColor="#591885" offset="100%" />
</linearGradient>
</svg>
<FiCheck style={{ stroke: "url(#blue-gradient)" }} />
obs: In some packs you may need to switch "stroke" to "fill".
<svg width="1em" height="1em">
<linearGradient id="blue-gradient" x1="100%" y1="100%" x2="0%" y2="0%">
<stop stopColor="#7a6ded" offset="0%" />
<stop stopColor="#591885" offset="100%" />
</linearGradient>
<FaFacebook style={{ fill: "url(#blue-gradient)"}}/>
My code worked in this way. Thanks a lot.
I think I found a solution, using additional svg and def tags. Only setback is that you must specify width and height of svg.
<Button className="border-0">
<svg width="20" height="20">
------------------------------- // add linearGradient
<defs>
<linearGradient id="myGradient" gradientTransform="rotate(90)">
<stop offset="5%" stopColor="gold" />
<stop offset="95%" stopColor="red" />
</linearGradient>
</defs>
--------------------------------------- //pass attr to react icon
<IconContext.Provider value={{ attr: {fill: "url('#myGradient')"}}}>
<FaInstagram />
</IconContext.Provider>
</svg>
</Button>
Hope this works!
Related
I'm using Recharts to draw some charts. Depending on the length of the data array, the chart doesn't start at x = 0 (not at the intersection of x and y) and doesn't finish at the max value of x:
How can I force it to always begin at x = 0 and occupy all the x axis?
This is how I'm implementing it:
<ResponsiveContainer width="100%" height={400}>
<ComposedChart
data={chartContent.content.chart}
//data={data}
margin={{ top: 5, bottom: 5, left: 5 }}
>
<defs>
<linearGradient
id="colorBtc"
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="5%"
stopColor="#ff9500"
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor="#ff9500"
stopOpacity={0}
/>
</linearGradient>
<linearGradient
id="colorStock"
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="5%"
stopColor="#00a1e4"
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor="#00a1e4"
stopOpacity={0}
/>
</linearGradient>
</defs>
<XAxis
dataKey="date"
tickFormatter={formatDate}
/>
<YAxis tickFormatter={formatYAxis} />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip
formatter={formatTooltip}
labelFormatter={formatDate}
/>
<Legend />
<Area
name={t("Total amount in BTC")}
type="monotone"
dataKey="investment_total_btc"
stroke="#ff9500"
fillOpacity={1}
fill="url(#colorBtc)"
/>
<Area
name={`${t("Total amount in")} ${
chartContent.content.symbol
}`}
type="monotone"
dataKey="investment_total_stock"
stroke="#00a1e4"
fillOpacity={1}
fill="url(#colorStock)"
/>
<Line
name={t("Total invested in $")}
dataKey="invested"
type="monotone"
stroke="#ff0000"
dot={false}
/>
</ComposedChart>
</ResponsiveContainer>
Unfortunately the API documentation is not so clear and I couldn't find a solution.
Thanks in advance
What helped in my scenario was to add dataMax + 1 to the domain parameter of the XAxis component.
Based on what the documentation says I would have assumed that adding 'dataMin' for the first element of the domain array would help. However, it was dataMax + 1 that resolved it for me
Full snippet
<XAxis
dataKey="date"
name="Time"
type="number"
domain={["dataMin", "dataMax + 1"]}
tickFormatter={(tickItem) => moment(tickItem).format("MMM Do YY")}
/>
Adding domain={["dataMin", "dataMax + 1"]} doesn't work, since the XAxis type in my case is category(the default value).
According to the library API document, in order to set domain, the XAxis type should be "number"reference
For category type XAxis, I found that Priyank Kachhela pointed out an alternative solution. You can specify the scale of XAxis to be point( the default scale is band ). It resolved my issue.
<XAxis
dataKey="date"
tickFormatter={formatDate}
scale="point"
/>
Trying to clip an Image component with an svg ClipPath is proving problematic. I can clip a Rect no problem, but when I replace the Rect with an Image, nothing shows up.
octogon.points = "80,0.3 23.6,23.6 0.3,80 23.6,136.4 80,159.7 136.4,136.4 159.7,80 136.4,2 3.6";
props.photoUri = "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252Fhalt-c46ef35c-1897-441e-840e-28147c0de5a9/ImagePicker/d68a7ddd-807e-4d26-95c2-bd47ddca803e.jpg"
<View style={styles.container}>
<Svg width={160} height={160} viewBox={`0 0 160 160`}>
<Defs>
<ClipPath id="clip">
<Polygon points={octogon.points} />
</ClipPath>
</Defs>
{/* <Rect x="0" y="0" width="160" height="160" fill="red" clipPath="#clip" /> */}
<Image x="0" y="0" width="160" height="160" source={{ uri: props.photoUri }} clipPath="#clip" />
</Svg>
</View>
The commented out Rect worked as intended and creates a red octogon. The Image returns blank.
I feel like I'm overlooking something simple. Any help is appreciated.
Answering my own question because I'm an idiot and hopefully it will help someone in the future. Be mindful of which components you are importing. I was using Image from 'react-native' instead of Image from 'react-native-svg'.
Once I swapped it over and changed source={{ uri: props.photoUri }} to href={{ uri: props.photoUri }}, it started working.
I have the following Codepen, where I'm trying to animate a stroke of a circle around an image.
So far, I've got a circle SVG which is clipping an image, but it doesn't show the stroke inside of clipPath.
How do I get the border to show?
class App extends React.Component {
render() {
return (
<svg width='48' height='48'>
<defs>
<clipPath id='circleView'>
<circle cx='24' cy='24' r='23' fill='none' stroke='red' strokeWidth='2' />
</clipPath>
</defs>
<image width='48' height='48' xlinkHref={'https://source.unsplash.com/random'} clipPath='url(#circleView)' />
</svg>
)
}
}
As Robert said, the child SVG figures in the clip-path line are not displayed.
Therefore, for the animation you need to add another circle outside the clip-path
<circle cx="25" cy="24" r="14" fill="none" stroke="red" strokeWidth="2" />
.container {
width:25%;
height:25%;
}
<div class="container">
<svg viewBox="0 0 48 48" >
<defs>
<clipPath id='circleView'>
<circle cx='24' cy='22' r='16' fill='none' stroke='red' stroke-width='2' />
</clipPath>
</defs>
<image width="100%" height="100%" xlink:href='https://i.stack.imgur.com/O9eO8.jpg'
clip-path='url(#circleView)' />
<circle cx='24' cy='22' r='16' fill='none' stroke='red' strokeWidth='2' />
</svg>
</div>
To animate a circle, use the change in the stroke-dashoffset attribute from maximum to zero. values="(100.48;0)"
Animation starts after click
.container {
width:25%;
height:25%;
}
<div class="container">
<svg id="svg1" viewBox="0 0 48 48">
<defs>
<clipPath id='circleView'>
<circle cx='24' cy='22' r='16' fill='none' stroke='red' stroke-width='2' />
</clipPath>
</defs>
<image width="100%" height="100%" xlink:href='https://i.stack.imgur.com/O9eO8.jpg' clip-path='url(#circleView)' />
<circle transform="rotate(-90 24 22)" cx="24" cy="22" r="16" fill='none' stroke='red' strokeWidth='2'
stroke-dasharray="100.48" stroke-dashoffset="100.48" >
<animate
attributeName="stroke-dashoffset"
dur="1s"
begin="svg1.click"
values="100.48;0"
fill="freeze"/>
</circle>
</svg>
</div>
Variant of animation with CSS
I added transparency animation to the circle animation.
The animation begins when you hover the mouse cursor.
.container {
width:25%;
height:25%;
}
#crc1 {
fill:skyblue;
stroke-width:1;
stroke:red;
stroke-dasharray:100.48;
stroke-dashoffset:100.48;
fill-opacity:0.9;
}
#crc1:hover {
animation: dash 1.5s ease-out forwards;
}
#keyframes dash {
0% { stroke-dashoffset: 100.48; fill-opacity:0.9; }
50% { fill-opacity:0.45;}
100% { stroke-dashoffset: 0; fill-opacity:0; }
}
#img1 {
clip-path: url(#circleView);
}
<div class="container">
<svg id="svg1" viewBox="0 0 48 48">
<defs>
<clipPath id='circleView'>
<circle cx='24' cy='22' r='16'/>
</clipPath>
</defs>
<image width="100%" height="100%" xlink:href='https://i.stack.imgur.com/O9eO8.jpg'
clip-path='url(#circleView)' />
<circle id="crc1" cx="24" cy="22" r="16" />
</svg>
</div>
NPM Package: https://www.npmjs.com/package/react-fabricjs
I'm using react-fabricjs and have a canvas with some icons showing. I'm now trying to get Grouping to work but I can't work out the syntax. Essentially I want to create this effect but using React JSX Code:
http://jsfiddle.net/anwjhs2o/1/
This is the code I have:
import React from 'react';
import {Canvas, Circle, Image, Path, Text, Rect, Ellipse, Triangle, Group} from 'react-fabricjs';
export default class CanvasFabric extends React.Component {
render() {
return (
<Canvas
ref="canvas"
width="556"
height="371"
>
<Circle
ref="circle"
radius={20}
left={100}
top={50}
stroke="green"
fill="blue"
blur={100}
color="rgba(0,0,0,0.6)"
/>
<Image
src="https://cloudinary-a.akamaihd.net/bountysource/image/twitter_name/d_noaoqqwxegvmulwus0un.png,c_pad,w_200,h_200,b_white/fabricjs.png"
width={64}
height={48}
left={10}
top={50}
/>
<Rect
ref="rect1"
width={50}
height={50}
left={150}
top={150}
fill="orange"
shadow="rgba(0,0,0,0.3) 5px 5px 5px"
/>
<Rect
ref="rect2"
width={60}
height={60}
left={145}
top={145}
stroke="purple"
strokeWidth={2}
blur={20}
/>
<Ellipse
ref="ellipse"
width={20}
height={100}
offsetX={350}
offsetY={250}
stroke="orange"
strokeWidth={2}
fill="yellow"
/>
<Triangle
ref="tri"
width={20}
height={20}
left={350}
top={250}
stroke="orange"
strokeWidth={1}
fill="black"
opacity={0.3}
/>
<Text text="Edit me"
top={300}
left={10}
shadow="rgba(0,0,0,0.3) 5px 5px 5px"
stroke="#ff1318"
strokeWidth={1}
fontStyle="italic"
fontFamily="Hoefler Text"
/>
</Canvas>
)
}
}
How could I say group the 2 Rect's - so they can be rotated/scaled etc together?
thankyou
Adam
** Extra Note: I'm no React Expert but I get the feeling the Fabric React Package is far from completed and this is why core functionality doesn't work.
I tried to check the lib, i did not get how to use the group.
The main point is you should just use fabricJS in a normal way even if you are using reactJS. Binding a fabricjs render to a react render may be slow or just unnecessary.
The group code is using:
While the Group constructor in fabric is different.
You can try 2 approaches, the only 2 that makes sense in react:
<Group>
<Rect
ref="rect1"
width={50}
height={50}
left={150}
top={150}
fill="orange"
shadow="rgba(0,0,0,0.3) 5px 5px 5px"
/>
<Rect
ref="rect2"
width={60}
height={60}
left={145}
top={145}
stroke="purple"
strokeWidth={2}
blur={20}
/>
</Group>
OR
<Group
objects={[<Rect
ref="rect1"
width={50}
height={50}
left={150}
top={150}
fill="orange"
shadow="rgba(0,0,0,0.3) 5px 5px 5px"
/>,
<Rect
ref="rect2"
width={60}
height={60}
left={145}
top={145}
stroke="purple"
strokeWidth={2}
blur={20}
/>]}
/>
I am using c3.js and my components are using CSS Modules. I wanted to modify the fill of points in the line chart with an image. For that in the render method I had added defs-pattern -
render(){
return (
<div>
<defs>
<pattern id="image" x="0" y="0" patternUnits="userSpaceOnUse" height="1" width="1">
<image x="0" y="0" src="http://dummyimage.com/20x20/faf2fa/0011ff.png&text=x" />
</pattern>
</defs>
<C3Chart {...this.getConfig()}/>
</div>
);
}
In the styles css file I had added -
container :global(.c3-target-subscriber .c3-circle-5) {
fill: url(#image) !important;
}
But nothing happens. I just want to show an image inside the point of the line chart.
Any help is highly appreciated.
A <defs> element must have a parent that is a SVG element and not a HTML element. You need something like this:
<svg>
<defs>
<pattern id="image" x="0" y="0" patternUnits="userSpaceOnUse" height="1" width="1">
<image x="0" y="0" src="http://dummyimage.com/20x20/faf2fa/0011ff.png&text=x" />
</pattern>
</defs>
<svg>
Also your CSS points to something with a id of gradient but your pattern's id is image.