Framer motion marquee skipping before animation ends - html

I have this framer motion-built marquee but it seems to skip before the text finishes going all the way through, any thoughts why? It animated fine but skips back to the beginning at the fifth word. Maybe be the values given for the x-coordinate?
.marquee {
position: relative;
width: 100vw;
max-width: 100%;
height: 206px;
overflow-x: hidden;
}
.track {
position: absolute;
white-space: nowrap;
}
.track > h1 {
margin: 20px 0;
font-size: 8rem;
font-family: machina;
-webkit-text-fill-color: rgba(255, 255, 255, 0);
-webkit-text-stroke-width: 2px;
-webkit-text-stroke-color: #f4955c;
text-transform: uppercase;
}
import React from 'react'
import { motion } from 'framer-motion'
const marqueeVariants = {
animate: {
x: [0, -1400],
transition: {
x: {
repeat: Infinity,
repeatType: "loop",
duration: 10,
ease: "linear",
},
},
},
};
const Marquee = () => {
return (
<div className='marquee machina'>
<motion.div
className='track'
variants={marqueeVariants}
animate='animate'>
<h1>Changing the way you view fitness by re-defining your holisitc RITUAL.</h1>
</motion.div>
</div>
)
}
export default Marquee

yes, you need to manually synchronize the two values. I suggest that you temporarily put a delay in the transition, to give you time to calibrate.

Related

Make Button Animation like in Google (Ripple Effect)

I found a button on a website that has the animation of a google Button.
How do you make such a button that it makes an animation wherever you click?
Here is my code what I have done so far:
button {
text-transform: uppercase;
padding: 0.8em;
width: 100px;
background: #0053d9;
color: #fff;
border: none;
border-radius: 5px;
transition: all 0.2s;
font-size: 15px;
font-weight: 500;
}
button:hover {
filter: brightness(80%);
cursor: pointer;
}
button:active {
transform: scale(0.92)
}
<button>Login</button>
This effect is known as the Material ripple effect (or at least that's along the lines of what most people call it).
There are two ways to accomplish this effect - one using JS and CSS, for the full-fledged effect, which means the ripple comes out of where the mouse is, and one using pure CSS, and no JS - which results in the ripple coming out of the button no matter where the mouse is inside the button.
Some people prefer the CSS-only one as it is cleaner, but most prefer the full-fledged version as it takes into account the mouse position and hence delivers a slightly better experience...
Anyway, I've created both these effects, chose whichever you prefer :).
PS: here are the rules for any full-fledged versions you see:
The ripple must be created when the mouse is down on the button - not when the mouse is clicked because that takes an extra hundred miliseconds on mobile devices (because mobile browsers delay delivering the click event to be able to check if it is a single click or a double click). So with this kind of dalay before showing the ripple, user experience goes down drastically as your site will seem slow and laggy even though it probably isn't.
The ripple must stay on the button and cover its background until the mouse is up, or the button has lost focus - whichever comes first.
Without further ado, here is the code...
window.addEventListener("mousedown", e => {
const target = e.target;
if(target.nodeName == "BUTTON" && !target.classList.contains("css-only-ripple")) {
show_ripple(target);
}
});
function show_ripple(button) {
const style = getComputedStyle(button);
let ripple_elmnt = document.createElement("span");
let diameter = Math.max(parseInt(style.height), parseInt(style.width)) * 1.5;
let radius = diameter / 2;
ripple_elmnt.className = "ripple";
ripple_elmnt.style.height = ripple_elmnt.style.width = diameter + "px";
ripple_elmnt.style.position = "absolute";
ripple_elmnt.style.borderRadius = "1000px";
ripple_elmnt.style.pointerEvents = "none";
ripple_elmnt.style.left = event.clientX - button.offsetLeft - radius + "px";
ripple_elmnt.style.top = event.clientY - button.offsetTop - radius + "px";
ripple_elmnt.style.transform = "scale(0)";
ripple_elmnt.style.transition = "transform 500ms ease, opacity 400ms ease";
ripple_elmnt.style.background = "rgba(255,255,255,0.5)";
button.appendChild(ripple_elmnt);
setTimeout(() => {
ripple_elmnt.style.transform = "scale(1)";
}, 10);
button.addEventListener("mouseup", e => {
ripple_elmnt.style.opacity = 0;
setTimeout(() => {
try {
button.removeChild(ripple_elmnt);
} catch(er) {}
}, 400);
}, {once: true});
button.addEventListener("blur", e => {
ripple_elmnt.style.opacity = 0;
setTimeout(() => {
try {
button.removeChild(ripple_elmnt);
} catch(er) {}
}, 450);
}, {once: true});
}
button {
text-transform: uppercase;
padding: 0.8em;
width: 100px;
background: #0053d9;
color: #fff;
border: none;
border-radius: 5px;
transition: all 0.2s;
font-size: 15px;
font-weight: 500;
position: relative;
overflow: hidden;
}
button:hover {
filter: brightness(80%);
cursor: pointer;
}
button:active {
transform: scale(0.92)
}
.css-only-ripple::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 150%;
aspect-ratio: 1 / 1;
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
border-radius: 999px;
background: rgba(255, 255, 255, .5);
}
.css-only-ripple:focus::after {
animation: scale_up 1000ms forwards;
}
#keyframes scale_up {
0% {
transform: translate(-50%, -50%) scale(0);
opacity: 1;
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 0;
}
}
<button>Login</button>
<button class="css-only-ripple">Login</button>
<br>
The first button is the CSS and JS version, and the second is the CSS-only version. For the CSS-only button, you have to unfocus it before you click it again or the ripple will not show (it only gets created on focus)

How to create a div which disappears after a time, but hovering will keep it?

I am trying to create a notification which will pop up, then disappear after a few seconds, but I want the user to be able to hover over it with the mouse to keep it from disappearing. If I could I'd like to also have a click make it disappear, but that's not necessary. I'm not sure how I could have the hover in html interact with the embedded ruby to stop the timeout. I'm aware I might need to restructure the whole thing to make it work. Here's the relevant css and html/erb snippets (not enough to run):
setTimeout(function() {
$('#alert_box').fadeOut('fast');
}, 3000);
.alert_wrap {
padding: 0px 50px;
margin-top: 3px;
height: 5%;
background-color: rgba(255, 0, 0, .3);
border: 3px solid red;
grid-row-start: 2;
justify-self: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<% unless notice == nil %>
<div id="alert_box" class="alert_wrap">
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
</div>
<% end %>
var myTimeOut = setTimeout("mytimeoutfunction()", 3000);
$('#alert_box').mouseout( function () {
myTimeOut = setTimeout("mytimeoutfunction()", 3000)
});
$('#alert_box').mouseover( function () {
clearTimeout(myTimeOut);
});
var mytimeoutfunction = function () {
$('#alert_box').fadeOut('fast');
}
// On click, fadeout
$("#close").click(mytimeoutfunction);
.alert_wrap {
padding: 0px 50px;
margin-top: 3px;
height: 5%;
background-color: rgba(255, 0, 0, .3);
border: 3px solid red;
grid-row-start: 2;
justify-self: center;
}
#alert_box {
position: relative;
}
#close {
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="alert_box" class="alert_wrap">
<p class="notice">This is notice text</p>
<p class="alert">This is alert text</p>
<span id="close">X</span>
</div>
In the first step you can use setTimeout and then on mouseover or hover event, you can clear the timeout function using clearTimeout and thus the fadeout will not take effect.
And on mouseout you can again use the setTimeout to start counting for 3 seconds.
Also since you have mentioned about the click event, I have added a close button that on click can call the timeout function straight away.
You could approach it with CSS animations:
.alert_wrap {
padding: 0px 50px;
margin-top: 3px;
height: 5%;
background-color: rgba(255, 0, 0, .3);
border: 3px solid red;
grid-row-start: 2;
animation: alert 4s linear none;
opacity: 0;
transition: all .3s ease-in-out;
}
#keyframes alert {
0%,
100% {
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
}
.alert_wrap:hover {
opacity: 1;
}
<div class="alert_wrap">
<p>Some alert</p>
</div>
Something like this may also work, but I would probably do this with CSS transitions (again using :not(:hover) pseudo selector)
var handler = setInterval(hideBox, 100); // interval function
function hideBox() {
if ($('.alert_wrap:not(:hover)').length) { // check if the alert is visible and not hovered
setTimeout(function() { // after 3 seconds
if ($('.alert_wrap:not(:hover)').length) { // if not hovered yet
$('.alert_wrap:not(:hover)').fadeOut('fast');
clearInterval(handler);
handler = 0;
}
}, 3000);
}
}

How to animate width using inline css

I'm trying to animate a width of an element using data from an API upon page load and I'm using Vue js. What I've done is I used inlined css and apply width value (from API data). I'm able to add the element width but it has no animation.
Vue template edited:
<li v-for="(stats, index) in teamStats[0]">
<div class="bar">
<span :style="'width:'+ stats +'%;'">
{{stats}}
</span>
</div>
</li>
Sass:
.bar {
span {
text-align: $l;
right: 0;
width: 0%;
-webkit-transition: width 1s;
-moz-transition: width 1s;
-o-transition: width 1s;
transition: width 1s;
}
}
You'll probably need to use the JavaScript transition hooks. Here's an example.
new Vue({
el: '#app',
data: {
stats: [],
},
created() {
// Simulate loading data from server
setTimeout(() => {
this.stats = [0.1, 0.15, 0.32, 0.55, 0.88, 0.96];
}, 500);
},
methods: {
onEnter(el) {
var stat = +el.dataset.stat;
var index = +el.dataset.index;
el.style.transitionDelay = index * 0.05 + 's';
el.style.width = (stat * 100) + '%';
el.style.backgroundColor = `hsl(${50 - 50 * stat | 0}, 100%, 50%)`;
},
},
});
.bars {
width: 400px;
}
.bar {
margin: 5px 0;
height: 30px;
display: flex;
align-items: center;
justify-content: flex-end;
color: white;
font-family: sans-serif;
font-weight: bold;
padding: 5px;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
}
.bar.v-enter-active {
transition: all 1s cubic-bezier(0, 1, 0.5, 1);
}
.bar.v-enter {
/* Needs !important to override inline styles */
opacity: 0;
width: 0% !important;
background-color: hsl(50, 100%, 50%) !important;
}
<script src="https://cdn.rawgit.com/vuejs/vue/dev/dist/vue.min.js"></script>
<div id="app">
<transition-group tag="div" class="bars" #enter="onEnter">
<div class="bar" v-for="stat, index of stats" :key="stat" :data-stat="stat" :data-index="index">
{{ (stat * 100 | 0) }}%
</div>
</transition-group>
</div>

Css animation random delay

I'm trying to build a webpage for a comic studio and I want one of the characters to come in from the side every so often. So far I have this in the css
.charc {
animation:peek 20s infinite;
left:-500px
}
#-webkit-keyframes peek{
1% {transform:translateX(-500px)}
10%{transform:translateX(100px)}
20% {transform:translateX(-200px)}
100% {transform:translateX(-500px)}
}
and the html
<img src="character.jpg" class="charc"/>
This means the character comes on over and over again. I don't know whether it is possible to get random figures in CSS but I thought if it is, You guys would know
p.s. I know this will only work in chrome but I will be changing that soon.
You need to use js/jQuery for that.
function move() {
$('.charc')
.animate({
left: '-500px'
}, 200)
.animate({
left: '100px'
}, 400)
.animate({
left: '50px'
}, 400)
.animate({
left: '-500px'
}, 100, function() {
var nextIn = Math.floor(Math.random() * 1000);
setTimeout('move()', nextIn);
})
}
$(document).ready(function() {
move();
});
#scene {
width: 500px;
height: 100px;
border: 2px solid black;
margin: 20px;
}
.charc {
position: absolute;
left: -500px;
top: 20px;
width: 20px;
height: 20px;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="scene">
<div class="charc"></div>
</div>
It is possible with js :
var divElement = document.getElementsByClassName("charc")[0];
var maxValue = 20; //the random value won't exceed 20s
function randomTime(maxvalue){
return Math.round(Math.random() * maxvalue );
}
function changeAnimationTime(maxValue){
var random = randomTime(maxValue);
divElement.style.animation = "peek "+randomTime+"s infinite";
setTimeout(function(){
changeAnimationTime(maxValue);
},random);
}
changeAnimationTime(maxValue);
The advantage of this methode is that you won't use js for animation but just for generating values. So it consumes less ressources.
Not a random delay but you can use a tool I created called WAIT! Animate to add a pause between animations. Here's your animation with a 2 second pause between animations:
.peek.wait2.animated {
animation: peek-wait2 22s linear infinite;
transform-origin: 50% 50%
}
#keyframes peek-wait2 {
0% { transform:translateX(-500px) }
9.09091% { transform:translateX(100px) }
18.18182% { transform:translateX(-200px) }
90.90909% { transform:translateX(-500px) }
100% { transform:translateX(-500px) }
}
Use WAIT! Animate to change the pause duration.
PS. I suggest starting at 0% to avoid a flicker in the animation.

CSS Animation won't apply for the second time

I am currently having a problem with CSS animations. A random background is called from an array, shows up and changes and so on. I applied two animation for the image caption id, a slide in and a delayed slide out. The slide in and out runs well for the first time, but when the second background shows up, the caption just appears to the screen without any animation.
This is my test page and below is my code.
HTML code:
<script type="text/javascript">
function loadRandomImage(imgs) {
var index = Math.floor(Math.random() * imgs.length);
console.log("loadRandomImages(): index = "+ index);
$.backstretch(imgs[index].url, {duration: 30000, fade: 1200});
$("#caption").html(imgs[index].caption);
}
var images = new Array(); //array of imgs objects
images[0] = {url: "https://s-media-cache-ak0.pinimg.com/736x/a5/47/45/a5474577f4a4ae93c85db719d0cbafd4.jpg", caption: "Caption0"};
images[1] = {url: "https://s-media-cache-ak0.pinimg.com/736x/e6/41/74/e64174e355f78a0f07e951bcec62ca96.jpg", caption: "Caption1"};
images[2] = {url: "https://media.giphy.com/media/3o7abHrsGbV10rCeze/giphy.gif", caption:"Caption2"};
images[3] = {url: "https://media.giphy.com/media/Bbt5FxRiArl3a/giphy.gif", caption:"Caption3"};
// Preload
setTimeout(loadRandomImage, 1000, images);
// Change images every 3 seconds
setInterval(loadRandomImage, 30000, images);
</script>
<div id="pattern"></div>
<div id="pattern2"></div>
<div id="caption"></div>
CSS code:
#caption {
position: relative;
font: 1.5em Trebuchet, sans-serif;
text-align: center;
margin-left: 75%;
z-index: 56;
color: #ffffff;
background: rgba(0, 0, 0, 0.7);
padding: 8px;
animation: slidein 3s, slideout 3s 27s;
}
#caption:empty
{
display: none;
}
#keyframes slidein {
0% {
margin-left: 100%;
width:100%;
visibility:hidden;
opacity: 0;
}
100% {
margin-left: 75%;
width:100%;
opacity: 1;
visibility:visible;
}
}
#keyframes slideout {
0% {
margin-left: 75%;
width:100%;
opacity: 1;
visibility:visible;
}
100% {
margin-left: 100%;
width:100%;
opacity:0;
visibility:hidden;
}
}
CSS animations have iteration count (animation-iteration-count) as only 1 when no value is given for that property. Here since you've not specified any value, the animation executes only once (that is on page load). There is no pure CSS way to re-trigger an animation once it has completed its cycle. It has to be removed from the element and then re-attached for it to start all over again.
So, for your case here is what you have to do - (a) Set the animations on #caption using JS on page load as it makes it easier to remove and re-add them (b) Upon completion of the slideout animation, remove both the animations from the element (that is, set animation-name: none) and also set html of #caption to none because :empty selector would only then hide it. (c) As soon as the next image is set on the element (using loadRandomImage function), set the animations back on the element. This would re-trigger the animation and so during each image switch, the caption would slide-in and out.
Note: I've changed some parts in the HTML and JS that are not relevant to this answer (like removing the two div and replacing them with 1, avoiding the $.backstretch and loading image using css() etc. But these are only auxiliary items and will not affect the crux of this answer (which is, to remove and add the animations).
function loadRandomImage(imgs) {
var index = Math.floor(Math.random() * imgs.length);
$('#img').css('background-image', 'url(' + images[index].url + ')');
$('#caption').css({
'animation-name': 'slidein, slideout',
'animation-duration': '3s, 3s',
'animation-delay': '0s, 7s'
});
$("#caption").html(imgs[index].caption);
}
var images = new Array(); //array of imgs objects
images[0] = {
url: "http://lorempixel.com/100/100/nature/1",
caption: "Caption0"
};
images[1] = {
url: "http://lorempixel.com/100/100/nature/2",
caption: "Caption1"
};
images[2] = {
url: "http://lorempixel.com/100/100/nature/3",
caption: "Caption2"
};
images[3] = {
url: "http://lorempixel.com/100/100/nature/4",
caption: "Caption3"
};
// Preload
setTimeout(loadRandomImage, 1000, images);
$('#caption').on('animationend', function(e) {
if (e.originalEvent.animationName == 'slideout') {
$('#caption').css('animation-name', 'none');
$('#caption').html('');
setTimeout(function() { /* dummy timeout to make sure browser sees animation as none before adding it again */
loadRandomImage(images);
}, 0);
}
});
#caption {
position: relative;
font: 1.5em Trebuchet, sans-serif;
text-align: center;
margin-left: 75%;
z-index: 56;
color: #ffffff;
background: rgba(0, 0, 0, 0.7);
padding: 8px;
}
#caption:empty {
display: none;
}
#keyframes slidein {
0% {
margin-left: 100%;
width: 100%;
visibility: hidden;
opacity: 0;
}
100% {
margin-left: 75%;
width: 100%;
opacity: 1;
visibility: visible;
}
}
#keyframes slideout {
0% {
margin-left: 75%;
width: 100%;
opacity: 1;
visibility: visible;
}
100% {
margin-left: 100%;
width: 100%;
opacity: 0;
visibility: hidden;
}
}
/* Just for demo */
#img {
height: 100px;
width: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="img"></div>
<div id="caption"></div>
The animationend event still requires vendor prefixes in some browsers.
You need to use a callback, which is explained here:
How do create perpetual animation without freezing?
I think the animation direction needs to be altered.
These are the possibilities:
animation-direction: normal|reverse|alternate|alternate-reverse|initial|inherit;
I think you need to do one of these:
alternate
The animation will be played as normal every odd time (1,3,5,etc..) and in reverse direction every even time (2,4,6,etc...)
alternate-reverse
The animation will be played in reverse direction every odd time (1,3,5,etc..) and in a normal direction every even time (2,4,6,etc...)
At the moment it is set as
animation-direction: initial, initial;
Seen here: http://www.w3schools.com/cssref/css3_pr_animation-direction.asp
Rather than the Javascript suggestions already provided, you could do a straight CSS solution.
Just set animation-iteration-count to "infinite" (to continuously alternate the 2 elements, or an integer for a set number of repeats)
If you want staggered / alternating animations:
Use an animation-delay (matching the animation-duration) on the second element so it doesn't appear until the first element animation has completed
Build a delay onto the end of your animation (revert to original state # 50%) so that the first element stays hidden while the second animates.