在获取数据时显示 CSS 加载指示器
我有一个不确定的线性 css 加载指示器,就像材料设计中的那样.每次我从 Redux 的 reducer 获取数据时,我都会显示这个指标.问题是,当指标出现时,它并没有被动画化.看起来整个页面都冻结了,直到 reducer 填充了数据.
假设我的加载指示器始终可见且具有动画效果.一旦我将数据提取/请求到减速器中,加载指示器就会停止动画.
您认为这是什么原因造成的?它是可修复的还是我需要实现一个 gif 指示器而不是一个 css 指示器?
编辑 1
这是我的 Loader
组件:
import React, { Component, PropTypes } from 'react';从镭"进口镭;//不确定线性(类型:indeterminateLinear)const indeterminateLinearLong = radium.keyframes({0%":{左:'-35%',对:'100%'},60%":{左:'100%',对:'-90%'},100%":{左:'100%',对:'-90%'}});const indeterminateLinearShort = radium.keyframes({0%":{左:'-200%',对:'100%'},60%":{左:'107%',对:'-8%'},100%":{左:'107%',对:'-8%'}});常量不确定线性 = {根据: {背景颜色:'#26a69a',},进步: {位置:'相对',高度:'4px',显示:'块',宽度:'100%',背景颜色:'#acece6',//边框半径:'2px',backgroundClip: '填充框',//边距:'0.5rem 0 1rem 0',溢出:'隐藏',z索引:'999'},前: {位置:'绝对',背景颜色:'黄色',顶部:'0',左:'0',底部:'0',willChange: '左,右',动画:'不确定的 2.1s 三次贝塞尔曲线(0.65,0.815,0.735,0.395)无限',动画名称:indeterminateLinearLong},后: {位置:'绝对',背景颜色:'黄色',顶部:'0',左:'0',底部:'0',willChange: '左,右',动画:'不确定短 2.1s 三次贝塞尔曲线(0.165, 0.84, 0.44, 1)无限',动画名称:indeterminateLinearShort,动画延迟:'1.15s'}};//Indeterminate Circular(类型:indeterminateCircular(默认))const indeterminateCircularRotate = radium.keyframes({100%":{变换:'旋转(360度)'},});const indeterminateCircularColor = radium.keyframes({'100%, 0%': {中风:'红色'},40%":{中风:蓝色"},60%":{中风:'绿色'},'80%, 90%': {中风:'黄色'}});const indeterminateCircularDash = radium.keyframes({0%":{strokeDasharray: '1,200',strokeDashoffset: '0'},'50%': {strokeDasharray: '89,200',strokeDashoffset: '-35'},100%":{strokeDasharray: '89,200',strokeDashoffset: '-124'},});常量不确定循环 = {装载机:{位置:'绝对',宽度:'100px',高度:'100px',左:'50%',顶部:'20%'},循环:{动画:'x 2s 线性无限',动画名称:不确定圆形旋转,高度:'24px',位置:'相对',宽度:'24px'},小路: {strokeDasharray: '1,200',strokeDashoffset: '0',动画:'破折号 1.5s 缓入出无限,颜色 6s 缓入出无限',动画名称:indeterminateCircularDash,strokeLinecap: '圆形',中风:'红色',行程宽度:'2'}}类加载器扩展组件 {使成为() {常量 {类型} = 这个.props;让装载机=空;如果(类型==='indeterminateLinear'){装载机 = (<div style={不确定线性进度}><div style={indeterminateLinear.before }></div><div style={ indeterminateLinear.base }></div><div style={indeterminateLinear.after }></div></div>);} 别的 {装载机 = (<div style={ 不确定循环加载器 }><svg style={ indeterminateCircular.circular } viewBox="25 25 50 50"><圆形样式={不确定循环路径} cx="50" cy="50" r="20" fill="none" strokeMiterlimit="10"/></svg></div></div>);}返回 (<div>{加载器}</div>);}}Loader.propTypes = {类型:PropTypes.string,};导出默认镭(Loader);
当我包含 Loader 组件时,它可以正常工作和动画;但是,当我请求获取一些数据时,动画会停止.我还可以注意到,在 React 重新渲染并显示获取的数据之前,其他所有内容都冻结了.
此 Loader 组件包含在连接到 reducer 的顶级组件中,当获取数据时,该组件正在显示.
{ this.props.globalState.someReducer.isFetching ?<主题渲染="加载器" type="indeterminateLinear"/>: 空值}
编辑 2
我想添加另一个用例.这个时间和redux无关,所以我们可以把它从图中去掉.它必须与自身反应.
我有一个页面列出了一些项目,大约.40 项,我有一个按日期对列表进行排序的按钮.
这是我的渲染函数中的内容:
const sortDesc = (a, b) =>(交易[b].updatedAt ||交易[b].createdAt) -(交易[a].updatedAt ||交易[a].createdAt)const sortAsc = (a, b) =>(交易[a].updatedAt ||交易[a].createdAt) -(交易[b].updatedAt ||交易[b].创建时间)让 sortDate = sortDesc;如果(!this.state.sortDesc){sortDate = sortAsc}<button onClick={ this.handleSortBy }>按日期排序</button>{Object.keys(交易).种类(排序日期).map(( 键,索引 )=> {返回 (<div键={索引}><支付 {...this.props}/></div>);})}
我的 Loader
组件位于父组件中.结构可能是这样的:
-- 布局 <-- 这里有加载器
---- Transactions <-- 这里是排序函数
-- 支付 <-- 子组件
现在,每次我点击排序按钮时,一切都会冻结,直到排序完成.当我说一切"时,它是指所有组件,包括父组件(在本例中为 Layout
和 Loader
).
Loader
是用 Radium 构建的.
我必须在这里遗漏一些东西,我不敢相信当子组件重新渲染时所有组件都会冻结.
解决方案我终于解决了.经过一些研究,我了解了很多关于网络工作者的知识,只是发现这不是正确的解决方案.无论如何,它必须与 javascript 的工作方式有关.Javascript是单线程的,因此是阻塞的.所以当我们有一些需要一些时间的任务时(在我的情况下是一秒钟左右)我们在屏幕上看到的所有东西都会冻结,甚至是 css 动画!这取决于您使用的浏览器.就我而言,我使用的是 Chrome.我用firefox测试了一下,动画没有停止.
让我们找到解决方案.我注意到 Chrome 在过渡方面存在一些问题,特别是一些基本的 CSS 元素,例如顶部、右侧、底部、左侧、边距……所以我不得不使用转换.并非所有转换都可以工作.例如,翻译百分比不起作用,所以我不得不使用像素.为此,我需要计算父元素的宽度并进行一些计算,以便能够将我的子元素在 X 轴上从屏幕的左侧平移到右侧.
我会用实际代码更新这篇文章.
I have a indeterminate linear css loading indicator like the ones in material design. Every time I fetch data from a Redux's reducer, I show this indicator. The problem is that while the indicator shows up, it is not being animated. It looks like the whole pages freezes up until the reducer is populated with the data.
Let's suppose I have my loading indicator visible and animated at all times. As soon as I fetch/request data into a reducer, the loading indicator stops animating.
What do you think is causing this? Is it fixable or Do I need to implement a gif indicator and not a css one?
EDIT 1
This is the my Loader
component:
import React, { Component, PropTypes } from 'react';
import radium from 'radium';
// indeterminate Linear (type: indeterminateLinear)
const indeterminateLinearLong = radium.keyframes({
'0%': {
left: '-35%',
right: '100%'
},
'60%': {
left: '100%',
right: '-90%'
},
'100%': {
left: '100%',
right: '-90%'
}
});
const indeterminateLinearShort = radium.keyframes({
'0%': {
left: '-200%',
right: '100%'
},
'60%': {
left: '107%',
right: '-8%'
},
'100%': {
left: '107%',
right: '-8%'
}
});
const indeterminateLinear = {
base: {
backgroundColor: '#26a69a',
},
progress: {
position: 'relative',
height: '4px',
display: 'block',
width: '100%',
backgroundColor: '#acece6',
// borderRadius: '2px',
backgroundClip: 'padding-box',
// margin: '0.5rem 0 1rem 0',
overflow: 'hidden',
zIndex: '999'
},
before: {
position: 'absolute',
backgroundColor: 'yellow',
top: '0',
left: '0',
bottom: '0',
willChange: 'left, right',
animation: 'indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite',
animationName: indeterminateLinearLong
},
after: {
position: 'absolute',
backgroundColor: 'yellow',
top: '0',
left: '0',
bottom: '0',
willChange: 'left, right',
animation: 'indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite',
animationName: indeterminateLinearShort,
animationDelay: '1.15s'
}
};
// Indeterminate Cirular (type: indeterminateCircular (default))
const indeterminateCircularRotate = radium.keyframes({
'100%': {
transform: 'rotate(360deg)'
},
});
const indeterminateCircularColor = radium.keyframes({
'100%, 0%': {
stroke: 'red'
},
'40%': {
stroke: 'blue'
},
'60%': {
stroke: 'green'
},
'80%, 90%': {
stroke: 'yellow'
}
});
const indeterminateCircularDash = radium.keyframes({
'0%': {
strokeDasharray: '1,200',
strokeDashoffset: '0'
},
'50%': {
strokeDasharray: '89,200',
strokeDashoffset: '-35'
},
'100%': {
strokeDasharray: '89,200',
strokeDashoffset: '-124'
},
});
const indeterminateCircular = {
loader: {
position: 'absolute',
width: '100px',
height: '100px',
left: '50%',
top: '20%'
},
circular: {
animation: 'x 2s linear infinite',
animationName: indeterminateCircularRotate,
height: '24px',
position: 'relative',
width: '24px'
},
path: {
strokeDasharray: '1,200',
strokeDashoffset: '0',
animation: 'dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite',
animationName: indeterminateCircularDash,
strokeLinecap: 'round',
stroke: 'red',
strokeWidth: '2'
}
}
class Loader extends Component {
render() {
const {
type
} = this.props;
let loader = null;
if ( type === 'indeterminateLinear' ) {
loader = (
<div style={ indeterminateLinear.progress }>
<div style={ indeterminateLinear.before }></div>
<div style={ indeterminateLinear.base }></div>
<div style={ indeterminateLinear.after }></div>
</div>
);
} else {
loader = (
<div>
<div style={ indeterminateCircular.loader }>
<svg style={ indeterminateCircular.circular } viewBox="25 25 50 50">
<circle style={
indeterminateCircular.path
} cx="50" cy="50" r="20" fill="none" strokeMiterlimit="10"/>
</svg>
</div>
</div>
);
}
return (
<div>{ loader }</div>
);
}
}
Loader.propTypes = {
type: PropTypes.string,
};
export default radium(Loader);
When I include the Loader component, it works and animates without a problem; however, when I request some data to be fetched, the animation stops. I can also notice that everything else frozes up too until React rerenders and shows the fetched data.
This Loader component is being included in an top level component that is connected to the reducers, when data is being fetched, the component is being shown.
{ this.props.globalState.someReducer.isFetching ?
<Theme render="Loader" type="indeterminateLinear" /> : null
}
EDIT 2
I want to add another use case. This time has nothing to do with redux, so we can remove it from the picture. It has to be with react itself.
I have a page listing some items, approx. 40 items, and I have a button that sorts this list by date.
This is what I have in my render function:
const sortDesc = ( a, b ) => (
transactions[b].updatedAt ||
transactions[b].createdAt) -
(transactions[a].updatedAt ||
transactions[a].createdAt
)
const sortAsc = ( a, b ) => (
transactions[a].updatedAt ||
transactions[a].createdAt) -
(transactions[b].updatedAt ||
transactions[b]. createdAt
)
let sortDate = sortDesc;
if (!this.state.sortDesc) {
sortDate = sortAsc
}
<button onClick={ this.handleSortBy }>sort by date</button>
{
Object.keys(transactions)
.sort(
sortDate
).map(( key, index )=> {
return (
<div key={ index }>
<Payout {...this.props} />
</div>
);
})
}
I have my Loader
component inside a parent component. The structure could be something like this:
-- Layout <-- here we have the Loader
---- Transactions <-- here is the sort function
------ Payout <-- child component
Now, everytime I hit the sort button, everything freezes up until the sorting is finished. When I say "everything" it means all components, including parent components (in this case Layout
and Loader
).
The Loader
is built with Radium.
I must be missing something here, I cannot believe all the components will freeze up when a child componen is re-rendering.
解决方案I finally solved it. After some research, I learned a lot about web workers just to find out it was not the right solution. Anyways, it has to do on how javascript works. Javascript is single threaded and thus blocking. So when we have some task that takes some time (in my case was a second or so) everything we seen on screen will freeze, even css animations! It does depend on what browser you are using. In my case, i was using Chrome. I tested it firefox and the animations did not stop.
Let's go to the solution. I noticed that Chrome has some issues with transitions, specially with some basic css elements such as top, right, bottom, left, margins... so i had to go with transforms. Not all transforms will work either. For example, translate percentages won't work, so I had to use pixels. For this, i needed to calculate the width of the parent element and do some calculations to be able to translate my child element on the X axis from the left to the right of the screen.
I will update this post with the actual code.
相关文章