Animate SVG React Native - svg

I am trying to modify the height of a rectangle in svg. First of all my svg is correctly displayed before i use the Animated api. Then i try to bind a height variable to the SVG as So :
render() {
let AnimatedRectangle = Animated.createAnimatedComponent(Rect);
let {height} = this.state
return (
<View style={styles.wrapper}>
<Svg height="100" width="100" x="0" y="0" fill="#333">
<AnimatedRectangle
x="0"
y="0"
width="4"
height={height}
fill="#333"
/>
<Rect x="10" y="0" width="4" height="7" fill="#333" />
<Rect x="20" y="0" width="4" height="7" fill="#333" />
</Svg>
</View>
);}
Here is my state and componentDidMount method :
state = {
height: new Animated.Value(5)
};
componentDidMount() {
Animated.timing(
// Animate value over time
this.state.height, // The value to drive
{
toValue: 30 // Animate to final value of 1
}
).start(); // Start the animation
}
This is not working even though my Animation is working. Indeed, if i bind the height to a simple Animated.View, the height is correctly modified.
Here is the error i am getting :

Related

How to exclude shapes in svg or canvas?

Forgive my English
I am using svg with filters, but faced the following problem.
This is the base for svg. The result is expected:
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
<g>
<circle id="2" cx="50" cy="50" r="50"/>
<g id="1">
<rect x="0" y="0" width="50" height="50" fill="#ccc"/>
<rect x="50" y="50" width="50" height="50" fill="#ccc"/>
</g>
</g>
</svg>
But with the filter feComposite:
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
<defs>
<filter id="myFilter1">
<feImage href="#1" result="1"/>
<feImage href="#2" result="2"/>
<feComposite in="1" in2="2" operator="xor"/>
</filter>
</defs>
<g filter="url(#myFilter1)">
<circle id="2" cx="50" cy="50" r="50"/>
<g id="1">
<rect x="0" y="0" width="50" height="50" fill="#ccc"/>
<rect x="50" y="50" width="50" height="50" fill="#ccc"/>
</g>
</g>
</svg>
As you can see, the image is shifted. If you inspect the code, the blocks will not match the visible image:
Here with the addition of interactivity:
const value = (max = 100000000, min = 0) => Math.round(Math.random() * (max - min)) + min;
const createCircle = (size) => {
const r = value(10, 3);
const cx = value(size - r - 10, r + 10);
const cy = value(size - r - 10, r + 10);
return {
r,
cx,
cy
}
};
const createCircles = (counts, size) => Array(counts).fill().map(() => createCircle(size));
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
position: {
x: 0,
y: 0,
}
};
this.size = 300;
this.circlesData = createCircles(100, this.size);
const getCoords = (c, i) => c + (this.state.position.x * 0.002 * c * (i % 2 ? 1 : -1));
this.circles = () => this.circlesData.map((item, i) => <circle key = {`circles_12dew1_${i}`} cx={getCoords(item.cx, i)} cy={getCoords(item.cy, i)} r={item.r}/>);
}
onMouseMove = e => {
const position = {
x: e.pageX,
y: e.pageY,
};
this.setState({position});
}
render() {
return (
<div className = "App" >
<svg onMouseMove={this.onMouseMove} ref = {elem => this.svg = elem} xmlns = "http://www.w3.org/2000/svg" width = {this.size} height = {this.size} viewBox={`0 0 ${this.size} ${this.size}`}>
<defs>
<filter id="myFilter1">
<feImage href="#1" result="1"/>
<feImage href="#2" result="2"/>
<feComposite in ="1" in2="2" operator="xor"/>
</filter>
</defs>
<g id = "3" filter = "url(#myFilter1)" >
<circle id = "2" cx={this.size / 2 + 100} cy={this.size / 2 + 100} r={this.size / 3}/>
<g id="1"> {this.circles()} </g>
</g>
</svg>
</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById('root'));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
As you can see, a big circle is moving, but it shouldn't be doing that.
How to fix?
Or how to make the exception of figures as in the interactive example without svg? For example using canvas, thank you!
The image placement behaviour you are seeing is because <feImage> elements are positioned using the filter region, or filter primitive region.
https://www.w3.org/TR/SVG11/single-page.html#filters-feImageElement
By default the filter region is an area that is 10% bigger than the original object on all sides.
x="-10%" y="-10%" width="120%" height="120%"
This is done to cater for filter primitives, such as <feGuassianBlur>, which extend outside the original size and would otherwise get clipped.
To get the images to position how you want, change the filter region, or filter primitive region, to be the same size as the original object. For example:
<feImage href="#1" x="0" y="0" width="100%" height="100%" result="1"/>
Updated demo:
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
<defs>
<filter id="myFilter1">
<feImage href="#1" x="0" y="0" width="100%" height="100%" result="1"/>
<feImage href="#2" x="0" y="0" width="100%" height="100%" result="2"/>
<feComposite in="1" in2="2" operator="xor"/>
</filter>
</defs>
<g filter="url(#myFilter1)">
<circle id="2" cx="50" cy="50" r="50"/>
<g id="1">
<rect x="0" y="0" width="50" height="50" fill="#ccc"/>
<rect x="50" y="50" width="50" height="50" fill="#ccc"/>
</g>
</g>
</svg>

Dragging wrong riot component

I'm creating a draggable riot js based SVG element.
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g name="green" data-is="r-rect" x="30" y="230" width="256" height="64" rx="5" ry="5" draggable="true" fill="green"></g>
<g name="blue" data-is="r-rect" x="10" y="110" width="256" height="64" rx="5" ry="5" draggable="true" fill="blue"></g>
</svg>
r-rect.tag
<r-rect>
<rect ref="rect" onmousedown={hold} />
<script>
tag = this;
tag.hold = hold;
tag.x= Number.parseInt(opts.x);
tag.y= Number.parseInt(opts.y);
tag._name= opts.name;
tag.on("mount", function(e) {
tag.refs.rect.setAttribute("x", opts.x);
tag.refs.rect.setAttribute("y", opts.y);
tag.refs.rect.setAttribute("width", opts.width);
tag.refs.rect.setAttribute("height", opts.height);
opts.rx && (tag.refs.rect.setAttribute("rx", opts.rx) );
opts.ry && (tag.refs.rect.setAttribute("ry", opts.ry) );
})
function hold(e){
console.log(tag._name)
}
</script>
</r-rect>
I add 2 r-rect tags. Try to drag blue rectangle. But mousedown event of green rectangle triggers always .
http://jsfiddle.net/1k2gacy1/1/
I found the issue. It was happening because I was assigning the tag instance in global variable. So I've changed this line tag = this; to var tag = this;. And it solved the issue.

Resizing SVG rounded rectangle and preserving aspect ratio

I am trying to define an SVG rounded rectangle that I need later resize using jointjs resize function which uses standard SVG transform="scale()". In this case scale function changes everything.
Is there a way to resize rounded rectangle shape without touching rx and ry attributes?
Code bellow shows what I would like to achieve by using some scaling functions (Desired).
<svg xmlns="http://www.w3.org/2000/svg" >
<!-- Original -->
<rect x="10" y="0" rx="10" ry="10" height="50" width="50"/>
<!-- Scaled -->
<rect x="10" y="50" rx="10" ry="10" height="50" width="50" transform="scale(2)"/>
<!-- Desired -->
<rect x="10" y="250" rx="10" ry="10" height="100" width="100" />
</svg>
Don't scale it, resize it. It's pretty easy with jointjs:
var rounded = joint.dia.Element.define('basic.Rounded',
{
attrs: {
rect: {
fill: 'black',
rx: 10,
ry: 10,
// bind the width and height of the DOM element to the shape size
refWidth: '100%',
refHeight: '100%'
}
}
},
{
markup: '<g class="rotatable"><rect/></g>',
});
// create instance variations
// I.
new rounded().size(100, 100).position(20, 20).addTo(graph);
// II.
new joint.shapes.basic.Rounded().size(200, 200).position(150, 150).addTo(graph)
Result:
it uses 'special attributes':
https://resources.jointjs.com/docs/jointjs/v2.0/joint.html#dia.attributes.refWidth
If you have the shape as a Vectorizer object (where the SVG DOM element is accessible through the node property), e.g.:
var vel = V('<svg><rect x="10" y="0" rx="10" ry="10" height="50" width="50"/></svg>');
console.log(vel.node);
// <svg id="v-NUM"><rect x="10" y="0" rx="10" ry="10" height="50" width="50"/></svg>
You can then also access the rectangle as a Vectorizer object and change its properties this way:
var velRect = V(vel.node.childNodes[0]);
velRect.scale(1).attr('y', 250);
console.log(vel.node);
// <svg id="v-NUM">
// <rect x="10" y="250" rx="10" ry="10" height="50" width="50" transform="scale(1,1)"/>
// </svg>
vel.scale and vel.attr are functions from the Vectorizer API.

Using SVG logo as mask

I'm basing my exercise on the accepted answer in: Creating transparent text to show gradient color of underlying div
Here's my rendition in jsfiddle: http://jsfiddle.net/skrln/zSjgL/
The svg code of my logo:
<svg width="190" height="121">
<mask id="cutouttext">
<rect width="190" height="121" x="0" y="0" fill="white" />
<path id="number-two" d="M75.3,56.1c7.3-3,14.2-12.5,14.2-24c0-17.7-15.1-32.1-36.8-32.1H0v121.5h52.4c30,0,43.4-16.5,43.4-36.8
C95.8,72.3,87,59.8,75.3,56.1z M66.5,94.6h-49V79.7h0.1l27-22.1c3.5-2.8,5.3-6.1,5.3-9c0-4-3.2-7.6-8.4-7.6c-6.4,0-9.1,5.7-10.2,9
l-14.6-3.9c2.9-10.8,11.8-19.1,25.2-19.1c14.4,0,24.5,9.4,24.5,21.5c0,12.4-9,18.1-17.1,23.8l-10.4,7.3h27.6V94.6z" />
<polygon id="filler" points="190,33.9 190,0 101.6,0 101.6,121.5 190,121.5 190,87.6 141.4,87.6 141.4,74.7 177.1,74.7 177.1,46.6
141.4,46.6 141.4,33.9 " />
</mask>
<rect width="190" height="121" x="0" y="0" fill="white" mask="url(#cutouttext)" />
</svg>
The result so far:
Issue:
The mask isn't behaving the way I want to; I want the inner parts of the "B" and "E" to mask out the gray underlying div so you can see the background image like the image below:
I'm having trouble knowing what part of the logo is the and which one is the . Also I can't seem to figure out the logic behind the <mask> in the SVG.
There's nothing wrong with your SVG. You placed it on a grey background, so the bits that are masked out are grey.
What you want to do is remove the grey background from below the SVG image. There may be neater ways of doing this, but one approach is to use a table layout with the logo in one cell and the grey background in another.
Here's a JSFiddle link
HTML
<div class="gray">
<svg width="190" height="121">
<mask id="cutouttext">
<rect width="190" height="121" x="0" y="0" fill="white" />
<path d="M75.3,56.1c7.3-3,14.2-12.5,14.2-24c0-17.7-15.1-32.1-36.8-32.1H0v121.5h52.4c30,0,43.4-16.5,43.4-36.8
C95.8,72.3,87,59.8,75.3,56.1z M66.5,94.6h-49V79.7h0.1l27-22.1c3.5-2.8,5.3-6.1,5.3-9c0-4-3.2-7.6-8.4-7.6c-6.4,0-9.1,5.7-10.2,9
l-14.6-3.9c2.9-10.8,11.8-19.1,25.2-19.1c14.4,0,24.5,9.4,24.5,21.5c0,12.4-9,18.1-17.1,23.8l-10.4,7.3h27.6V94.6z" />
<polygon points="190,33.9 190,0 101.6,0 101.6,121.5 190,121.5 190,87.6 141.4,87.6 141.4,74.7 177.1,74.7 177.1,46.6
141.4,46.6 141.4,33.9 " />
</mask>
<rect width="190" height="121" x="0" y="0" fill="white" mask="url(#cutouttext)" />
</svg>
<div></div>
</div>
CSS
.body {
background: #550000 url('http://sciencelakes.com/data_images/out/7/8788677-red-background-wallpaper.jpg');
display: block;
height: 500px;
margin: 0;
}
.gray {
display:table-row;
width:100%;
height:121px;
}
.gray div, .gray svg {
display:table-cell;
}
.gray div {
background:gray;
width:100%;
}

SVG: dynamic size based on text, merging objects

<div style="float: left; padding: 10px; border: 1px solid;
background: #ccc">random text</div>
Is there a way to achieve something like this in SVG? I mean to have a rectangle and a text and:
a) rectangle's width and height are dynamic, so when I change the text, the rectangle adjust its size
b) when I move the rectangle, the text goes with it
And would it be easier to achieve something like this in <canvas>?
EDIT:
<defs>
<text id="text1" x="90" y="100" style="text-anchor:start;font-size:30px;">
THIS IS MY HEADER</text>
</defs>
<filter x="0" y="0" width="1" height="1" id="background">
<feFlood flood-color="gray"/>
<feComposite in="SourceGraphic"/>
</filter>
<use xlink:href="#text1" fill="black" filter="url(#background)"/>
Erik Dahlström proposed something like this. How to put padding to the background, how to add eg. shadow or border to the rectangle? And, this doesn't work in IE9, so I cannot accept it. I could just use <foreignObject> if there was a support for it in IE.
And I just figured out the answer for b) point of my question. You have to put both elements in the group:
<g>
<rect x="0" y="0" width="100" height="100" fill="red"></rect>
<text x="50" y="50" font-size="14" fill="blue" text-anchor="middle">Hello</text>
</g>
And then you can move the group using transform param:
<g transform="translate(x, y)">
Seems to work correct in every browser.
You can use JavaScript to adjust the box:
<svg xmlns="http:/www.w3.org/2000/svg" onload="init()" height="100" width="200">
<style type="text/css">
rect {
stroke:black;
stroke-width:1;
fill:#ccc;
}
</style>
<script type="text/javascript">
var text, rect
var padding = 10
function init() {
text = document.getElementsByTagName("text")[0]
rect = document.getElementsByTagName("rect")[0]
adjustRect()
}
function adjustRect() {
var bbox = text.getBBox()
rect.setAttribute("x",bbox.x - padding)
rect.setAttribute("y",bbox.y - padding )
rect.setAttribute("width",bbox.width + 2*padding)
rect.setAttribute("height",bbox.height + 2*padding)
}
</script>
<g>
<rect x="0" y="0" width="100" height="100" fill="red"></rect>
<text x="50" y="50" font-size="14" fill="blue" text-anchor="middle">Hello</text>
</g>
</svg>
<div>
<button onclick="text.textContent='Goodbye';adjustRect()">change text</button>
</div>
Use an svg filter to draw the background, that will adapt to the size and position of the text. See this answer for a full example.

Resources