Reputation: 518
I was trying to create a similar effect on up and down arrows as shown in the image below but got stuck midway because of my low javascript/jquery skills.
I can't figure out how to make the text appear and then fade away on click with color change.
Here's a link to the fiddle just in case SO code snippet doesn't work
$("span").click(function() {
$("span").css("color", "grey");
$(this).css("color", "red");
});
ul > li{
list-style:none;
}
span {
cursor: pointer;
}
.fa {
font-size: 55px;
text-indent: 200px;
margin-bottom: 20px;
margin-top:30px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li><span id='select1'><i class="fa fa-long-arrow-up" aria-hidden="true"></i></span></li>
<li><span id='select2'><i class="fa fa-long-arrow-down" aria-hidden="true"></i></span></li>
</ul>
So far none of the answers have worked for me so I am asking for more help on this.
I saw this effect on reddit and I've tried many times and spent so much time but failed to get the similar effect. I'd really appreciate it if anybody could help me understand and create the exact effect.
Upvotes: 1
Views: 1183
Reputation: 1569
here is my version of the solution, https://jsfiddle.net/hnk1vw6x/33/ see some explanations below.
HTML
<div class="padding-container">
<span id="rating">0</span>
<a class="arrow fa fa-arrow-up" data-animation-text="Nice!" data-value="1"></a><br/>
<a class="arrow fa fa-arrow-down" data-animation-text="Troll" data-value="-1"></a>
</div>
CSS
.padding-container {
width: 60px;
margin: 100px;
}
#rating {
float: right;
font-size: 2.1em;
width: auto;
}
a.arrow {
display: inline-block;
position: relative;
cursor: pointer;
}
a.arrow:after {
content: attr(data-animation-text);
display: block;
position: absolute;
left: 50%;
transform: translateX(-50%);
text-align: center;
width: auto;
opacity: 0;
}
a.arrow.fa-arrow-up {
color: #FF0000;
}
a.arrow.fa-arrow-down {
color: #0000FF;
}
a.arrow.fa-arrow-up:after {
bottom: 100%;
}
a.arrow.fa-arrow-down:after {
top: 100%;
}
a.arrow.animate.fa-arrow-up:after {
animation-name: slideup, bounce;
animation-duration: 3s;
}
a.arrow.animate.fa-arrow-down:after {
animation-name: slidedown, bounce;
animation-duration: 3s;
}
@keyframes slideup {
from {
bottom: 100%;
opacity: 1;
}
to {
bottom: 300%;
opacity: 0;
}
}
@keyframes slidedown {
from {
top: 100%;
opacity: 1;
}
to {
top: 300%;
opacity: 0;
}
}
@keyframes bounce {
from {
font-size: 1em;
}
3% {
font-size: 1.25em;
}
6% {
font-size: 0.75em;
}
9% {
font-size: 1em;
}
}
JavaScript
function arrowAnimationEndHandler(e) {
var arrow = e.target;
if (typeof arrow === 'undefined') {
return;
}
arrow.className = arrow.className.replace(/\banimate\b/,'');
}
function arrowClickHandler(e) {
var arrow = e.target;
if (typeof arrow === 'undefined') {
return;
}
arrow.className = arrow.className.replace(/\banimate\b/,'');
setTimeout(function () {
arrow.className += ' animate';
}, 0);
ratingUpdateBusinessLogic(arrow);
}
function ratingUpdateBusinessLogic(arrow) {
if (typeof ratingElement === 'undefined') {
return;
}
var ratingDelta = parseInt(arrow.getAttribute('data-value'), 10);
ratingElement.innerHTML = parseInt(ratingElement.innerHTML, 10) + ratingDelta;
}
var ratingElement = document.getElementById("rating");
var arrows = document.getElementsByClassName("arrow");
for (var i = 0; i < arrows.length; i++) {
arrows[i].addEventListener("animationend", arrowAnimationEndHandler, false);
arrows[i].addEventListener("click", arrowClickHandler, false);
}
Now little bit of explanation:
The problem is quite complex and author is asking for a complete solution rather then explanation of one aspect which is not clear. I decided to give an answer because then I can outline the software design steps, which might help someone else to solve another complex problem.
In my opinion the key to complex tasks is the ability to split them in smaller, which in turn are easier to approach. Let's try to split this task into smaller pieces:
Now let's try to solve those smaller problems one by one:
We need to draw two arrows and a number. Well, HTML is our friend here and below is a trivial html code. I'm using font-awesome to draw the actual arrow icons.
<div class="padding-container">
<span id="rating">0</span>
<a class="arrow fa fa-arrow-up"></a>
<a class="arrow fa fa-arrow-down"></a>
</div>
We want our arrows to be positioned in a certain way on the screen, let's make the arrows inline-blocks, and add a line-break between them, also add some CSS to line up:
.padding-container {
width: 60px;
margin: 100px;
}
#rating {
float: right;
font-size: 2.1em;
width: auto;
}
a.arrow {
display: inline-block;
cursor: pointer;
}
Our arrows should have different colors. Again trivial CSS here. The colors are not 100% like in the gif, but that is the question of making the screenshot and picking the right color - you can do it yourself.
a.arrow.fa-arrow-up {
color: #FF0000;
}
a.arrow.fa-arrow-down {
color: #0000FF;
}
We need to draw the arrow tooltips/labels next to them. Ok, that starts to be interesting. Let's use the :after
pseudo-element to draw our tooltips, because those tooltips are part of representation (and not content), they don't need to be reflected in the html structure.
I use :after
and not :before
because font-awesome is using before
for the arrow icon rendering ;) Let's also use absolute positioning to place them relative to the actual arrows. That gives us the following CSS:
a.arrow {
position: relative;
}
a.arrow:after {
content: attr(data-animation-text);
display: block;
position: absolute;
left: 50%;
transform: translateX(-50%);
text-align: center;
width: auto;
}
a.arrow.fa-arrow-up:after {
bottom: 100%;
}
a.arrow.fa-arrow-down:after {
top: 100%;
}
Now, our tooltips are rendered just next to the arrows, and we have the possibility to control the content of them through html, e.g. for translation purposes. Tooltips are also centered relative to the arrows.
We need to animate the arrow tooltips/labels on user interaction. We can animate elements by javascript and we can also do that via CSS. Doing it via CSS is way more efficient, so unless we need to support really old browsers, let's stick to CSS. We need to implement two animations, one is tooltip fading together with lift/drop and the second one is the tooltip bounce. Let's what CSS has to offer:
a.arrow:after {
opacity: 0;
}
a.arrow.fa-arrow-up:after {
animation-name: slideup, bounce;
animation-duration: 3s;
}
a.arrow.fa-arrow-down:after {
animation-name: slidedown, bounce;
animation-duration: 3s;
}
@keyframes slideup {
from {
bottom: 100%;
opacity: 1;
}
to {
bottom: 300%;
opacity: 0;
}
}
@keyframes slidedown {
from {
top: 100%;
opacity: 1;
}
to {
top: 300%;
opacity: 0;
}
}
@keyframes bounce {
from {
font-size: 1em;
}
3% {
font-size: 1.25em;
}
6% {
font-size: 0.75em;
}
9% {
font-size: 1em;
}
}
Now we see a nice label animation straight after we load the page. All that was done without a single line of JavaScript so far. But the task says we need to animate on user interaction.
Ok, let's now add some javascript. But before that we need a possibility to trigger the animation, let's trigger it using CSS class: animate
, our CSS then changes like
a.arrow.animate.fa-arrow-up:after {
animation-name: slideup, bounce;
animation-duration: 3s;
}
a.arrow.animate.fa-arrow-down:after {
animation-name: slidedown, bounce;
animation-duration: 3s;
}
Note added animate
class. If we now manually add the class to the HTML - we will see the animation again. But we need that to happen on user click, well that is easy:
function arrowClickHandler(e) {
var arrow = e.target;
arrow.className += ' animate';
}
var arrows = document.getElementsByClassName("arrow");
for (var i = 0; i < arrows.length; i++) {
arrows[i].addEventListener("click", arrowClickHandler, false);
}
Now, if we load the page and click the arrow - we will see the animation, but only once. We need to find a way to reset it. Let's remove the animate
class on animation finish.
function arrowAnimationEndHandler(e) {
var arrow = e.target;
if (typeof arrow === 'undefined') {
return;
}
arrow.className = arrow.className.replace(/\banimate\b/,'');
}
var arrows = document.getElementsByClassName("arrow");
for (var i = 0; i < arrows.length; i++) {
arrows[i].addEventListener("animationend", arrowAnimationEndHandler, false);
}
Now, we can click the arrow and see an animation as many times as we want. But there is a problem, we can't restart the animation if it is going already. For that we need a little trick:
function arrowClickHandler(e) {
var arrow = e.target;
if (typeof arrow === 'undefined') {
return;
}
arrow.className = arrow.className.replace(/\banimate\b/,'');
setTimeout(function () {
arrow.className += ' animate';
}, 0);
}
as long as we remote the animate
class - we give the browser a chance to execute it's code and stop the animation and then we add the animate
class again.
We need to apply our business logic (change the rating) on user input. Here is no rocket science, we read current value and update it according to the values we have assigned to arrows:
function arrowClickHandler(e) {
...
ratingUpdateBusinessLogic(arrow);
}
function ratingUpdateBusinessLogic(arrow) {
if (typeof ratingElement === 'undefined') {
return;
}
var ratingDelta = parseInt(arrow.getAttribute('data-value'), 10);
ratingElement.innerHTML = parseInt(ratingElement.innerHTML, 10) + ratingDelta;
}
var ratingElement = document.getElementById("rating");
UPDATE:
solution with glyphicons would require replacing css/html classes fa fa-arrow-up
and fa fa-arrow-down
with corresponding glyphicon classes, i.e.: glyphicon glyphicon-arrow-up
and glyphicon glyphicon-arrow-down
. After little thinking I also decided to unbind the custom css from library classes and added custom arrow-up
and arrow-down
classes to simplify the icon library replacement:
<a class="arrow arrow-up glyphicon glyphicon-arrow-up" data-animation-text="Sick!" data-value="1"></a>
<a class="arrow arrow-down glyphicon glyphicon-arrow-down" data-animation-text="Suck!" data-value="-1"></a>
CSS
a.arrow.arrow-up {
.
}
a.arrow.arrow-down {
...
}
a.arrow.arrow-up:after {
...
}
a.arrow.arrow-down:after {
...
}
a.arrow.animate.arrow-up:after {
...
}
a.arrow.animate.arrow-down:after {
...
}
Upvotes: 3
Reputation: 988
You can use jquery animate to get that effect. Try this
EDIT: for exact effect use jquery easing plugin and give
easeOutElastic easing effect
$("#select1").click(function() {
$(".nice").css("display","block");
$(".nice").animate({
top: -10,
}, 500, "easeOutElastic", function() {
// Animation complete.
$(".nice").css({"opacity":"1", "top":"10px","display":"none"});
});
});
$("#select2").click(function(){
$(".troll").css("display","block");
$(".troll").animate({
top: 130,
}, 500,"easeOutElastic", function(){
$(".troll").css({"opacity":"1", "top":"120px","display":"none"});
});
});
ul > li{
list-style:none;
}
span {
cursor: pointer;
}
.fa {
font-size: 55px;
text-indent: 200px;
margin-bottom: 20px;
margin-top:30px;
}
.nice{
position:absolute;
top:10px;
text-indent :190px;
display:none;
}
.troll{
position:absolute;
top:120px;
text-indent : 190px;
display:none;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js"></script>
<ul>
<li>
<p class="nice">Nice</p>
<span id='select1'><i class="fa fa-long-arrow-up" aria-hidden="true"></i></span></li>
<li><span id='select2'><i class="fa fa-long-arrow-down" aria-hidden="true"></i></span>
<p class="troll">Troll</p>
</li>
</ul>
Upvotes: 2
Reputation: 89
Add a setTimeout to make the text fade out after a few milliseconds:
$("span").click(function() {
$("span").css("color", "grey");
$(this).css("color", "red");
});
$("#select1").click(function() {
$("#down").fadeOut(300);
$("#up").fadeIn(300);
setTimeout(function() {
$("#up").fadeOut(300); // fade out the up text
}, 300); // delay of 0.3s before fading out
});
$("#select2").click(function() {
$("#up").fadeOut(300);
$("#down").fadeIn(300);
setTimeout(function() {
$("#down").fadeOut(300); // fade out the down text
}, 300); // delay of 0.3s before fading out
});
jsFiddle: https://jsfiddle.net/qze7mqj4/16/
I also added a position:absolute;
to the fading text so that it doesn't make the arrows "jump" around.
You can read more about setTimeout here: http://www.w3schools.com/jsref/met_win_settimeout.asp
Basically it tells the browser to execute a function after a specified number of milliseconds, in this case, we tell the browser to fadeOut() the text after 300ms.
Upvotes: 0
Reputation: 888
Just Add the text and show/hide it with the help of fadeout and fadein property of Jquery. Check your updated fiddle
$("span").click(function() {
if($(this).attr('id')=='select1')
{
$("#downText").fadeOut(300);
$("#upText").fadeIn(300);
}
else
{
$("#upText").fadeOut(300);
$("#downText").fadeIn(300);
}
$("span").css("color", "grey");
$(this).css("color", "red");
});
$("fa").click(function(){
$("fa").fadeTo("slow", 0.15);
});
Upvotes: 1