I was just going through the web and found some cool text animations over here.
so I thought of taking a part of it and extending it.
As I know what to do first I went through the stack to find out whether any question will relate my idea and for my surprise, I had found one that explains what I need but that has not been answered correctly. I guess the main cause was because it was not explained correctly. so I will try my best to explain the idea.
Previously asked question
The Idea.
Let's suppose I have a heading tag that I need to animate. Them main Idea is not to break one same sentence into two instead if you write a heading or paragraph tag the whole thing should animate. I want to lift/reveal/raise the words from where they are in the image (from the line)
I have changed some of the existing code from the source I got. But the text is revealing/raising from the bottom of the whole block. Which I don't want. I want it to raise them from the line at bottom.
The Code:
// Wrap every letter in a span
$('.ml16').each(function() {
$(this).html($(this).text().replace(/([^\x00-\x80]|\w)/g, "<span class='letter'>$&</span>"));
});
anime.timeline({
loop: true
})
.add({
targets: '.ml16 .letter',
translateY: [100, 0],
easing: "easeOutExpo",
duration: 1400,
delay: function(el, i) {
return 30 * i;
}
}).add({
targets: '.ml16',
opacity: 0,
duration: 1000,
easing: "easeOutExpo",
delay: 1000
});
.wrap {
width: 700px;
margin: 100px auto;
}
.ml16 {
color: #402d2d;
padding: 40px 0;
font-weight: 800;
font-size: 2em;
text-transform: uppercase;
letter-spacing: 0.5em;
overflow: hidden;
text-transform: uppercase;
font-family: sans-serif;
}
.ml16 .letter {
display: inline-block;
line-height: 2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/2.0.2/anime.min.js"></script>
<div class="wrap">
<h1 class="ml16"><span>Made with love for a testing purpose</span></h1>
</div>
Can someone help me pushing my incomplete idea to a destination?
What you need to do is wrap each word in another span (say, <span class="word"></span>) and set an overflow: hidden to that - see this fiddle: https://jsfiddle.net/w5uz4mex/
This will ensure that each word independently gets 'hidden' as it animates.
// Wrap every word in a span
$('.ml16').each(function() {
let text = $(this).text();
let words = text.split(' ');
// Clear current element
this.innerHTML = '';
// Loop through each word, wrap each letter in a span
for(let word of words) {
let word_split = word.replace(/([^\x00-\x80]|\w)/g, "<span class='letter'>$&</span>");
// Wrap another span around each word, add word to header
this.innerHTML += '<span class="word">' + word_split + '</span>';
}
});
anime.timeline({
loop: true
})
.add({
targets: '.ml16 .letter',
translateY: [100, 0],
easing: "easeOutExpo",
duration: 1400,
delay: function(el, i) {
return 30 * i;
}
}).add({
targets: '.ml16',
opacity: 0,
duration: 1000,
easing: "easeOutExpo",
delay: 1000
});
.wrap {
width: 700px;
margin: 100px auto;
}
.ml16 {
color: #402d2d;
padding: 40px 0;
font-weight: 800;
font-size: 2em;
text-transform: uppercase;
letter-spacing: 0.5em;
overflow: hidden;
text-transform: uppercase;
font-family: sans-serif;
}
.ml16 .word {
display: inline-block;
overflow: hidden;
height: 2em;
margin: 0 0.4em;
}
.ml16 .letter {
display: inline-block;
line-height: 2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/2.0.2/anime.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrap">
<h1 class="ml16">Made with love for a testing purpose</h1>
</div>
Edit: As a bonus (unrelated) this can be done very simply without jQuery, and instead utilising CSS animations. This also gives you the benefit of very easily being able to add new animations via the CSS, without having to touch the JS. This is just a quick demo, so should be used as a starting point only (i.e. it has not been tested for any production environment).
See below for an example of slideUp, slideDown and zoomIn
/**
* Create new class for sliding text
*
* #params {Element} wrapper - HTML element with text content
*/
class TextSliderUpper {
constructor(wrapper) {
this.wrapper = wrapper;
// Set delay between characters (in ms)
this.delay = 40;
// Wrap content in relevant wrappers
this._wrapContent();
}
_wrapContent() {
let words = this.wrapper.textContent.split(' ');
let delay = 0;
let content = '';
// Loop through each word, wrap each character in a span
words.forEach((word, multiplier) => {
let word_split = word.split(/([^\x00-\x80]|\w)/g);
let word_content = '';
// Look through each letter, add a delay (incremented)
word_split.forEach((char, index) => {
delay += this.delay;
word_content += `<span style="animation-delay: ${delay}ms">${char}</span>`;
});
// Add spacing between words
if (content !== '') content += ' ';
// Add wrapped words to content
content += `<span>${word_content}</span>`;
})
// Add content to wrapper
this.wrapper.innerHTML = content;
}
init() {
this.wrapper.classList.add('show');
}
}
// Get a list of all headers
let headers = document.querySelectorAll('[data-animate]');
// Loop through, add relevant class
Array.from(headers).forEach(header => {
let slideHeader = new TextSliderUpper(header);
// Allow for delays? Sure!
let delay = header.dataset.delay || 0;
// Delay class (if necessary)
setTimeout(() => {
slideHeader.init();
}, delay)
})
body {
font-family: sans-serif;
}
h1 {
text-transform: uppercase;
font-weight: bold;
letter-spacing: 0.1em;
}
[data-animate] {
line-height: 1.2em;
}
[data-animate] > span {
display: inline-block;
height: 1.2em;
overflow: hidden;
}
[data-animate] > span > span {
display: none;
animation: 3s cubic-bezier(0, 1.2, 0.1, 0.9);
animation-fill-mode: backwards;
}
[data-animate].show > span > span {
display: inline-block;
}
[data-animate=slideup] > span > span {
animation-name: slideUp;
}
[data-animate=zoomin] > span > span {
animation-name: zoomIn;
}
[data-animate=slidedown] > span > span {
animation-name: slideDown;
}
#keyframes slideUp {
from {
opacity: 0;
transform: translate(0, 1.2em);
}
}
#keyframes zoomIn {
from {
opacity: 0;
transform: scale(0);
}
}
#keyframes slideDown {
from {
opacity: 0;
transform: translate(0, -1.2em);
}
}
<h1 data-animate="slideup">This is some text. Hello there!</h1>
<hr />
<h1 data-animate="zoomin" data-delay="2000">I am delayed!</h1>
<hr />
<h1 data-animate="slidedown" data-delay="7000">I am late to the party!</h1>
Related
jQuery(function ($) {
var target = $("#target");
target.html(target.text().replace(/./g, "<span>$&</span>"));
setTimeout(runAnimation, 250);
function runAnimation() {
var index, spans;
index = 0;
spans = target.children();
doOne();
function doOne() {
var span = $(spans[index]);
if (!$.trim(span.text())) {
// Skip blanks
next();
return;
}
// Do this one
span.css({
position: "relative",
})
.animate(
{
top: "-20",
},
"fast"
)
.animate(
{
top: "0",
},
"fast",
function () {
span.css("position", "");
next();
}
);
}
function next() {
++index;
if (index < spans.length) {
doOne();
} else {
setTimeout(runAnimation, 500);
}
}
}
});
.title {
margin: auto;
width: 50%;
color: black;
text-align: right;
}
#target:hover {
color: rgb(21, 121, 252);
animation-name: bounce;
animation-duration: 2s;
}
#keyframes bounce {
0% { transform: translateY(0); }
50% { transform: translateY(-50px); }
100% { transform: translateY(0); }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="title">
<p style="font-size: 45px;"><span id="target">I</span><span id="target">'</span><span id="target">m</span><span>
</span><span id="target">K</span><span id="target">r</span><span id="target">i</span><span id="target">s</span>
</p>
</div>
I'm trying to add a wobble effect on each letter but I can't get it to work, I would like the letters to get a bit bigger while hovering them and making the effect run. I'm just learning javascript so I'm not really good at it, the snippet doesn't work and I don't know what's the problem with it.
I found this code for the wobble effect but it's not working, can someone help ? Thanks
Instead of manually typing the letters in individual span blocks, let JavaScript do it. This will be much more flexible.
Other than that, you do not need to use JavaScript for the animation, do it with CSS instead. That will be much simpler and handleable.
const animatedElements = document.getElementsByClassName('animate');
[...animatedElements].forEach(elm => {
let text = elm.innerText.split(''); // Split the text into letters and store them in an array
elm.innerText = ''; // Clear the text in the div
// Add the letters one by one, this time inside a <span>
text.forEach(letter => {
const span = document.createElement('span');
span.innerText = letter;
elm.appendChild(span);
})
})
.animate>span {
display: inline-block; /* Changing display from the default `inline` to `inline-block` so that `transform` rules apply */
white-space: break-spaces; /* This is necessary so that the `inline-block` does not collapse the "spaces" */
transition: transform 200ms ease-in-out;
}
.animate>span:hover {
transform: translateY(-10px);
}
/* The following rules are just to format the embedded result */
.animate {
margin: 40px;
font-size: 40px;
}
<div class="animate">
I'm Kris
</div>
I have created a marquee slider. After a few iterations the marquee lacks.
I want to have the marquee to have full view-width. As I can see the marquee is lacking less on smaller devices. I have created other similar marquees before with different elements and never had that problem. The empty list elements I have implemented to have a spacing between the text elements, but the icons should be next to the text as they are currently.
(function marquee() {
var marqueeWrapper = $('.js-marquee');
var FPS = (60/100); // 60fps
var SLIDESPEED = 1000; // default | lower is faster
marqueeWrapper.each(function (index, element) {
var _marqueeWrapper = $(element);
var _marqueeTracks = $('>ul', _marqueeWrapper);
var _marqueeSlides = $('>ul>li', _marqueeWrapper);
var _marqueeWidth = parseFloat(_marqueeSlides.last().position().left + _marqueeSlides.last().outerWidth(true));
var shifted = _marqueeWrapper.attr('data-marquee-shift') || false;
var SPEED = (_marqueeWrapper.attr('data-marquee-speed') * _marqueeSlides.length) || (SLIDESPEED * _marqueeSlides.length);
var frames = SPEED * FPS;
var steps = _marqueeWidth / frames; // distance elems will move each frames
var posX = 0;
var tempSteps;
function _clone() {
var times = Math.ceil(_marqueeWrapper.outerWidth(true) / _marqueeWidth) + 1;
_marqueeTracks.each(function () {
$('>ul', _marqueeWrapper).empty();
var sliders = _marqueeSlides;
for (i = 1; i <= times; i++) {
sliders.clone().appendTo(($(this)));
}
})
}
function _animated() {
posX += -steps;
_marqueeTracks.css({
transform: 'translate3d(' + posX + 'px, 0, 0)'
});
if (Math.abs(posX) >= _marqueeWidth) {
posX = 0;
}
window.requestAnimationFrame(_animated);
}
function _pause() {
tempSteps = steps;
return steps = 0;
}
function _resume() {
return steps = tempSteps;
}
function _shiftPosition() {
if(shifted) return posX = -(_marqueeSlides.first().outerWidth(true)) / 2 ;
}
/*
function _registerEvents() {
_marqueeTracks.on('mouseenter', _pause);
_marqueeTracks.on('mouseleave', _resume);
$(window).on('resize', debounce(_clone, 300))
}*/
function init() {
_shiftPosition()
_clone();
_animated();
/*_registerEvents();*/
}
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};// debounce
init();
})
})();
.marquee {
background: black;
width: 100%;
overflow: hidden;
}
.marquee__track {
display: flex;
padding: 0;
margin: 0;
list-style: none;
}
.marquee__item {
display: flex;
justify-content: center;
align-items: center;
color: white;
height: 100px;
margin-right: 10px;
padding-bottom: 10px;
}
.marquee__item {
height: 80px;
}
.marquee__item_vegan {
flex: 0 0 120px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_gluten {
flex: 0 0 160px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_natural {
flex: 0 0 130px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_empty {
flex: 0 0 180px;
}
.marquee__item_small {
flex: 0 0 80px;
}
.marquee__item_icon {
width: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div data-marquee-speed="100" data-marquee-shift="false" class="marquee js-marquee">
<ul class="marquee__track">
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_vegan">VEGAN</li>
<li class="marquee__item marquee__item_empty"></li>
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item marquee__item_empty"></li>
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_natural">NATURAL</li>
<li class="marquee__item marquee__item_empty"></li>
</ul>
</div>
The given code seems rather complex for a simple marquee.
While you will need to invoke a little JS at the start (on a load or a resize) to make sure the timing is right for the speed required (it varies depending on viewport width) there seems little to be gained from using JS for the actual animation.
And there may be some loss in using JS which does not guarantee real-time timing (your system may be busy doing other things) and that can introduce lag. Using a CSS method can help to smooth things as there is less need to move back and forth between CPU and GPU.
The given code also performs the animation in steps, moving position, which can be jerkier than translating.
This snippet is stripped down to demonstrate the basic ideas in using HTML and CSS without JS.
As in the code in the question, a second copy of the items is made. This allows the marquee to appear continuous, as the first item disappears to the left it can be seen coming in from the right.
The units used are related to the viewport width so the whole thing is responsive. A CSS animation is used to get the movement, moving the whole 50% of its width, then when the copy comes in it is overwritten by the first part so the movement looks continuous (this is the same technique as used in the given code but with pure CSS rather than JS/CSS).
.marquee,
.marquee * {
margin: 0;
padding: 0;
border-width: 0;
}
.marquee {
background: black;
overflow: hidden;
}
.marquee>ul {
display: flex;
width: 200vw;
justify-content: space-around;
list-style: none;
animation: move 5s linear infinite;
white-space: nowrap;
}
.marquee>ul li {
font-size: 4vw;
color: white;
margin: 5vw;
position: relative;
}
.marquee__item_vegan,
.marquee__item_gluten,
.marquee__item_natural {
--bg: linear-gradient(white, white);
/* just for demo */
}
.marquee>ul li::before {
content: '';
width: 4vw;
height: 4vw;
left: -4vw;
display: inline-block;
position: absolute;
background-image: var(--bg);
background-repeat: no-repeat;
background-size: 80% 80%;
background-position: bottom left;
}
#keyframes move {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
<div class="marquee">
<ul>
<li class="marquee__item_vegan">VEGAN</li>
<li class="marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item_natural">NATURAL</li>
<li class="marquee__item_vegan">VEGAN</li>
<li class="marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item_natural">NATURAL</li>
</ul>
</div>
Notes: you may want to reinstate some JS to calculate the timing - depends on your use case.
The spurious list items have been removed. The imgs which are really just visual clues are added as before pseudo elements. The empty items are not needed. (It's best to keep formatting and content separate, especially for those using assistive technology such as screen readers).
I have 2 buttons with the same html structure on the same page. I have some js code to set the width depending on the text length of the button text. I need the js code to affect each individual button.
Please see the codepen here:
https://codepen.io/gianpiero-di-lullo/pen/gOYxoVr
I've inserted them in an each() loop
$(".LIbtnWrapper").each(function(){
var frontW = $(".LIbtnFront h1").width(),
frontH = $(".LIbtnFront h1").height(),
backW = $(".LIbtnBack h1").width(),
backH = $(".LIbtnBack h1").height();
if(frontW > backW) {
$(".LIbtnBack h1").width(frontW);
} else {
$(".LIbtnFront h1").width(backW);
}
})
I expect each button to set their own frontFace and backface width based on their text length and also behave independently on mouse events
The problem is there's no relation between your callback context and the way you're targeting the inner elements.
$(".LIbtnWrapper").each(function(){
var frontW = $(".LIbtnFront h1").width(),
The selector doesn't know that you mean the h1 in the element being iterated over - you're using a general selector, so it will just use the width of the first one it finds in the DOM matching the selector.
Instead, tightly couple your selectors to your callback context.
$(".LIbtnWrapper").each(function(){
var frontW = $(this).find("h1").width(), //<-- h1 inside current iterator element
You can use $(this) inside each to select the processing element and find() to select the child:
var a = $(".LIbtnWrapper");
$.each(a,function(){
var front = $(this).find(".LIbtnFront").find("h1");
var back = $(this).find(".LIbtnBack ").find("h1");
var frontW = front.width(),
frontH = front.height(),
backW = back.width(),
backH = back.height();
if(frontW > backW) {
back.width(frontW);
} else {
front.width(backW);
}
})
This is about context, and the element is each .LIbtnWrapper - the context. So you have to find the child element from that for the buttons.
More about context here https://stackoverflow.com/a/16422612/125981
$(".LIbtnWrapper").each(function(index, element) {
let bf = $(element).find(".LIbtnFront").find("h1");
let bb = $(element).find(".LIbtnBack").find("h1");
let frontW = bf.width(),
frontH = bf.height(),
backW = bb.width(),
backH = bb.height();
console.log(frontW, frontH, backW, backH);
if (frontW > backW) {
bb.width(frontW);
} else {
bf.width(backW);
}
});
TweenLite.set(".LIbtnWrapper", {
perspective: 1000
});
TweenLite.set(".LIbtn", {
transformStyle: "preserve-3d"
});
TweenLite.set(".LIbtnBack", {
rotationX: -90,
transformOrigin: "50% top",
y: "100%"
});
TweenLite.set([".LIbtnBack", ".LIbtnFront"], {
backfaceVisibility: "hidden"
});
$(".LIbtnWrapper").hover(
function() {
TweenLite.to($(this).find(".LIbtn"), .3, {
rotationX: 90,
ease: Power4.easeOut
});
},
function() {
TweenLite.to($(this).find(".LIbtn"), .5, {
rotationX: 0,
ease: Power4.easeOut
});
}
);
$(".LIbtnWrapper")
.mouseup(function() {
TweenLite.to($(this), .1, {
transformOrigin: "center",
scale: "1"
});
$(".LIbtnFace h1").css({
color: "#fff"
});
})
.mousedown(function() {
TweenLite.to($(this), .04, {
transformOrigin: "center",
scale: ".96"
});
$(".LIbtnFace h1").css({
color: "#00B1A7"
});
});
body {
background-color: black;
margin: 20px;
font-family: Arial, sans-serif;
padding: 20px 0px
}
.container {
width: 80%;
display: flex
}
.LIbtnWrapper {
position: relative;
float: left;
margin: auto;
cursor: pointer;
-webkit-font-smoothing: antialiased;
}
.LIbtnFace {
position: absolute;
overflow: hidden;
}
.LIbtnFront,
.LIbtnBack {
background-color: rgba(0 0 0 0);
}
.LIbtn-large {
font-size: 30px;
}
.LIbtn-medium {
font-size: 20px;
}
.LIbtn-small {
font-size: 10px;
}
.LIbtnFace h1 {
margin: auto;
padding: 15px;
border: solid 6px #fff;
color: white;
white-space: nowrap;
text-align: center;
}
.LIbtnFace.LIbtnBack h1 {
border-color: #00B1A7;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<div class="container">
<div class="LIbtnWrapper">
<div class="LIbtn">
<div class="LIbtnFace LIbtnFront">
<h1 class="LIbtn-large">FRONT</h1>
</div>
<div class="LIbtnFace LIbtnBack">
<h1 class="LIbtn-large">THIS IS THE BACK</h1>
</div>
</div>
</div>
<div class="LIbtnWrapper">
<div class="LIbtn">
<div class="LIbtnFace LIbtnFront">
<h1 class="LIbtn-large">ANOTHER FRONT</h1>
</div>
<div class="LIbtnFace LIbtnBack">
<h1 class="LIbtn-large">BACK</h1>
</div>
</div>
</div>
</div>
jQuery's .each takes two arguments: index and element
$('.LIbtnWrapper').each(function(index, element){
// log the current element in this iteration
console.log('the current element: ', element);
// get child element with context of this/element
var thisH1 = $('.LIbtnFront h1', element);
console.log('the child h1 of the current element', thisH1);
});
This is the "jQuery" way of accessing the current element and its children as shown in the api examples.
My purpose is to make an image blink 3 times on a scroll (like lights on, then off, 3 times in a row with a 1 sec delay), then stay on until user scrolls down more that 3600px.
I've added event listener:
created() {
window.addEventListener('scroll', this.scrollAnimation)
}
On scroll i fire method scrollAnimation:
methods: {
scrollAnimation() {
let currentPos = window.pageYOffset
if (currentPos > 3000 && currentPos < 3600) {
this.$refs.supportOff.style.display = 'none'
this.$refs.supportOn.style.display = 'block'
} else {
this.$refs.supportOff.style.display = 'block'
this.$refs.supportOn.style.display = 'none'
}
}
}
And here's the template for the images:
<div class="support__image-wrapper">
<img ref="supportOff" class="support__image support__image_on" src="../../assets/images/247-off.png">
<img ref="supportOn" class="support__image support__image_off" src="../../assets/images/247-on.png">
</div>
Now this code works, when i scroll 3000 pixels down, but not lower than 3600 pixels, it shows 247-on image and hides 247-off image. But, there's an issues with blinking it, if i will use setInterval it's going to be fired every time a user scrolls between 3000 and 3600 px. What's the best way to achieve that blinking?
A few things to try...
Don't start manipulating the dom with $ref
Instead, create a variable which will trigger changes in the dom
methods: {
scrollAnimation() {
this.showSupport = window.pageYOffset > 3000 && window.pageYOffset < 3600
}
}
<div>
<img v-if="showSupport" class="blink" src="../../assets/images/247-on.png">
<img v-else src="../../assets/images/247-off.png">
</div>
Blinking I would advise using css animations (#keyframes). This way you can control the timing of the blink without anything in the script. It would just start blinking as soon as it's visible on the page.
.blink {
animation: blink 1s infinite;
}
#keyframes blink {
0% {opacity: 0}
49%{opacity: 0}
50% {opacity: 1}
}
Hope this helps.
Just wanted to add a quick demo for future readers, based on t3__rry's comment on how scroll based events which are not optimized/debounced can lead to serious performance issue; as well as Mulhoon's nice advice on utilizing CSS #keyframes for blinking animation:
new Vue({
el: '#app',
data() {
return {
blinkRate: 1000,
blinkCount: 3,
blinking: false,
blinkTimeoutId: -1,
state: false,
currentPos: window.pageYOffset
}
},
mounted() {
window.addEventListener('scroll', _.debounce(() => {
this.currentPos = window.pageYOffset;
if (this.currentPos > 3000 && this.currentPos < 3600) {
this.state = true;
}
else {
this.state = false;
}
}), 100);
},
methods: {
blink() {
if (this.blinkTimeoutId > -1) {
clearTimeout(this.blinkTimeoutId);
}
this.blinking = true;
this.blinkTimeoutId = setTimeout(() => {
this.blinking = false;
}, 1000 * this.blinkCount);
}
},
watch: {
state() {
this.blink();
}
}
});
#app {
background-color: gainsboro;
height: 2000vh;
padding: 10px;
}
.page-offset {
position: fixed;
top: 20px;
right: 20px;
}
.blinker > div {
border-radius: 50%;
border: 2px solid white;
color: white;
font-weight: bold;
height: 35px;
left: 20px;
line-height: 35px;
padding: 5px;
position: fixed;
text-align: center;
top: 20px;
vertical-align: middle;
width: 35px;
}
.blinker.animate > div {
animation: blink 1s infinite;
}
.blinker .on {
background-color: green;
}
.blinker .off {
background-color: crimson;
}
#keyframes blink {
0% {
opacity: 0
}
49% {
opacity: 0
}
50% {
opacity: 1
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<div id="app">
<div :class="['blinker', { 'animate': blinking } ]">
<div class="on" v-if="state">ON</div>
<div class="off" v-else>OFF</div>
</div>
<code class="page-offset">pageYOffset: {{Math.floor(currentPos)}}</code>
</div>
i'm trying to animate text in element dynamically but i cant find a way to do that, there is what i tried so far https://jsfiddle.net/yup55u9f/3/ but its not the best way.
i have tried some methods like split text into array and push letters in span but it doesn't work.
var i = -1,
spn = document.querySelectorAll('.spn'),
stInt;
var setTO = setTimeout(function AnimTxt() {
stInt = setInterval(function () {
if (i <= spn.length) {
i += 1;
$('.spn').eq(i).css({
color: "red",
marginTop: "-10px"
});
return false;
}
}, 100);
}, 2000);
.spn {
position: absolute;
transition: all 1s ease;
top: 8px;
left: 5px;
text-transform: uppercase;
letter-spacing: 0px;
margin-top: 40px;
}
.spn:nth-of-type(2) {
left: 16px
}
.spn:nth-of-type(3) {
left: 27px
}
.spn:nth-of-type(4) {
left: 42px
}
p {
margin-top: 30px;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<span class="placeholder-cont">
<span class="spn">t</span>
<span class="spn">e</span>
<span class="spn">x</span>
<span class="spn">t</span>
</span>
I tried separating letters into span tags for now. As for this - it will be better without separating in new elements - I can't find a way to achieve this without separating letters into span.
for (i = 0; i < text.length; i++) {
$(".placeholder-cont").append("<span class='spn'>" + text[i] + "</span>");
}
And then animating them using .each() and setTimeout() instead of setInterval
$(".spn").each(function(index, el) {
setTimeout(function() {
$(el).css({
color: "red",
marginTop: "-10px"
});
if (index == (text.length - 1)) {
setTimeout(function() {
$('p').show();
}, 1000);
}
}, 100 * index);
});
Please refer this fiddle.
EDIT
In order to remove position: absolute I have added to span tags -
display: inline-block;
and then animated using 'transform' property.
Updated fiddle.
Here is another approach. You could use split() to create array from letters and then use map() and replace() to wrap each letter in span and then join() to return to string.
$('.text').html($('.text').text().split('').map(function(e) {
return e.replace(/[^ ]/g, "<span class='letter'>$&</span>");
}).join(''));
$('.text .letter').each(function(i) {
setTimeout(() => {
$(this).css({
'transform': 'translateY(-50px)',
'color': 'red'
});
}, 100 * i);
})
.text {
font-size: 25px;
text-transform: uppercase;
}
.letter {
margin-top: 50px;
transition: all 0.3s ease-in-out;
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="text">Lorem ipsum dolor.</div>