Reputation: 61
(The solution clean for the W3 Validator can be found at the bottom of this post)
the goal of my task is simple :
to have a "heart" icon that users can click on and have it filled in red (it is required for the heart to fill slowly);
Ideally, users can click again on the "filled heart" to empty it (the like and unlike mechanic);
Oh and one last requirement : only html and css/scss allowed on board.
I had trouble first to make the heart-icon. I checked the web for various solutions, from designing it myself in code-lines to importing an image. Finally, I decided to import a .svg file [from this site][1] and with this code:
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 40.508 40.508" style="enable-background:new 0 0 40.508 40.508;" xml:space="preserve">
<g>
<path style="fill:#010002;" d="M3.023,20.822c0.019,0.031,0.042,0.059,0.066,0.083l15.918,16.914
c0.115,0.122,0.271,0.192,0.444,0.197h0.012c0.163,0,0.317-0.062,0.435-0.176L37.28,21.032c0.008-0.006,0.076-0.068,0.117-0.108
c0.034-0.032,0.062-0.065,0.068-0.081c1.962-2.032,3.043-4.702,3.043-7.517c0-5.974-4.86-10.834-10.835-10.834
c-3.91,0-7.498,2.094-9.419,5.48c-1.92-3.387-5.508-5.48-9.419-5.48C4.86,2.492,0,7.352,0,13.326
C-0.001,16.14,1.078,18.808,3.023,20.822z M10.834,3.743c3.875,0,7.346,2.312,8.842,5.891c0.098,0.233,0.324,0.383,0.577,0.383
c0.252,0,0.479-0.15,0.577-0.384c1.497-3.578,4.968-5.89,8.843-5.89c5.285,0,9.585,4.3,9.585,9.584
c0,2.521-0.978,4.904-2.754,6.712c-0.017,0.018-0.032,0.035-0.045,0.053L19.483,36.5l-15.4-16.358
c-0.023-0.037-0.05-0.072-0.082-0.104c-1.775-1.805-2.752-4.188-2.752-6.711C1.249,8.042,5.549,3.743,10.834,3.743z"/>
</g>
</svg>
With no idea how to work properly with an SVG, I searched the web, looked-up some articles and YouTube videos, the course I'm taking right now is not telling me much or not in an intuitive way but I finally put my hands on two interesting-looking opportunities :
Its pro : it's already made. its nearly and honestly, the few Js code for the "pulse" effect is not needed in my own case, so I deleted it.
It's cons :
animation:forwards;
But here in this case, it doesn't seem to work if anyone has a solution, I think it would be a good option for the project they asked me to provide;
it doesn't use '@keyframes', and looks like a simple transition, but, again, my lack of knowledge makes it difficult to interpret;
The linked Q/A was related to a water-filling animation applied to a circle.
its pro: it's a good effect and the additional animations proposed by another contributor are brilliant too ! I prefer this "bottom-up" filling effect than the one in the first option.
its con: Well it's not a proper con, it's me not being able to adapt it to my own needs. in the present example, I can't apply it to my .svg image rather than a "natively made form" like they did with their circle.
I had hoped to take the animation described in the post and apply it to my heart-icon. Well, needless to say that, without proper skills in the field, I merely copied/pasted what seemed to work and surprisingly, it has some effect but not the one I'm looking for, here's my current code.
[EDIT : I use here ":hover" instead of ":active" because I couldn't find how to simply click once on the heart in order for it to fill automatically, with ":active" I have to click on itmultiple times like in a button-smashing mini-game to fill it.]
HTML first :
<svg class='bigHeart' height="0" width="0">
<defs>
<clipPath id=svgClip clipPathUnits="objectBoundingBox" transform="scale(0.01 0.01)">
<path d='M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z'></path>
</clipPath>
</defs>
</svg>
<div class="heart-container">
<svg viewBox="0 0 100 100" class='heart-stroke'>
<path class=bigHeart d="M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z" />
</svg>
<input type='checkbox' class='heart-clip'>
<div class="fill">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 400" xml:space="preserve">
<path d="M0,6c100,10,100,-10,200,0c100,10,100,-10,200,0v394h-400Z" class="waveShape"></path>
</svg>
</div>
</div>
And then the CSS :
.heart{
width:40px;
position:relative;
overflow:hidden;
}
.heart:hover:before{
content:'';
position:absolute;
background: #04ACFF;
z-index: -1;
width:100%;
bottom:0;
animation: wipe 5s forwards;
}
@keyframes wipe {
0% {
height: 0;
}
100% {
height: 100%;
}
}
If we refer to the [original code I found][3], I decided to delete the "background: #000" and had to add a "z-index:-1" in order to have the filling animation not overlapping over the borders of the icon. But then I'm stuck and after trying to mess around and rereading what I had at hand, I decided to type this (long) post.
I would like to thank you if you've been this far, thanks for reading my request today and thank you in advance for any advice and solution you will be kind to share with me,
The solution has been provided by @thehujib here below, I only post his code again here because it's cleaned of all small elements that were spotted by the W3 validator (mainly open-ended and repeated occurrences of ID's):
<svg class='bigHeart' height="0" width="0">
<defs>
<clipPath id=svgClip clipPathUnits="objectBoundingBox" transform="scale(0.01 0.01)">
<path d='M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z'></path>
</clipPath>
</defs>
<linearGradient id="my-cool-gradient" x2="1" y2="1">
<stop offset="0%" stop-color="#FF79DA" />
<stop offset="100%" stop-color="#9356DC" />
</linearGradient>
</svg>
<div class="heart-container">
<svg viewBox="0 0 100 100" class='heart-stroke'>
<path class=bigHeart d="M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z" />
</svg>
<input type='checkbox' class='heart-clip'>
<div class="fill">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 400" xml:space="preserve">
<path d="M0,6c100,10,100,-10,200,0c100,10,100,-10,200,0v394h-400Z" class="waveShape"></path>
</svg>
</div>
</div>
and the CSS (the only change is to have converted some repeated ID into CLASSES :
:root {
--stroke-width: 2px;
--heart-width: 100%;
--transition-length: 1.2s;
--heart-color:#d32f2f;
}
.heart-container {
position: relative;
width:10%;
aspect-ratio: 1/1;
overflow: hidden;
clip-path: url(#svgClip);
}
.heart-clip {
appearance: none;
margin: 0px;
top: 0;
bottom: 0;
left: 0;
right: 0;
position: absolute;
overflow: hidden;
z-index: 2;
}
.heart-clip + .fill {
top:0;
left:0;
transform: translateY(50%);
width: calc(2 * var(--heart-width, 100px));
aspect-ratio: 1/1;
transition: transform var(--transition-length, 0s) cubic-bezier(.2, .6, .8, .4);
}
.heart-clip:checked + .fill {
transform: translateY(0);
}
.heart-stroke {
width: calc(100% - (2 * var(--stroke-width, 2px)));
aspect-ratio: 1/1;
position: absolute;
top: var(--stroke-width, 0);
left: var(--stroke-width, 0);
fill: transparent;
stroke: var(--heart-color);
stroke-width: var(--stroke-width, 1px);
}
.waveShape {
animation-name: waveAction;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-duration: 1s;
fill: var(--heart-color);
}
@keyframes waveAction {
0% {
transform: translate(-175px, 0px);
}
100% {
transform: translate(0px, 0px);
}
}
``
Regards,
Elrad.
[1]: https://www.svgrepo.com/svg/5233/heart
[2]: https://codepen.io/GeorgeWL/pen/yLeGGMw
[3]: https://stackoverflow.com/questions/29738787/filling-water-animation/29739227#29739227
Upvotes: 1
Views: 2730
Reputation: 239
I think the solution would be something like this: It still is a simple transition for the filling of the heart and an animation for the wave-effect.
it uses custom properties that you can play around with
:root {
--stroke-width: 2px;
--heart-width: 150px;
--transition-length: 1.2s;
--heart-color: #d32f2f;
}
.heart-container {
position: relative;
width: var(--heart-width, 100px);
aspect-ratio: 1/1;
overflow: hidden;
clip-path: url(#svgClip);
}
.heart-clip {
appearance: none;
margin: 0px;
top: 0;
bottom: 0;
left: 0;
right: 0;
position: absolute;
overflow: hidden;
z-index: 2;
}
.heart-clip + .fill {
top:0;
left:0;
transform: translateY(50%);
width: calc(2 * var(--heart-width, 100px));
aspect-ratio: 1/1;
transition: transform var(--transition-length, 0s) cubic-bezier(.2, .6, .8, .4);
}
.heart-clip:checked + .fill {
transform: translateY(0);
}
.heart-stroke {
width: calc(100% - (2 * var(--stroke-width, 2px)));
aspect-ratio: 1/1;
position: absolute;
top: var(--stroke-width, 0);
left: var(--stroke-width, 0);
fill: transparent;;
stroke: var(--heart-color);
stroke-width: var(--stroke-width, 1px);
}
.waveShape {
animation-name: waveAction;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-duration: 1s;
fill: var(--heart-color);
}
@keyframes waveAction {
0% {
transform: translate(-175px, 0px);
}
100% {
transform: translate(0px, 0px);
}
}
<svg height="0" width="0">
<defs>
<clipPath id=svgClip clipPathUnits="objectBoundingBox" transform="scale(0.01 0.01)">
<path d='M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z'>
</clipPath>
</defs>
</svg>
<div class="heart-container">
<svg viewBox="0 0 100 100" class='heart-stroke'>
<path d="M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z" />
</svg>
<input type='checkbox' class='heart-clip'>
<div class="fill">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 400 400" xml:space="preserve">
<path d="M0,6c100,10,100,-10,200,0c100,10,100,-10,200,0v394h-400Z" class="waveShape"></path>
</svg>
</div>
</div>
Upvotes: 1