function Loader() {
var style = {
border: '16px solid #eee',
borderTop: '16px solid #3ae',
borderRadius: '50%',
width: '1cm',
height: '1cm',
animation: 'spin 2s linear infinite',
}
return (
<div style={style}>
<style>{`
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`}</style>
</div>
)
}
Is there a way to specify @keyframes
locally to the component (a component function in this example) without using an inlined string?
Also if you specify <style>
inside the component render function does it have a performance penalty if there are multiple objects of the same class (or using the same @keyframes
)?
The idea is to keep stuff inside <style>
locally inside the component, I do not want to move it to the .css
file, but at the same time I do not want hundreds of repeated class/keyframes definitions when only one is actually sufficient.
This is not possible without writing a helper function or using some standard library that can inject keyframes, as browser options for this are still experimental and not widely supported, such as animate function.
https://developer.mozilla.org/en-US/docs/Web/API/Element/animate
When importing them from another file, such as css, or a JS file by using CSS modules, Webpack usually does the injection heavy lifting for you.
I would suggest that you either DO import the CSS file, or to check out styled components helper function for this purpose.
import React from "react";
import ReactDOM from "react-dom";
import { keyframes } from "styled-components";
function Loader() {
var spin = keyframes`
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
`;
var styles = {
border: "16px solid #eee",
borderTop: "16px solid #3ae",
borderRadius: "50%",
width: "1cm",
height: "1cm",
animation: `${spin} 2s linear infinite`
};
return <div style={styles} />;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Loader />, rootElement);
You can do it basically the same way you already are, just declare a Keyframes
component to neaten up your client code. Some usage:
<Keyframes name="oscillate" from={{ opacity: 0.9 }} to={{ opacity: 0.2 }} />
or
<Keyframes name="oscillate" _0={{ opacity: 0.9 }} _100={{ opacity: 0.2 }} />
You need the underscore or some other letter in front of the percentage values but it's otherwise the same. The component cycles through all its props outputting the formatted css. Using jsx, React has a special CSS object type which needs to be converted to string, but otherwise it's just a component.
import * as React from "react";
interface IProps {
name: string;
[key: string]: React.CSSProperties | string;
}
export const Keyframes = (props: IProps) => {
const toCss = (cssObject: React.CSSProperties | string) =>
typeof cssObject === "string"
? cssObject
: Object.keys(cssObject).reduce((accumulator, key) => {
const cssKey = key.replace(/[A-Z]/g, v => `-${v.toLowerCase()}`);
const cssValue = (cssObject as any)[key].toString().replace("'", "");
return `${accumulator}${cssKey}:${cssValue};`;
}, "");
return (
<style>
{`@keyframes ${props.name} {
${Object.keys(props)
.map(key => {
return ["from", "to"].includes(key)
? `${key} { ${toCss(props[key])} }`
: /^_[0-9]+$/.test(key)
? `${key.replace("_", "")}% { ${toCss(props[key])} }`
: "";
})
.join(" ")}
}`}
</style>
);
};
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With