How to achieve overflow hidden with svg animation? - javascript

I am trying to animate a path within an svg.
The path is of a squiggly line to look like a wave. What I am trying to do is have this path translate horizontally on a infinite loop to make it look as though the water is moving.
You can see a similar effect achieved by the outline, only going vertically and this is not within an SVG: https://theoutline.com/
Here is the JsFiddle I have so far with an svg wave/squiggle.
<g class="wave-container">
<path class="wave" d="M1,1c1.6,0,1.6,1.6,3.3,1.6S5.9,1,7.6,1c1.6,0,1.6,1.6,3.3,1.6S12.5,1,14.2,1s1.6,1.6,3.3,1.6
c1.6,0,1.6-1.6,3.3-1.6"/>
</g>
https://jsfiddle.net/bje5rxzs/
I am trying to be able to animate this wave horizontally within it's group/container. I know SVG does not support overflow:hidden; within, so how would this be achieved?
Would a mask work? I am open to using a snap.svg if required. I will have other elements within this svg moving, so the squiggle needs to be within the same svg.
Any help much appreciated! Thank you :)

Updated
Like any animation where you are changing the position of something you can use transforms.
The key here is making the squiggly path wider than the svg viewbox, and setting overflow:hidden on svg (which is supported).
Since your illustration is tiny I had to make the svg viewbox tiny as well, only 15px wide, so that the path could overlap the svg container.
<svg version="1.1" x="0px" y="0px" width="15px" height="3.6px" viewBox="0 0 15 3.6">
<path class="white-path animate" d="M1,1c1.6,0,1.6,1.6,3.3,1.6S5.9,1,7.6,1c1.6,0,1.6,1.6,3.3,1.6S12.5,1,14.2,1s1.6,1.6,3.3,1.6 c1.6,0,1.6-1.6,3.3-1.6"/>
</svg>
css:
svg {
overflow:hidden;
}
.white-path {
fill:none;
stroke:#FFFFFF;
stroke-width:2;
stroke-linecap:round;
stroke-linejoin:round;
stroke-miterlimit:10;
}
#keyframes squiggle {
from {
transform: translateX(-7px)
}
to {
transform: translateX(0px)
}
}
.animate {
animation: squiggle 1s linear infinite;
}
I used a negative x translation, and through trial and error picked the right distance so the looping was seamless.
Demo:
https://jsfiddle.net/bje5rxzs/6/

you can just nest 2 svg.
var x = -5
setInterval(() => {
wave.setAttribute("transform", `translate(${x},0)`)
if (x >= 0) {
x = -5
} else {
x++
}
}, 100)
<svg viewBox="0 0 300 300" width="200" height="200">
<circle cx="150" cy="150" r="150" fill="red"/>
<svg x="50" y="50" viewBox="1 0 10 3.5" width="100" height="35">
<path id="wave" transform="translate(-5,0)" d="M1,1c1.6,0,1.6,1.6,3.3,1.6S5.9,1,7.6,1c1.6,0,1.6,1.6,3.3,1.6S12.5,1,14.2,1s1.6,1.6,3.3,1.6
c1.6,0,1.6-1.6,3.3-1.6" fill="none" stroke="black"/>
</svg>
</svg>

Related

How to set a dynamic height value to a wave in svg based on % value?

I want to design dynamic wave effect with help of svg. Suppose I have passed value 40%, wave should appear 40%. Based on the value, wave effect should increase or decrease.
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 1440 320">
<path fill="#FFF6E3" fill-opacity="1" d="M0,192L48,197.3C96,203,192,213,288,229.3C384,245,480,267,576,250.7C672,235,768,181,864,181.3C960,181,1056,235,1152,234.7C1248,235,1344,181,1392,154.7L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z">
</path>
</svg>
Depending on exactly what is meant by 40% etc increase, using transform scale on the y but leaving the x will result in the waves being higher.
For example, to get them 5 times higher (than the heights of the SVG given in the question) use transform: scale(1, 5)
svg {
transform: scale(1, 5);
}
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 1440 320">
<path fill="#FFF6E3" fill-opacity="1" d="M0,192L48,197.3C96,203,192,213,288,229.3C384,245,480,267,576,250.7C672,235,768,181,864,181.3C960,181,1056,235,1152,234.7C1248,235,1344,181,1392,154.7L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z" ></path></svg>

Click only through holes in svg mask

I have svg mask which determines holes in rectangular. Behind svg mask I have some clickable elements and I would like to pass events to them, but only through holes. I've experimented with pointer-events values, but I can only make either whole mask to pass events or whole mask to capture them. For one hole it can be simply done using clip-path, just determining outer part of the hole, but several holes make things more difficult. Is there any possibility to avoid using clip-path? I also tried pointer-events: visiblePainted and pointer-events: painted, but had no success.
.background {
width: 400px;
height: 400px;
background: red;
cursor: pointer;
}
.svg {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
}
<button class="background">
</button>
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg" class="svg">
<defs>
<mask id="mask">
<rect
x="0"
y="0"
width="400"
height="400"
fill="white"
/>
<rect
x="20"
y="20"
width="40"
height="40"
fill="black"
/>
<rect
x="290"
y="290"
width="40"
height="40"
fill="black"
/>
</mask>
</defs>
<rect
x="0"
y="0"
width="400"
height="400"
fill="black"
opacity="0.5"
mask="url(#mask)"
pointer-events="auto"
/>
</svg>
There are several aspects to this problem. First, you are right the behavior of masks and clip-paths is different in relation to hit-testing.
A clip path is a geometric boundary, and a given point is clearly either inside or outside that boundary; thus, pointer events must be captured normally over the rendered areas of a clipped element, but must not be captured over the clipped areas... By contrast, a mask is not a binary transition, but a pixel operation, and different behavior for fully transparent and almost-but-not-fully-transparent may be confusingly arbitrary; as a consequence, for elements with a mask applied, pointer events must still be captured even in areas where the mask goes to zero opacity.
Second, a clip-path is a geometric shape, but just like all paths, it might contain holes. Instead of three <rect>s, you can use one <path> with three subpaths, as long as the clip-rule makes sure the subpaths inside get cut out of the surrounding shape.
Third, if the pointer-events property is applied to an <svg> element in a HTML context, its behavior becomes...strange. Any other value than pointer-events: none on the <svg> element lead to the whole bounding box receiving events - a behavior proposed for HTML elements, but currently not part of any spec.
The solution here is to set pointer-events: none on the <svg> element, and then to reverse that with pointer-events: painted on the child <rect> element.
button, svg {
position:absolute;
width:400px;
height:400px
}
button {
background: #0000ff;
cursor: pointer;
}
button:hover {
background: #008800;
}
svg {
pointer-events: none;
}
.over {
fill: #000;
clip-path: url(#clip);
pointer-events: painted;
}
<button></button>
<svg xmlns="http://www.w3.org/2000/svg" height="400" width="400">
<defs>
<clipPath id="clip" clip-rule="evenodd">
<path d="M 20 20 h 360 v 360 h -360 z
M 40 40 v 40 h 40 v -40 z
M 200 290 v 40 h 40 v -40 z" />
</clipPath>
</defs>
<rect y="0" x="0" height="400" width="400" class="over" />
</svg>
Clip masks are useful for cropping parts out of complicated objects, but if you're just working with blocks of solid colour then maybe it would be just as easy to create shapes that already have holes in them.
I've added an example below. Does this help?
<svg width="400" heoght="200" viewBox="0 0 400 200">
<text x="100" y="100" text-anchor="middle"
alignment-baseline="middle" onclick="alert('Hello!')"
style="cursor:pointer">Click me</text>
<text x="300" y="100" text-anchor="middle"
alignment-baseline="middle" onclick="alert('Hello?')"
style="cursor:pointer">Not me</text>
<path d="M20 20 180 20 180 180 20 180ZM60 60 60 140 140
140 140 60Z" fill="#3a6" fill-opacity="0.7"
fill-rule="nonzero"/>
<path d="M220 20 380 20 380 180 220 180Z" fill="#f20"
fill-opacity="0.7"/>
</svg>

Am I handling SVG's correctly?

I have a square div with two triangles drawn via SVGs within that looks like this :
I then have a button that once the onClick event is triggered it will add a class called hidden just basically has : display: none; visibility: hidden; to hide the bottom triangle, then displays two more triangles like so :
My current code looks like :
SVG to draw my triangle.
<svg id="triangle" width="100%" height="100%">
<path d="M0,0 L680,0 L0,680 Z " id="top_triangle" class="o-top_main"></path>
</svg>
Then the image for me to be able to use a background image :
<svg style="position: absolute;">
<defs>
<pattern id="image_top" width="1" height="1" viewBox="0 0 100 100">
<image id="svg_img_top" xlink:href='http://41.media.tumblr.com/tumblr_m8r9k312PE1qc6mk8o1_500.jpg' width="100" height="100" preserveAspectRatio="none"></image>
</pattern>
</defs>
</svg>
I've been drawing the SVG path like such <path d="M0,0 L680,0 L0,680 Z ". I've tried drawing an SVG triangle in photoshop, exporting out as an SVG then getting the path in the code and pasting in but the dimensions are never correct so I end up editing the path to the point where i've redrawn it anyway.
Is there anyway way I can draw an SVG triangle in Photoshop where I can then paste the path into my site without needing to change the path to the point where i've redrawn it?
My end site kind of looks similar to this :
Lets take a look at your first image here:
Now usually you allways want to create an viewBox on any svg element.
Width and height are usually set in css, but there are lots of cases where setting it in inline is better.
Now lets see your drawing 2 triangles with paths so and then adding another:
My suggestion is drawing 3 triangles then changing the color:
Update:
now with image url instead of colors. (the svg is getting quite complicated now)
var bottom = document.getElementById("tri_tri");
bottom.addEventListener("click", function() {
this.style.fill = "yellow";
this.style.stroke = "yellow";
});
/**/
.triangleArt {
width: 250px;
}
#tri_one {
fill: firebrick;
}
#tri_two {
fill: url(#image_top);
stroke: url(#image_top);
}
#tri_tri {
fill: url(#image_top);
stroke: url(#image_top);
cursor: pointer;
}
<svg viewBox="0 0 100 100" class="triangleArt" xmlns="http://www.w3.org/2000/svg" xmlns:x="http://www.w3.org/1999/xlink">
<defs>
<pattern id="image_top" patternUnits="userSpaceOnUse" width="250" height="156">
<image id="svg_img_top" xlink:href='http://41.media.tumblr.com/tumblr_m8r9k312PE1qc6mk8o1_500.jpg' x="0" y="0" width="250" height="156"></image>
</pattern>
</defs>
<path id="tri_one" d="m0,100 100,-100, -100,0z" />
<path id="tri_two" d="m100,0 0,100 -50,-50z" />
<path id="tri_tri" d="m100,100 -50,-50 -50,50z" />
</svg>

Change opacity of an area based on mouse position

Is it possible to change the opacity of background but only underneath the cursor area (for example a white small circle)? I am thinking of it a bit like a basic heatmap but the heat doesn't stay - it just follows the cursor.
At the moment I have the following
HTML:
html {
background-color: #000;
width: 100%;
height: 100%;
}
JS:
$(document).mousemove(function(event){
var i = event.pageX.toPrecision(1) / 1000;
$("html").css('opacity', i)
});
Sorry this is probably a very basic starting point. Would I need to use canvas?
You can do that using svg
What i did :-
I placed two same images with same co ordinates,height and width and gave a circular clip-path to the one on top (which has full opacity) when mouse moves the position of the circle also changes
$('svg').on('mousemove',function(e){
$('.a').attr('cx',e.pageX).attr('cy',e.pageY)
})
.one{
opacity:.5;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg width="500" height="500">
<clippath id="clip" >
<circle cx="50" cy="50" r="50" class="a"/>
</clippath>
<image xlink:href="https://images.unsplash.com/photo-1474575981580-1ec7944df3b2?dpr=1&auto=format&fit=crop&w=1500&h=934&q=80&cs=tinysrgb&crop=&bg=" width="500" height="500" x="0" y="0" class="one"/>
<image xlink:href="https://images.unsplash.com/photo-1474575981580-1ec7944df3b2?dpr=1&auto=format&fit=crop&w=1500&h=934&q=80&cs=tinysrgb&crop=&bg=" width="500" height="500" x="0" y="0" clip-path="url(#clip)"/>
</svg>

How to resize an SVG animation?

I am stuck at resizing an svg animation to a different percentages of the page.
I have created a circle that increases its size and then goes back to normal with this:
<svg id="mySVG" xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle id="myCircle" cx="600" cy="250" r="70" fill="yellow" stroke="green" stroke-width="5">
<animate attributeName="r" values="70; 140; 210; 140; 70" dur="10s" repeatCount="indefinite"/>
</circle>
</svg>
Then, when I change the área size of the svg, the circle does not change its size nor the position within this area.
I have tried to scale the animation with CSS giving size to the body in pixels and then assign an area to the SVG in percentages:
body{
width: 1440px;
heigth: 990px
}
#mySVG{
width: 100%;
}
Also, I tried to change size with javascript:
function resize(){
var svg = document.getElementById("mySVG");
svg.style.width = window.innerWidht;
svg.style.height = window.innerHeight;
}
None of them worked… wondering what is the best method to scale SVG animations not only with this circle but with SVG animations in general.
Thank’s in advanced.
You have to use a viewbox to define the co-ordinate space. E.G.
<svg id="mySVG" xmlns="http://www.w3.org/2000/svg" version="1.1"
viewbox="0 0 1200 1200">
The viewBox attribute allows to specify that a given set of graphics
stretch to fit a particular container element.
The value of the viewBox attribute is a list of four numbers min-x,
min-y, width and height, separated by whitespace and/or a comma, which
specify a rectangle in user space which should be mapped to the bounds
of the viewport established by the given element, taking into account
attribute preserveAspectRatio.
<svg id="mySVG" xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 1200 1200">
<circle id="myCircle" cx="600" cy="250" r="70" fill="yellow" stroke="green" stroke-width="5">
<animate attributeName="r" values="70; 140; 210; 140; 70" dur="10s" repeatCount="indefinite" />
</circle>
</svg>
Useful SVG Resource Article # CSS-Tricks.com
MDN Link

Categories