"Filling Water" effect with infinite repeat wrapping - css

I've been trying to achieve the effect seen here for one wave in a circle:
http://www.jquery-az.com/css/demo.php?ex=131.0_1
Unfortunately, I've been unable to get the animation to repeat smoothly with my own svg, seen here: http://jsbin.com/diserekigo/1/edit?html,css,output. You'll also notice that the bottom "rectangle" part isn't filled either.
My css is as follows:
.circle {
border-radius: 100%;
border: 1px solid black;
width: 200px;
height: 200px;
overflow: hidden;
position: relative;
perspective: 1px;
}
.liquid {
position: absolute;
left: 0;
top: 0;
z-index: 2;
width: 100%;
height: 100%;
-webkit-transform: translate(0, 80%);
transform: translate(0, 80%);
}
.wave {
left: 0;
width: 400%;
position: absolute;
bottom: 100%;
margin-bottom: -1px;
-webkit-animation: wave-front .7s infinite linear;
animation: wave-front 0.7s infinite linear;
}
#-webkit-keyframes wave-front {
100% {
-webkit-transform: translate(-100%, 0);
transform: translate(-100%, 0);
}
}
#keyframes wave-front {
100% {
-webkit-transform: translate(-100%, 0);
transform: translate(-100%, 0);
}
}
How can I improve the repeating behavior, as well as make the wave fill up the entire space beneath it?

You're missing a lot from the original demo. Why not just copy and paste and make whatever local changes to the size and position you wanted? Most of your issues are a result of not having all the SVG elements - the original demo has 3, not just 1. I've added them into your jsbin to get it to work:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" style="display: none;">
<symbol id="wave">
<path d="M420,20c21.5-0.4,38.8-2.5,51.1-4.5c13.4-2.2,26.5-5.2,27.3-5.4C514,6.5,518,4.7,528.5,2.7c7.1-1.3,17.9-2.8,31.5-2.7c0,0,0,0,0,0v20H420z"></path>
<path d="M420,20c-21.5-0.4-38.8-2.5-51.1-4.5c-13.4-2.2-26.5-5.2-27.3-5.4C326,6.5,322,4.7,311.5,2.7C304.3,1.4,293.6-0.1,280,0c0,0,0,0,0,0v20H420z"></path>
<path d="M140,20c21.5-0.4,38.8-2.5,51.1-4.5c13.4-2.2,26.5-5.2,27.3-5.4C234,6.5,238,4.7,248.5,2.7c7.1-1.3,17.9-2.8,31.5-2.7c0,0,0,0,0,0v20H140z"></path>
<path d="M140,20c-21.5-0.4-38.8-2.5-51.1-4.5c-13.4-2.2-26.5-5.2-27.3-5.4C46,6.5,42,4.7,31.5,2.7C24.3,1.4,13.6-0.1,0,0c0,0,0,0,0,0l0,20H140z"></path>
</symbol>
</svg>
<div class="circle">
<div class="liquid"></div>
<div id="water" class="water">
<svg viewBox="0 0 560 20" class="water_wave water_wave_back">
<use xlink:href="#wave"></use>
</svg>
<svg viewBox="0 0 560 20" class="water_wave water_wave_front">
<use xlink:href="#wave"></use>
</svg>
</div>
</div>
You also need JavaScript to get the water to fill.
http://jsbin.com/pinowufeqe/edit?html,css,js,output

Related

Specify child in a parent in svelte

I am trying to replicate the page loading template from here
HTML
<div class="loading">
<div class="loading__ring">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve"><path>...../path></svg>
</div>
<div class="loading__ring">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve"><path>......</path></svg>
</div>
</div>
Normal CSS style
/* Demo */
body {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(#ecf0f1, #fff);
}
/* Animation */
#keyframes rotate {
to {
transform: rotate(360deg);
}
}
/* Variables */
$loader-size: 100px;
/* Loading Icon */
.loading {
width: $loader-size;
height: $loader-size;
&__ring {
position: absolute;
width: $loader-size;
height: $loader-size;
&:first-child {
transform: skew(30deg,20deg);
}
&:last-child {
transform: skew(-30deg,-20deg) scale(-1, 1);
svg {
animation-delay: -0.5s;
}
}
svg {
animation: rotate 1s linear infinite;
fill: rgba(0, 0, 0, 0.2);
}
}
}
I tried to alter into the svelte style by using example specifying child like .loading:global(loading__ring) but this is exactly where I am getting the error saying
Internal server error: /Users/mypc/Documents/testingsveltenvite/v2/src/App.svelte:18:2 Identifier is expected
How do I alter specifying child &__ringor.loading:global(loading__ring) and &:first-child in svelte
/* Loading Icon */
.loading {
width: var(--size);
height: var(--size);
.loading:global(loading__ring) {
position: absolute;
width: var(--size);
height: var(--size);
&:first-child {
transform: skew(130deg,15deg);
}
&:last-child {
transform: skew(-130deg,-1deg) scale(-1, 1);
svg {
animation-delay: -0.91s;
}
}
svg {
animation: rotate 4s linear infinite;
fill: rgba(0, 0, 0, 0.2);
}
}
}
I don't see why you would need the :global() flag, if you have everything inside one component REPL - only the scss has to be converted to normal css (or scss added to the Svelte project)
(the body tag here needs the :global(body) because it's not in the component scope. If you want to make this a loading component, you would probably better add a background element and style this instead)
<script>
import {path} from './path'
</script>
<div class="loading">
<div class="loading__ring">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
{#html path}
</svg>
</div>
<div class="loading__ring">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
{#html path}
</svg>
</div>
</div>
<style>
:global(body) {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(#ecf0f1, #fff);
}
/* Animation */
#keyframes rotate {
to {
transform: rotate(360deg);
}
}
/* Variables */
:root {
--size: 100px;
}
/* Loading Icon */
.loading {
width: var(--size);
height: var(--size);
}
.loading__ring {
position: absolute;
width: var(--size);
height: var(--size);
}
.loading__ring:first-child {
transform: skew(30deg,20deg);
}
.loading__ring:last-child {
transform: skew(-30deg,-20deg) scale(-1, 1);
}
.loading__ring:last-child svg {
animation-delay: -0.5s;
}
.loading__ring svg {
animation: rotate 1s linear infinite;
fill: rgba(0, 0, 0, 0.2);
}
</style>

Scaled svg path on hover is not centered

I have an svg <path> that I want scaled on mouse hover. The scaled path should be centered on top of its original location. I have read other similar posts but could not get it done.
I tried using transform-origin: 50% 50%;, transform-origin: 0 0;, and transform-origin: center; and also tried removing transform-origin all throughout, but none of them works as intended since the scaled path has an offset of several pixels:
path {
fill: #f00
}
path:hover {
fill: #000;
transform: scale(2);
-ms-transform: scale(2);
-webkit-transform: scale(2);
transform-origin: 50% 50%;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 881 571" pointer-events="fill" fill="none" fill-opacity="1">
<path d="M447 263h1-1 1v-1 1h1-1 1-1 1v1h1-1 2v1h1v1h-1 1-1v1h1v1h1-1v1h-1v1-1 1h-1v1h-1v3h-1v-1h-2v-1h-1v-1h-2v-2h-1v-3h-1v-1h1-1 1v-1h1v-2h1-1v1-2 1h1v1h1v-1h1v1h1z"/>
</svg>
Your SVG artbox seems enormous, and I believe this is part of the issue. I tried cleaning it up a bit to fit the content of the SVG, and I wrapped the element inside of a div.
Here's the SVG with a more proportionate artbox:
svg {
display: block;
width: 100%;
height: auto;
}
div {
fill: #f00;
width:127px;
height:150px;
margin-left: 50px;
margin-top: 50px;
}
div:hover {
fill: #000;
transform: scale(2);
-ms-transform: scale(2);
-webkit-transform: scale(2);
transform-origin: 50% 50%;
}
<div>
<svg xmlns="http://www.w3.org/2000/svg"><path d="M65.5 23.583h10.583H65.5h10.583V13v10.583h10.584-10.584 10.584-10.584 10.584v10.584H97.25 86.667h21.166V44.75h10.584v10.583h-10.584 10.584-10.584v10.584h10.584V76.5H129h-10.583v10.583h-10.584v10.584-10.584 10.584H97.25v10.583H86.667V140H76.083v-10.583H54.917v-10.584H44.333V108.25H23.167V87.083H12.583v-31.75H2V44.75h10.583H2h10.583V34.167h10.584V13H33.75 23.167v10.583V2.417 13H33.75v10.583h10.583V13h10.584v10.583H65.5z"/>
</svg>
</div>
Hope it helps!

I'm having trouble controlling an animated svg border using CSS

I've been trying to animate a svg border, I've gotten as far as this
html {
background: white;
}
div {
position: fixed;
height: 200px;
width: 605px;
position: fixed;
left: 30%
}
.mainNav {
position: fixed;
top: 6;
}
[class="navBorder"] .outline {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
stroke: #7272f8;
stroke-width: 11px;
fill: none;
}
.navBorder .outline {
stroke-dasharray: 2000;
stroke-dashoffset: 1900;
-webkit-transition: 0.5s;
-moz-transition: 0.5s;
-o-transition: 0.5s;
transition: 0.5s;
}
.navBorder:hover .outline {
stroke-dasharray: 1100 0;
stroke-dashoffset: 0;
}
<div>
<a class="navBorder" target="_blank">
<svg height="100%" width="100%" xmlns="http://www.w3.org/2000/svg">
<rect class="outline" height="100%" width="100%" />
</svg>
</a>
</div>
http://codepen.io/lorehill/pen/pEPXar
The problem is I can't seem to get the starting position of the border to be on the top center and then close center bottom.
I'm very confused trying to figure out how to calculate the values I need to set stroke-dasharray and stroke-dashoffset for the starting position in order to get the effect I'm after.
If anyone could explain it like I'm 5 that would be fantastic.
Thank you!
AFAIK, the starting position of the stroke is always the starting point of the rect which is top left for a rect element.
I can't seem to get the starting position of the border to be on the top center and then close center bottom.
I think you'll need two polyline elements for that, although you can use the same class on both.
svg {
height: 100px;
margin: 1em;
}
.outline {
fill: lightblue;
stroke-dasharray: 200;
stroke-dashoffset: 190;
-webkit-transition: 0.5s;
-moz-transition: 0.5s;
-o-transition: 0.5s;
transition: 0.5s;
}
svg:hover .outline {
stroke-dasharray: 200 0;
stroke-dashoffset: 0;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 100 100">
<polyline class="outline" points="50,0 100,0, 100,100 50,100" style="stroke:#660000; stroke-width: 3;" />
<polyline class="outline" points="50,0 0,0 0,100 50,100" style="stroke:#660000; stroke-width: 3;" />
</svg>
Codepen Demo

Clip-path and SVG rect inside animation

I am trying to make a fancy animation only in CSS. I started with a tutorial on W3 School and wanted to make it better. My idea is to have a square loader turning clockwise while another inside would turn in the opposite direction.
On this link you will see what I'm talking about, the only difference is that I would like the red part to be turning in the opposite direction.
In order to do so I tried adding another div with class name .spinner. Here's my try at it: https://jsfiddle.net/avhjj4ps/
.loader-container {
position: absolute;
left: calc(50% - 75px);
width: 150px;
height: 150px;
padding: 5px;
border: 1px solid red;
top: calc(50% - 75px);
}
img {
width: 200px;
margin: 20px;
/*animation: move 2s alternate infinite linear;*/
}
#myClip, #svg {
position: absolute;
left: 50%;
top: 50%;
}
.loader, .spinner {
position: absolute;
}
.loader {
left: calc(50% - 35px);
top: calc(50% - 35px);
width: 40px;
height: 40px;
border: 15px solid transparent;
border-top: 15px solid none;
/*-webkit-animation: loader 2s linear infinite;
animation: loader 2s linear infinite;*/
}
.spinner {
left: calc(50% - 55.1px);
top: calc(50% - 55.1px);
/*clip-path: url(#myClip);*/
width: 40px;
border-radius: 50%;
height: 40px;
border: 36px solid #f3f3f3;
border-top: 36px solid #5cb85c;
/*-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;*/
}
#-webkit-keyframes loader {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
#keyframes loader {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(-360deg); }
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(-360deg); }
}
<div class="loader-container">
<div class="loader"></div>
<div class="spinner"></div>
<svg id="svg" width="0" height="0">
<defs>
<clipPath id="myClip">
<rect x="-35" y="-35" width="15" height="70" />
<rect x="20" y="-35" width="15" height="70" />
<rect x="-35" y="-35" width="70" height="15" />
<rect x="-35" y="20" width="70" height="15" />
</clipPath>
</defs>
</svg>
</div>
I am trying to show the green spinner only where there is the square loader. It would be like a mask. In the above snippet (also available here: http://codepen.io/anon/pen/ZOoByA), I'm trying to use the clip-path property.
Can some tell me why clip-path: url(#myClip); doesn't work ? When I comment this line the loader shows completely, however while active it's not showing at all.
You can create your loader in svg with some polygons and then clip the inner green loader away with clipPath.
First, define the gray border as a polygon:
<polygon id="loader" points="0,0 0,70 70,70 70,0 0,0 15,15 55,15 55,55 15,55 15,15" />
As we will reuse this shape (the actual loader and the clip-path shape), we put into the defs tag:
<svg height="0" width="0">
<defs>
<polygon id="loader" points="..." />
</defs>
</svg>
Then we put the clipPath into the same defs tag:
<clipPath id="loaderClipper">
<use xlink:href="#loader" x="15" y="15" />
</clipPath>
The offset of 15 is calculated in the following way: The loader's width is 70, but if it is rotated by 45 degrees, it's width is 70√2 which rounds to 100. The whitespace in the left and in the right is (100 - 70) / 2 = 15.
The svg for the actual used element looks like this:
<svg width="100" height="100" viewbox="0 0 100 100" clip-path="url(#loaderClipper)">
<use xlink:href="#loader" class="loader" x="15" y="15" />
<polygon class="spinner" points="0,0 100,0 50,50" x="30" y="30" />
</svg>
And some css for colors and the animation:
svg {
animation: rotate 2s linear infinite;
transform-origin: 50px 50px;
}
.loader {
fill: #dcdada;
}
.spinner {
fill: #5cb85c;
animation: rotate 1s linear infinite reverse;
transform-origin: 50px 50px;
}
Result fiddle: https://jsfiddle.net/apLepsv3/10/
Successfully tested on both mobile and desktop Firefox and Chrome.
For a CSS-only solution without SVG you need some helper elements:
<div class="loader">
<div class="square"></div>
<div class="cutter">
<div class="spinner">
</div>
</div>
</div>
And then this CSS code:
.square {
width: 40px;
height: 40px;
background: #f3f3f3;
z-index: 1;
}
.cutter {
width: 70px;
height: 70px;
left: -15px;
top: -15px;
overflow: hidden;
}
.spinner {
width: 54px;
border-radius: 50%;
height: 54px;
border: 8px solid transparent;
border-top: 8px solid #5cb85c;
-webkit-animation: spin 1s linear infinite;
animation: spin 1s linear infinite;
margin-left: -15px;
margin-top: -15px;
}
Result: https://jsfiddle.net/avhjj4ps/3/
Disadvantage: Inner square must have a solid background (no gradient or image) if it has to match the parent's / body's background.
You can create the spinner with HTML and CSS and then cut the overflow away by using the clip-path property in combination with a svg <clipPath> element.
Your html structure of the spinner:
<div class="loader">
<div class="spinner">
</div>
</div>
Now position the two elements over each other:
.loader {
width: 40px;
height: 40px;
position: relative;
left: 30px;
top: 30px;
border: 15px solid #dcdada;
border-top: 15px solid none;
-webkit-animation: loader 2s linear infinite;
animation: loader 2s linear infinite;
}
.spinner {
width: 0px;
height: 0px;
position: relative;
left: -30px;
top: -30px;
border: 50px solid transparent;
border-top: 50px solid #5cb85c;
-webkit-animation: spin 1s linear infinite;
animation: spin 1s linear infinite;
}
But there's still that green overflow outside and inside of the gray border. So we need to cut it away with a svg <polygon>.
<svg height="0" width="0">
<defs>
<clipPath id="loaderClipper">
<polygon points="0,0 0,70 70,70 70,0 0,0 15,15 55,15 55,55 15,55 15,15"/>
</clipPath>
</defs>
</svg>
The points define a 70x70 square with a 40x40 square cut off.
Then add the clip-path property that references to the svg <clipPath> element:
.loader {
clip-path: url(#loaderClipper);
}
Fiddle: https://jsfiddle.net/apLepsv3/2/
Disadvantage: Only supported in Firefox, not Chrome

3d transforms on SVG element

Is it possible to achieve perspective with 3d transforms on a SVG elements?
I'm talking about something similar with how the Star Wars opening titles look like with 3d perspective. This is a jsfiddle with the desired effect achieved using CSS3 3d transforms:
<section style="transform: perspective(200px) rotateX(-30deg); transform-origin: 50% 100%; text-align: justify; width: 100px;">
<p style="backface-visibility: hidden;">TEXTTEXTTEXT</p>
</section>
Update Nov 2018:
Testing the snipet from the question in latest chrome and Firefox works. Although support for 3d transforms on svg elements isn't very wide, browsers are implementing it more and more.
Origin answer :
3D transforms aren't supported on SVG elements. There are a few workarounds though :
If the svg doesn't contain elements that shouldn't be transformed, you can use CSS 3d transforms on the SVG element itself :
svg {
width: 70%;
margin: 0 auto;
display: block;
-webkit-transform: perspective(300px) rotateX(30deg);
transform: perspective(300px) rotateX(30deg);
}
<svg viewbox="0 0 100 20">
<text x="0" y="20">TEXTEXTEX</text>
</svg>
In case of polygons, you make a 2D polygon look like a 3D polygon. In the following example, the red rectangle is 3D rotated (rotateX(40deg)) and the black rectangle is a 2D SVG polygon made to look like a 3D rotated rectangle:
div{
display:inline-block;
width:200px; height:100px;
background:red;
transform:perspective(500px) rotateX(40deg);
}
svg{
display:inline-block;
width:220px; height:auto;
}
div, svg{
display:inline-block;
margin:0 10px;
}
<div></div>
<svg viewbox="0 0.5 10 4">
<polygon points="9.9 4.1 0.1 4.1 0.7 0.6 9.3 0.6" fill=""/>
</svg>
3D transforms are supported inside <svg> elements (f.e. on <circle>) (at least to some extent, it seems like perspective is isometric only).
For example, here's animation of transform: rotate3d applied to <circle> elements (tested in Chrome only):
body, html {
background: black;
width: 100%; height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
}
svg {
width: 100%;
}
.gAExgp {
transform-origin: 50% 50% 0px;
animation-name: phEs, ipaUyp;
animation-duration: 4s, 7s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.PwswZ {
transform-origin: 50% 50% 0px;
animation-name: gcRPJT, ipaUyp;
animation-duration: 4s, 8s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
#keyframes phEs {
50% {
transform: rotate3d(0, 2, 1, 180deg);
}
100% {
transform: rotate3d(0, 2, 1, 360deg);
}
}
#keyframes gcRPJT {
50% {
transform: rotate3d(2, 0, 1, 180deg);
}
100% {
transform: rotate3d(2, 0, 1, 360deg);
}
}
#keyframes ipaUyp {
0% {
stroke: magenta;
}
33% {
stroke: cyan;
}
66% {
stroke: yellow;
}
100% {
stroke: magenta;
}
}
<!-- Logo from https://rebassjs.org -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" style="display:block;max-width:100%;margin:0;fill:none;stroke:cyan" vector-effect="non-scaling-stroke" class="sc-htoDjs hCHUAb"><circle cx="32" cy="32" r="32" fill="#000" stroke="none"></circle><circle cx="32" cy="32" r="30" stroke-width="1" vector-effect="non-scaling-stroke" opacity="0.5"></circle><g><circle cx="32" cy="32" r="24" stroke-width="2" vector-effect="non-scaling-stroke" class="sc-dnqmqq gAExgp"></circle><circle cx="32" cy="32" r="24" stroke-width="2" vector-effect="non-scaling-stroke" class="sc-iwsKbI PwswZ"></circle></g><text x="32" y="34" text-anchor="middle" font-family="system-ui, sans-serif" font-weight="bold" font-size="4" stroke="none" fill="white" style="text-transform:uppercase;letter-spacing:0.5em">Rebass</text></svg>
Also available here: https://codepen.io/anon/pen/MPeyEj

Resources