Closed jaydenseric closed 7 years ago
Interesting. I'm not sure it's possible to pass the theme
to keyframes
, as it's propagated through React context to components. (which keyframes
isn't)
Maybe @geelen has an idea how we could achieve this?
If you change a little implementation detail this should work.
// Contrived blinking error message example
export default styled.p`
animation: ${animation} 0.5s infinite;
`
This doesn't work I don't think @k15a, though we could make that work just like css
maybe?
Why not? animation is a reference to a function and functions get called with props as the first argument?
Do I miss something?
Example: http://www.webpackbin.com/4ydwqilDG
Oh yeah, that's true!
What I was talking about is that we should pass props
to functions in keyframes
too, just like with css
:
const rotate = keyframes`
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(${props => props.rotation || '360deg'});
}
`
const RotatingBox = styled.div`
animation: ${rotate} 10s linear infinite;
`
// Usage:
<RotatingBox rotation="180deg" />
Yeah that would be a bit more convenient.
I think we should support this for consistency.
I think I'm running into this as well trying to port one of my CSS loaders to use styled-components. I tried using @k15a's workaround and making my dash
keyframes a function like so:
function dash(props) {
return keyframes`
0% {
stroke-dasharray: ${getCircumference(props.radius)};
stroke-dashoffset: ${getCircumference(props.radius) * 0.97};
transform: rotate(-130deg);
}
100% {
stroke-dasharray: ${getCircumference(props.radius)};
stroke-dashoffset: ${getCircumference(props.radius) * 0.5};
transform: rotate(-110deg);
}
`;
}
but it doesn't seem to have any affect.
Hmm, I kinda like the fact that keyframes
just returns a string. It seems weird if props
gets magically passed to it, when using it inside a function is so easy: http://www.webpackbin.com/V15uwsQwG
This is more consistent because keyframes
, like injectGlobal
has one and only one immediate side-effect—to inject some CSS into the page. styled.div
returns a component that has a complex life cycle, including rerendering, so I don't really want to blur the lines between component-oriented and global CSS injection methods.
@geelen In my case I tried to use the function workaround for my keyframes but it didn't work - see this example.
This might be because I'm also deriving some of the other animation props (ex. duration
) so animation
is using a function as well.:
const Spinner = styled.circle`
stroke: ${props => props.color};
stroke-width: ${props => props.width};
stroke-linecap: round;
fill: none;
transform-origin: 50%;
animation: ${props => `
${dash} ${props.duration} ease-in-out infinite alternate,
${rotate} ${props.duration} linear infinite
`}
`;
As I wrote this I realized "maybe I need to pass the props explicitly to dash
now that it is within a function / template literal" like so...
const Spinner = styled.circle`
stroke: ${props => props.color};
stroke-width: ${props => props.width};
stroke-linecap: round;
fill: none;
transform-origin: 50%;
animation: ${props => `
${dash(props)} ${props.duration} ease-in-out infinite alternate,
${rotate} ${props.duration} linear infinite
`}
`;
and it worked.
I guess (at least for me) it was unclear when props are passed to a function and when they not.
Passed "automatically"
function animation (props) {
return keyframes`
to {
color: ${props.error};
}
`
}
const Text = styled.p`
color: #222;
font-size: 1.5em;
font-family: Helvetica, Arial, sans-serif;
text-align: center;
animation: ${animation} 0.5s infinite;
`;
Must be passed explicitly
function animation (props) {
return keyframes`
to {
color: ${props.error};
}
`
}
const Text = styled.p`
color: #222;
font-size: 1.5em;
font-family: Helvetica, Arial, sans-serif;
text-align: center;
animation: ${props => `${animation(props)} 0.5s infinite`};
`;
I see arrow functions work as well, which basically means you just need to inject (props) =>
between your variable name and keyframes
BEFORE
const RADIUS = 50;
const dash = keyframes`
0% {
stroke-dasharray: ${getCircumference(RADIUS)};
stroke-dashoffset: ${getCircumference(RADIUS) * 0.97};
transform: rotate(-130deg);
}
100% {
stroke-dasharray: ${getCircumference(RADIUS)};
stroke-dashoffset: ${getCircumference(RADIUS) * 0.5};
transform: rotate(-110deg);
}
`;
AFTER
const dash = (props) => keyframes`
0% {
stroke-dasharray: ${getCircumference(props.radius)};
stroke-dashoffset: ${getCircumference(props.radius) * 0.97};
transform: rotate(-130deg);
}
100% {
stroke-dasharray: ${getCircumference(RADIUS)};
stroke-dashoffset: ${getCircumference(RADIUS) * 0.5};
transform: rotate(-110deg);
}
`;
Yeah exactly. This is one of the confusing things about tagged template literals. In your previous example:
const Spinner = styled.circle`
...
animation: ${props => `
${dash} ${props.duration} ease-in-out infinite alternate,
${rotate} ${props.duration} linear infinite
`}
`;
You're actually using a normal (untagged) template literal. If you used css
, it'd work.
const Spinner = styled.circle`
...
animation: ${props => css`
${dash} ${props.duration} ease-in-out infinite alternate,
${rotate} ${props.duration} linear infinite
`}
`;
If you leave it off it's effectively calling toString
on everything you pass in, including dash
which will return function dash() { keyframes(" ... ", ...) }
or whatever babel compiled it to. Using css
stops those functions from being toString
-ed, and instead calls them with props
at render time. See the docs here: https://github.com/styled-components/styled-components/blob/master/docs/tagged-template-literals.md
Awe, thanks for the clarification. I remember reading about that early on, but until I encountered it, it didn't really sink in.
On Thu, Jan 26, 2017, 11:54 PM Glen Maddern notifications@github.com wrote:
Yeah exactly. This is one of the confusing things about tagged template literals. In your previous example:
const Spinner = styled.circle
... animation: ${props =>
${dash} ${props.duration} ease-in-out infinite alternate, ${rotate} ${props.duration} linear infinite}
;You're actually using a normal (untagged) template literal. If you used css, it'd work.
const Spinner = styled.circle
... animation: ${props => css
${dash} ${props.duration} ease-in-out infinite alternate, ${rotate} ${props.duration} linear infinite}
;If you leave it off it's effectively calling toString on everything you pass in, including dash which will return function dash() { keyframes(" ... ", ...) } or whatever babel compiled it to. Using css stops those functions from being toString-ed, and instead calls them with props at render time. See the docs here: https://github.com/styled-components/styled-components/blob/master/docs/tagged-template-literals.md
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/styled-components/styled-components/issues/397#issuecomment-275588876, or mute the thread https://github.com/notifications/unsubscribe-auth/AAK1RMr8M_pp2ncYkqPIN715lWGIwEw8ks5rWXiFgaJpZM4LqURs .
`import styled, { keyframes } from 'styled-components';
const move = keyframes 0% {top: 0px; left: 0px; background: ${(props) => props.color};} 25% {top: 0px; left: 100px; background: blue;}
;
const AnimationDiv = styled.div width: 100px; height: 100px; border: 1px solid ${(props) => props.color}; &:hover { animation: ${move} 5s infinite; }
;
export default AnimationDiv;` I'm using this, but the coverage can't pass through the line ${(props) => props.color} in keyframes Anyone can help?
@quanganh1202 first of all, it's always better to turn to spectrum, stackoverflow, or a new issue, instead of commenting on an old and closed one :wink:
keyframes are created separately since they're a global. We generate a hash for their name and return it as a result of calling the tag. This means that they cannot be used like an interpolation, since they just return a static name.
If you write p => keyframes
as an interpolation instead and use it as a factory, it will work.
It's even mentioned on a comment before: https://github.com/styled-components/styled-components/issues/397#issuecomment-275581243
It was really hard to work out a way to access props in animation keyframes to make animations themeable. Eventually I came across this comment, which lead me to this approach:
There must be a more elegant way. Whatever it is, lets add it to the keyframes and/or theming docs.