I try to recreate this animation with HTML and CSS in React with Typescript using inline styling.I am creating an object with styling information, and refer to it in the style attribute. The code is down below. It does not work and I am not sure what I am doing wrong, I suspect the styles are not defined and referred correctly?
Here is the original Codepen example I try to rewrite: Apple Animation
And here is my code
import React from 'react';
const styles = {
'#keyframes showTopText': {
'0%': { transform: 'translate3d(0, 100%, 0)' },
'40%, 60%': { transform: 'translate3d(0, 50%, 0)' },
'100%': { transform: 'translate3d(0, 0, 0)' },
},
'#keyframes showBottomText': {
'0%': { transform: 'translate3d(0, -100%, 0)' },
'100%': { transform: 'translate3d(0, 0, 0)' },
},
animatedTitle: {
color: '#222',
fontFamily: 'Roboto, Arial, sans-serif',
height: '90vmin',
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)',
width: '90vmin',
},
'animatedTitle > div': {
height: '50%',
overflow: 'hidden',
position: 'absolute',
width: '100%',
},
'animatedTitle > div div': {
fontSize: '12vmin',
padding: '2vmin 0',
position: 'absolute',
},
'animatedTitle > div div span': {
display: 'block',
},
'animated-title > div.text-top': {
borderBottom: '1vmin solid #000',
top: 0,
},
'animatedTitle > div.text-top div': {
animation: 'showTopText 1s',
animationDelay: '0.5s',
animationFillMode: 'forwards',
bottom: 0,
transform: 'translate(0, 100%)',
},
'animatedTitle > div.text-top div span:first-child': {
color: '#767676',
},
'animatedTitle > div.text-bottom': {
bottom: 0,
},
'animatedTitle > div.text-bottom div': {
animation: 'showBottomText 0.5s',
animationDelay: '1.75s',
animationFillMode: 'forwards',
top: 0,
transform: 'translate(0, -100%)',
},
};
function Design() {
return (
<div style={styles.animatedTitle}>
<div style={styles['animatedTitle > div.text-top div']}>
<div>
<span>mimicking</span>
<span>apple's design</span>
</div>
</div>
<div style={styles['animatedTitle > div.text-bottom']}>
<div>for the win!</div>
</div>
</div>
);
}
export { Design };
You can try using styled-components to keep your component more flexible and compatible. I replicated the codepen example with styled-components and made it easily extensible.
So, TopAnimateBlock and BottomAnimateBlock have numOfLine property hence how many lines is inside the block. The second property in BottomAnimateBlock is delayTopLine, it should have the same numbers as a numOfLine in TopAnimateBlock, because we need to wait for top lines to play.
Also you can easily change the text color with color property in TextStyle and pass as color value, HEX color or rgba() / hsla().
TextAnimation.tsx
import styled, { keyframes } from 'styled-components';
const showTopText = keyframes`
0% { transform: translate3d(0, 100% , 0); }
40%, 60% { transform: translate3d(0, 50%, 0); }
100% { transform: translate3d(0, 0, 0); }
`;
const showBottomText = keyframes`
0% { transform: translate3d(0, -100%, 0); }
100% { transform: translate3d(0, 0, 0); }
`;
const Section = styled.section`
width: calc(100% + 10vmin);
display: flex;
flex-flow: column;
padding: 2vmin 0;
overflow: hidden;
&:last-child {
border-top: 1vmin solid white;
}
`;
const Block = styled.div<{ numOfLine: number }>`
position: relative;
`;
const TopAnimateBlock = styled(Block)`
animation: ${showTopText} calc(0.5s * ${props => props.numOfLine}) forwards;
animation-delay: 0.5s;
transform: translateY(calc(100% * ${props => props.numOfLine}));
`;
const BottomAnimateBlock = styled(Block)<{ delayTopLine: number }>`
animation: ${showBottomText} calc(0.5s * ${props => props.numOfLine}) forwards;
animation-delay: calc(0.7s * ${props => props.delayTopLine});
transform: translateY(calc(-100% * ${props => props.numOfLine}));
`;
const TextStyle = styled.p<{ color: string }>`
font-family: Roboto, Arial, sans-serif;
font-size: 12vmin;
color: ${props => props.color};
`;
export const TextAnimation = () => {
return (
<>
<Section>
<TopAnimateBlock numOfLine={2}>
<TextStyle color="grey">mimicking</TextStyle>
<TextStyle color="white">apple's design</TextStyle>
</TopAnimateBlock>
</Section>
<Section>
<BottomAnimateBlock numOfLine={1} delayTopLine={2}>
<TextStyle color="white">for the win!</TextStyle>
</BottomAnimateBlock>
</Section>
</>
);
};
If we want to animate 3 lines instead of 2, just add / change:
Add new TextStyle component
Change from 2 to 3 numOfLine in TopAnimateBlock and delayTopLine in BottomAnimateBlock
And replace keyframes to this fragment:
const showTopText = keyframes`
0% { transform: translate3d(0, 100% , 0); }
25%, 40% { transform: translate3d(0, 66%, 0); }
60%, 75% { transform: translate3d(0, 33%, 0); }
100% { transform: translate3d(0, 0, 0); }
`;
Related
I have created a dialog that slides out on closing if on mobile view.
Otherwise it just scales backwards.
This component is based on the awesome work from here:
https://web.dev/building-a-dialog-component/#styling-with-open-props
With some tweeks of my own to adapt it to react and my own needs.
This will add scrollbar on html body for a breef second.
This is super annoying and only fix that works is to hide scrollbar on the html-tag.
html {
box-sizing: border-box;
margin: 0px; padding: 0px
&::-webkit-scrollbar {
display: none;
}
scrollbar-width: none;
}
I do not like this at all, are there any better solutions for this?
import { useEffect, useState, useRef } from "react";
import styled, { keyframes } from "styled-components";
import PropTypes from "prop-types";
import { shadow } from "shared/theme";
import { spacingIndex } from "shared/spacing";
const scaleDown = keyframes`
to { transform: scale(.75) }
`;
const slideOutDown = keyframes`
to { transform: translateY(100%) }
`;
const slideInUp = keyframes`
from { transform: translateY(100%) }
`;
const Container = styled.dialog`
display: ${({ open }) => (open ? "flex" : "none")};
justify-content: center;
align-items: center;
border-radius: 8px;
border: none;
max-inline-size: min(90vw, 60ch);
max-block-size: min(80vh, 100%);
overflow: hidden;
margin: auto;
padding: ${spacingIndex * 2}px;
inset: 0;
box-shadow: ${shadow.overlay};
transition: opacity 0.5s cubic-bezier(0.25, 0, 0.3, 1);
animation: ${scaleDown} 0.5s cubic-bezier(0.25, 0, 0.3, 1) forwards;
animation-timing-function: cubic-bezier(0.5, -0.5, 0.1, 1.5);
&:is([open]) {
animation: ${slideInUp} 0.5s cubic-bezier(0.25, 0, 0.3, 1) forwards;
}
&:not([open]) {
pointer-events: none;
opacity: 0;
}
::backdrop {
backdrop-filter: blur(4px);
transition: backdrop-filter 0.5s ease;
}
#media (max-width: 768px) {
animation: ${slideOutDown} 0.5s cubic-bezier(0.25, 0, 0.3, 1) forwards;
margin-block-end: 16px;
}
`;
const Dialog = ({ isOpened, onClose, children }) => {
const ref = useRef(null);
const [open, setOpen] = useState(false);
const isClickOutsideOfDialog = (dialogEl, event) => {
if (!dialogEl) return false;
const rect = dialogEl.getBoundingClientRect();
return (
event.clientY < rect.top ||
event.clientY > rect.bottom ||
event.clientX < rect.left ||
event.clientX > rect.right
);
};
const onDialogContainerClick = (event) => {
if (isClickOutsideOfDialog(ref.current, event)) onClose();
};
const handleAnimationEnd = (e) => {
if (slideInUp.name !== e.animationName) setOpen(false);
};
useEffect(() => {
ref.current?.addEventListener("animationend", handleAnimationEnd);
return () => ref.current?.removeEventListener("animationend", handleAnimationEnd);
}, []);
useEffect(() => {
if (!isOpened) ref.current?.close();
if (isOpened) {
setOpen(true);
ref.current?.showModal();
}
}, [isOpened]);
return (
<Container
{...{
ref,
open,
onClick: onDialogContainerClick,
}}>
{children}
</Container>
);
};
Dialog.propTypes = {
isOpened: PropTypes.bool,
onClose: PropTypes.func,
};
export default Dialog;
Edit: Guess I was tired and stupid..Just added position:fixed; because I then move the dialog out of the document flow.
I'm working on a react component that would render a loading animations.
Here is the js file
import React from 'react'
import './css/CircleSpinner.css'
const CircleSpiner = () => {
return (
<div className="lds-circle" >
</div>
)
}
export default CircleSpiner
The CSS is on a separate file and the component doesn't render the animations as expected. Any help would be appreciated.
Here's the code sandbox for the above.
You need content or draw the circle:
.lds-circle > div {
display: inline-block;
width: 64px;
height: 64px;
margin: 8px;
border-radius: 50%;
border: 1px solid #000;
color: black;
animation: lds-circle 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
You are missing border: 1px solid #000; or content so you can view the circle.
Also define the CircleSpiner and use it on your app.js:
CircleSpiner.js
import React from 'react'
const CircleSpiner = () => {
return (
<div>
</div>
)
}
App.js
export default CircleSpiner
import "./styles.css";
import CircleSpiner from "./CircleSpiner"
export default function App() {
return (
<div className="App">
<div className="lds-circle">
<CircleSpiner></CircleSpiner>
</div>
</div>
);
}
Your CSS does not correspond to your html. Specifically, this rule here:
.lds-circle > div {
display: inline-block;
width: 64px;
height: 64px;
margin: 8px;
border-radius: 50%;
color: black;
animation: lds-circle 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
The .lds-circle > div selector means "any div which is a direct descendent of .lds-circle". You don't have any descendents of your class so it targets nothing. The second issue is that you are providing a color property which styles foreground elements like text, but you (presumably) want a background-color property to see the background of the div element.
So, putting those two together:
.lds-circle > div {
display: inline-block;
width: 64px;
height: 64px;
margin: 8px;
border-radius: 50%;
background-color: black;
animation: lds-circle 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
#keyframes lds-circle {
0%,
100% {
animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5);
}
0% {
transform: rotateY(0deg);
}
50% {
transform: rotateY(1800deg);
animation-timing-function: cubic-bezier(0, 0.5, 0.5, 1);
}
100% {
transform: rotateY(3600deg);
}
}
<div class="lds-circle" >
<div></div>
</div>
This question already has answers here:
CSS animation triggered through JS only plays every other click
(2 answers)
Closed 2 years ago.
How can I re-attach the following animation?
So that every time the text is been clicked, the animation will be activated?
Currently, if I click the text, for the first time the animation will be activated, but no longer when clicking again.
Here is my code:
<!DOCTYPE html>
<html>
<head>
<style>
.shakeit {
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}
#keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}
40%, 60% {
transform: translate3d(4px, 0, 0);
}
}
</style>
</head>
<body>
<h2 id='xxx' class="">JavaScript Numbers</h2>
<script>
const x = document.getElementById('xxx');
x.addEventListener('click', () => {
x.classList.add("shakeit");
});
</script>
</body>
</html>
You can use the onanimationend event to remove the class again after its finished.
<!DOCTYPE html>
<html>
<head>
<style>
.shakeit {
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}
#keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}
40%, 60% {
transform: translate3d(4px, 0, 0);
}
}
</style>
</head>
<body>
<h2 id='xxx' class="">JavaScript Numbers</h2>
<script>
const x = document.getElementById('xxx');
x.addEventListener('click', () => {
x.classList.add("shakeit");
});
x.onanimationstart = function(event) {
console.log("Animation started", event);
}
x.onanimationend = function(event) {
x.classList.remove("shakeit");
};
</script>
</body>
</html>
I'd like to make both .quote-container and #new-quote elements in the same line even if the window width is very small. For example 83pixels. Using min-width on the .quote-container element worked, however, using the same technique on the #new-quote element didn't work.
Maybe that's because #new-quote isn't the children of .quote-container? I even tried to make it a child and it was even worse (picture was taken on the desktop window size):
What I'd like to achieve in visual:
var getNewQuote = function(callback) {
var quote = {};
quote.text = 'Example';
quote.author = 'Example';
$(".loading").hide();
callback(quote);
};
var quoteContainerStartingPadding,
quoteContainerEndingPadding,
newQuoteEndingPadding;
if ($(window).width() > 648) {
quoteContainerStartingPadding = "0 2.5rem";
quoteContainerEndingPadding = "2.5rem";
newQuoteEndingPadding = "2.5rem .75rem";
} else {
quoteContainerStartingPadding = "0 1.5em";
quoteContainerEndingPadding = "1.5rem";
newQuoteEndingPadding = "1.5rem .75rem";
}
$(".quote-container").css("padding", quoteContainerStartingPadding);
getNewQuote(function(quote) {
var getRandomColor = function() {
var colors = ["#ff9966", "#7f00ff", "#396afc", "#0cebeb", "#06beb6", "#642b73", "#36d1dc", "#cb356b", "#3a1c71", "#ef3b36", "#159957", "#000046", "#007991", "#56ccf2", "#f2994a", "#e44d26", "#4ac29a", "#f7971e", "#34e89e", "#6190e8", "#3494e6", "#ee0979"],
randomNumber = Math.floor(Math.random() * colors.length);
return colors[randomNumber];
};
var updateText = function($t) {
var twitter = "https://twitter.com/intent/tweet?hashtags=quotes&related=freecodecamp&text=";
twitter += '"' + quote.text + '" ';
twitter += quote.author;
var tumblr = "https://www.tumblr.com/widgets/share/tool?posttype=quote&tags=quotes,freecodecamp&caption=";
tumblr += quote.author;
tumblr += "&content=";
tumblr += quote.text;
tumblr += "&canonicalUrl=https%3A%2F%2Fwww.tumblr.com%2Fbuttons&shareSource=tumblr_share_button";
var $icon = $("<i class='fa fa-quote-left'>").prop("aria-hidden", true);
$t.find(".quote-text").html("").append($icon, quote.text);
$t.find(".quote-author").html("- " + quote.author);
$("#tweet-quote").attr("href", twitter);
$("#tumblr-quote").attr("href", tumblr);
};
var calcNewHeight = function(q) {
var $temp = $("<div>", {
class: "quote-container temp",
}).appendTo($("body"));
$temp.append($("<div>", {
class: "quote-text"
}), $("<div>", {
class: "quote-author"
}));
updateText($temp, q);
var h = $temp.height() + 40;
$temp.remove();
return h;
};
var changeColor = function(newColor) {
$("body, .button:not(#new-quote)").animate({
backgroundColor: newColor
});
$("#new-quote").animate({
color: newColor
});
$(".quote-text, .quote-author").css("color", newColor);
if ($("#modStyle").length === 0) {
$("head").append("<style id='modStyle'>#new-quote:before {background:" + newColor + ";} .lds-eclipse {box-shadow: 0 .25rem 0 0 " + newColor + ";}</style>");
} else {
$("head style#modStyle").html("#new-quote:before {background:" + newColor + ";} .lds-eclipse {box-shadow: 0 .25rem 0 0 " + newColor + ";}");
}
};
var getQuote = function() {
var nc, nh = 0;
nc = getRandomColor();
nh = calcNewHeight(quote);
changeColor(nc);
$(".quote-container, #new-quote").animate({
height: nh / 16 + "rem",
}, {
duration: 1000,
queue: false
});
$(".quote-container").animate({
padding: quoteContainerEndingPadding
}, {
duration: 1000,
queue: false
});
$("#new-quote").animate({
padding: newQuoteEndingPadding
}, {
duration: 1000,
queue: false
});
updateText($(".quote-container"), quote);
$(".quote-container").children().not($(".loading")).fadeTo(750, 1);
};
$(".quote-container, #new-quote").css({
visibility: "visible",
height: 0
});
$("#new-quote").css("padding", "0 .75rem");
getQuote();
}
);
var two = function() {
$(".quote-container").children().not($(".loading")).hide();
$(".loading").show();
getNewQuote(function(quote) {
var getRandomColor = function() {
var colors = ["#ff9966", "#7f00ff", "#396afc", "#0cebeb", "#06beb6", "#642b73", "#36d1dc", "#cb356b", "#3a1c71", "#ef3b36", "#159957", "#000046", "#007991", "#56ccf2", "#f2994a", "#e44d26", "#4ac29a", "#f7971e", "#34e89e", "#6190e8", "#3494e6", "#ee0979"],
randomNumber = Math.floor(Math.random() * colors.length);
return colors[randomNumber];
};
var updateText = function($t) {
var twitter = "https://twitter.com/intent/tweet?hashtags=quotes&related=freecodecamp&text=";
twitter += '"' + quote.text + '" ';
twitter += quote.author;
var tumblr = "https://www.tumblr.com/widgets/share/tool?posttype=quote&tags=quotes,freecodecamp&caption=";
tumblr += quote.author;
tumblr += "&content=";
tumblr += quote.text;
tumblr += "&canonicalUrl=https%3A%2F%2Fwww.tumblr.com%2Fbuttons&shareSource=tumblr_share_button";
var $icon = $("<i class='fa fa-quote-left'>").prop("aria-hidden", true);
$t.find(".quote-text").html("").append($icon, quote.text);
$t.find(".quote-author").html("- " + quote.author);
$("#tweet-quote").attr("href", twitter);
$("#tumblr-quote").attr("href", tumblr);
};
var calcNewHeight = function(q) {
var $temp = $("<div>", {
class: "quote-container temp",
}).appendTo($("body"));
$temp.append($("<div>", {
class: "quote-text"
}), $("<div>", {
class: "quote-author"
}));
updateText($temp, q);
var h = $temp.height() + 40;
$temp.remove();
return h;
};
var changeColor = function(newColor) {
$("body, .button:not(#new-quote)").animate({
backgroundColor: newColor
});
$("#new-quote").animate({
color: newColor
});
$(".quote-text, .quote-author").css("color", newColor);
if ($("#modStyle").length === 0) {
$("head").append("<style id='modStyle'>#new-quote:before {background:" + newColor + ";} .lds-eclipse {box-shadow: 0 .25rem 0 0 " + newColor + ";}</style>");
} else {
$("head style#modStyle").html("#new-quote:before {background:" + newColor + ";} .lds-eclipse {box-shadow: 0 .25rem 0 0 " + newColor + ";}");
}
};
var getQuote = function() {
var nc = getRandomColor(),
nh = calcNewHeight(quote);
$(".quote-container").children().not($(".loading")).css("opacity", 0);
changeColor(nc);
$(".quote-container, #new-quote").animate({
height: nh / 16 + "rem",
}, {
duration: 1000,
queue: false
});
updateText($(".quote-container"), quote);
$(".quote-container").children().not($(".loading")).fadeTo(750, 1);
};
getQuote();
});
}
;
html,
body {
height: 100%;
width: 100%;
}
body {
margin: 0;
padding: 0;
background: #333;
color: #333;
font-family: sans-serif;
}
.quote-container {
width: 35%;
background: #fff;
margin: 0;
display: inline-block;
vertical-align: middle;
border-radius: 0.1875rem;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
visibility: hidden;
min-width: 15rem;
}
.quote-text {
font-size: 1.625rem;
}
.quote-text i {
margin-right: 0.6rem;
}
.quote-text p {
display: inline;
}
.quote-author {
font-size: 1rem;
margin: 0 0.4rem 2rem 0;
text-align: right;
}
.button {
padding: 0.75rem;
text-align: center;
font-size: 1rem;
color: #fff;
border-radius: 0.1875rem;
display: inline-block;
cursor: pointer;
-webkit-user-select: none;
user-select: none;
}
.button:not(#new-quote):hover {
opacity: 0.8 !important;
}
.button:not(#new-quote) {
min-width: 1rem;
min-height: 1rem;
}
.button i {
vertical-align: middle;
}
#new-quote {
white-space: nowrap;
writing-mode: vertical-lr;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
vertical-align: middle;
background: #fff !important;
margin: 0;
position: relative;
right: 0.25625rem;
color: #333;
visibility: hidden;
}
#new-quote:before {
content: "";
position: absolute;
height: 100%;
width: 0.0625rem;
bottom: 0;
left: 0;
visibility: hidden;
-webkit-transform: scaleY(0);
transform: scaleY(0);
-webkit-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
#new-quote:hover:before {
visibility: visible;
-webkit-transform: scaleY(1);
transform: scaleY(1);
}
.v-align {
position: relative;
top: 50%;
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
.text-center {
text-align: center;
}
footer {
font-size: 0.85rem;
margin-bottom: 1rem;
}
footer a {
text-decoration: none;
color: #fff;
position: relative;
}
footer a:before {
content: "";
position: absolute;
width: 100%;
height: 0.0625rem;
bottom: 0;
left: 0;
background: #fff;
visibility: hidden;
-webkit-transform: scaleX(0);
transform: scaleX(0);
-webkit-transition: all 0.3s ease-in-out 0s;
transition: all 0.3s ease-in-out 0s;
}
footer a:hover:before {
visibility: visible;
-webkit-transform: scaleX(1);
transform: scaleX(1);
}
/* Loading animation */
#keyframes lds-eclipse {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
50% {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
#-webkit-keyframes lds-eclipse {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
50% {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
.loading {
position: relative;
top: 50%;
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
.lds-eclipse {
-webkit-animation: lds-eclipse 1s linear infinite;
animation: lds-eclipse 1s linear infinite;
width: 10rem;
height: 10rem;
border-radius: 50%;
margin: auto;
}
#media (max-width: 62.5em) {
.quote-container {
width: 50%;
}
}
#media (max-width: 50em) {
.quote-container {
width: 65%;
}
}
#media (max-width: 17.96875em) {
.quote-container {
width: 40%;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="v-align text-center">
<div class="quote-container">
<div class="quote-text">
</div>
<div class="quote-author"></div>
<a id="tweet-quote" class="button"><i class="fa fa-twitter"></i></a>
<a id="tumblr-quote" class="button"><i class="fa fa-tumblr"></i></a>
<div class="loading">
<div class="lds-eclipse"></div>
</div>
</div>
<div id="new-quote" class="button">New quote</div>
<footer>
Created by LukasLSC
</footer>
</div>
EDIT 1: Codepen: https://codepen.io/Kestis500/pen/KZvXgB?editors=0110
If you want to align things in CSS you usually have two different positioning concepts you can use for this purpose:
display (flex)
float
Usually it is a good idea to put all elements you want to align in a wrapping container like a div. In this manner you can just focus on your aligning issue and forget about the general layout - means where you want to have your aligned elements in the layout eventually. You can later on just position the wrapper and do not have to worry about the elements inside.
Another best practice is to give all your elements that this container inherits from a dimension (at least width). A common mistake is that elements that should be aligned break just because the parent element does not have enough space to fit all elements on one line. If you want to know why I provide an example at the end, just follow the *.
But lets go back to the two concepts that you can use. Which one you should use depends on one hand what other attributes you need to give the respective elements and what browsers you need to support. If you only want to support newer browser versions you can go with flexbox, the more secure way to do this is use percentages for widths and float.
Flexbox
.container {
display: flex;
flex-direction: row; // this makes your elements align horizontally
}
.child1 {
flex: 1 1 auto;
}
.child2 {
flex: 0 1 auto;
}
The flex attribute determines the dimension of a child. So consider the parent as width: 100%; and the numbers you give as a first parameter to flex is the ratio of the child's dimension compared to the other children.
Float
.container {
overflow: hidden;
width: 100%; // this must be relative to the containers parent of course
}
.child1 {
width: 75%;
float: left;
}
.child2 {
width: 25%;
float: left;
}
Mind that float takes effect on the elements following in the document flow AFTER the element that you give the float attribute. Also take into account that you might need to calculate margins, paddings or borders in additionally to the elements' widths (except for paddings when using box-sizing: border-box) and that elements containing only floated elements lose their "automatic" dimensions as floated elements lose their information about height and width as well. (overflow: hidden on the container solves this issue for you)
*In a responsive design e.g. you should give the highest parent a width of 100%. If you provide to a child width: 50%; it will now have exactly 50% of the entire width. If you now give the child of the child width: 50% it will be 25% of the entire width. This is less error prone then giving the child's child directly 25%. Let's assume later on you give the child a width of 50% the width of the child's child (25%) will relate to the childs width instead of the parent. So you will end up with a width of 12.5% for the child's child relative to the entire width.
I'm trying to set the keyframes of a pulsate animation in ReactJS. I tried just setting the keyframes inside the inline style but that doesn't work.
My code
const NewRelpyheButton = ({style = {}, open, handleOPenSettings}) => {
var bar = {
color: '#000',
padding: '1em 0',
fontSize: '20px',
textAlign: 'center',
cursor: 'pointer',
position: 'fixed',
bottom: '0',
width: '100%',
zIndex: '10',
animation: 'pulse 1.2s ease-in-out',
animationIterationCount: 'infinite',
}
Object.assign(style, {});
let openModal;
if (open) {
openModal = <Modal><NewRelpyhe/></Modal>
}
return (
<div>
{openModal}
<Bar color='purple' style={bar} onClick={handleOpenSettings}>
create a new relphye site
</Bar></div>
)
}
I'm trying to mimic this in css:
.element {
width: 100%;
height: 100%;
animation: pulse 5s infinite;
}
#keyframes pulse {
0% {
background-color: #001F3F;
}
100% {
background-color: #FF4136;
}
}
html,
body {
height: 100%;
}
If you like to keep all your styling tightly coupled to your components, give Styled Components a go. They have a helper for keyframes
e.g.
import styled, { keyframes } from 'styled-components'
const pulse = keyframes`
from {
background-color: #001F3F;
}
to {
background-color: #FF4136;
}
`
const Bar = styled.div`
color: #000;
padding: 1em 0;
font-size: 20px,
text-align: center;
cursor: pointer;
position: fixed;
bottom: '0',
width: 100%;
z-index: 10;
animation: ${pulse} 1.2s ease-in-out;
animation-iteration-count: infinite;
`
Then use like so:
<Bar>I pulse</Bar>
Here's how we will achieve it without adding any dependency.
{/*Your Js File Code */}
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import React from "react";
import "./test.css";
class Anim extends React.Component {
constructor(props) {
super(props);
this.state = {
animationName: ""
};
}
addStylesheetRules(rules) {
var styleEl = document.createElement("style");
document.head.appendChild(styleEl);
var styleSheet = styleEl.sheet;
styleSheet.insertRule(rules, 0);
}
clickHdl() {
let animationName = `animation${Math.round(Math.random() * 100)}`;
let keyframes = `
#-webkit-keyframes ${animationName} {
10% {-webkit-transform:translate(${Math.random() * 300}px, ${
Math.random() * 300
}px)}
90% {-webkit-transform:translate(${Math.random() * 300}px, ${
Math.random() * 300
}px)}
100% {-webkit-transform:translate(${Math.random() * 300}px, ${
Math.random() * 300
}px)}
}`;
this.addStylesheetRules(keyframes);
this.setState({
animationName: animationName
});
}
render() {
let style = {
animationName: this.state.animationName,
animationTimingFunction: "ease-in-out",
animationDuration: "0.6s",
animationDelay: "0.0s",
animationIterationCount: 1,
animationDirection: "normal",
animationFillMode: "forwards"
};
return (
<div>
<button type="button" onClick={this.clickHdl.bind(this)}>
Animation!
</button>
<div className="box" style={style}></div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<Anim />
</StrictMode>,
rootElement
);
{/*Css Code test.css */}
.box {
width: 30px;
height: 30px;
background: red;
border-radius: 50%;
cursor: pointer;
}
Demo: https://codesandbox.io/s/reverent-sun-qjo91?file=/src/index.js