echarts轴最大数分割算法

  |  
 阅读次数

最大值 以及 分割数 计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// echarts 支持callback函数
max: ({ max, min }) => {
/** 判断最大值的长度 减一是因为 两位数只用 除10 即可,即保留十位上的为整数,以此类推 */
let maxLen = max.toString().length - 1;

/** 取平方 */
let pow = Math.pow(10, maxLen)
let pow2 = Number(`10e${max.toString().length - 1}`)

// 取整
let ceilMax = Math.ceil(max / pow) * pow;
this.state.max = ceilMax;
this.state.min = min;
return ceilMax
},

// 不支持 callback,可以 通过 state缓存 max 函数 中最大值计算
interval: (() => this.state.max / 5)(),

前端文件上传

  |  
 阅读次数

前端 文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const { fileList } = this.state; // 取得文件列表
const formData = new FormData(); // 新增实例
fileList.forEach((file) => {
formData.append('file', file); // 遍历添加数据,append 第一个参数 为 `name` 属性
});

axios({
url: `${realAddress}/yuhuaqu/${list}/import`,
method: 'post',
processData: false,
headers: {
'Authorization': SessionStorage.get('token')
},
data: formData,
}).then((res) => { // do something...});

react-无缝滚动动画

  |  
 阅读次数

无缝滚动

第一步 定义 Dom结构

  • 第一层div最外侧 负责 1️⃣鼠标 交互,2️⃣超出隐藏
  • 第二层div负责整体动画,通过marginTop属性实现动画效果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <div
    className="contetn"
    ref="content" // react中 通过 this.refs.content 获取 Dom元素
    onMouseEnter={this.removeAnimationInterval} // 鼠标移入 清除定时器
    onMouseLeave={this.animationInterval} // 鼠标移出 设置定时器
    style={{overflow: "hidden"}} // 超出隐藏 多余内容
    // onWheel={} // 鼠标滚轴 事件
    >
    <div style={{ marginTop: scrollData + "px" }}> // 通过`marginTop`属性实现动画效果
    {
    dutyList.map((item, index) => {
    return (
    <div className="card" key={`${item.title}:${item.name}${index}`}>
    内部元素 非 动画效果 关键,可自定义内容
    </div>
    )
    })
    }
    </div>
    </div>

第二步 获取数据 利用周期执行动画

在请求数据的回调函数 中 设置state, 利用回调 执行 动画方法

1
2
3
4
5
6
7
8
9
10
getOnDutyListData() {
axios({
method: 'GET',
url: `/OnDutyList`,
}).then(res => {
this.setState({
dutyList: res.data.result
}, () => this.animationInterval()) // 在这里,利用 回调 执行 动画方法。可以避免 在其他 周期里面获取不到 异步数据的情况
})
}

第三步 动画实现效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
animationInterval() {
/** 获取滚动系数, 数据矩阵 */
let { scrollData, dutyList } = this.state;
/** 非空判断 */
if (!dutyList.length) return;

/** 设置定时器,挂载到 当前类下的 state */
this.state.animationInterval = setInterval(() => {
/** setState触发render,实现实时动画效果 */
this.setState({
scrollData: scrollData--
});

/** 初始化判断 */
if (this.state.inital && scrollData === -1) {
dutyList.push(dutyList[0]); // 第一次多加载 一个 数据第一项,实现首尾无缝滚动
this.setState({
inital: false // 关闭 初始化
})
}

/** 判断 滚动位置 128为当前效果 中 itemCard 高度, 一但card全部高度 超出视图 执行内部语句 */
if (scrollData % 128 === 0) {
dutyList.shift(); // 删除第一项
dutyList.push(dutyList[0]); // 把当前 数据 第一项,加入 矩阵尾部
scrollData = 0; // 并把 高度 重置
}
// if (scrollData > -113) {
// let firstItem = dutyList[0];
// // dutyList.shift();
// // dutyList.push(firstItem);
// this.setState({
// scrollData: scrollData--
// })
// } else {
// scrollData = 0;
// let firstItem = dutyList[0];
// dutyList.shift();
// dutyList.push(firstItem);
// }
}, 50)
}

操作重点

  • 数组操作: 1️⃣shift(), 2️⃣push(dutyList[0])
  • react: setState

第四步 鼠标移入移出/ 组件卸载 清除定时器

1
2
3
4
5
6
7
removeAnimationInterval() {
this.state.animationInterval && clearInterval(this.state.animationInterval);
}

componenWillUnmount() {
this.removeAnimationInterval();
}

附录 完整代码

CarouselList.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
export default class CarouselList extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
scrollData: 0
};
}

componentDidMount = () => {
this.addHandleTimer();
};

componentWillUnmount = () => {
this.removeHandleTimer();
}
//timers
addHandleTimer = () => {
let { data, scrollData } = this.state;
if (!data || !data.length) return null;
// 定时器
this.timer = setInterval(() => {
if (scrollData > -27) {
scrollData--;
this.setState({
scrollData: scrollData
})
} else {
scrollData = 0;
let firstData = data[0];
data.shift();
data.push(firstData);
}
}, 70);
}

removeHandleTimer = () => {
this.timer && clearInterval(this.timer);
}

handleScroll = e => {
const ev = e || window.event;
e.preventDefault();
const conDom = this.refs["content"];
const speed = 5; // scroll speed
if (ev.deltaY > 0) {
conDom.scrollTop += speed;
} else {
conDom.scrollTop -= speed;
}
};

render() {
const { data, scrollData } = this.state;
return (
<div
ref="content"
onMouseEnter={this.removeHandleTimer}
onMouseLeave={this.addHandleTimer}
onWheel={this.handleScroll}
>
<ul style={{ marginTop: scrollData + "px" }}>
{
data.length > 0 && data.map((item, index) => {
return (
this.props.type === 'simple' ?
<li key={index}><p title={item.value}>{item.value}</p></li>
: <li key={index}><span>{item.id}</span><p title={item.intelligence}>{item.intelligence}</p></li>
)
// 默认:complex
{/*<li key={index}><span>{item.id}</span><p title={item.intelligence}>{item.intelligence}</p></li>*/ }
}
)
}
</ul>
</div>
);
}
}

CarouselList2.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
export default class CarouselList2 extends Component {
constructor(props) {
super(props);
console.log('是大法官',this.props.data)
this.state = {
data: this.props.data,
scrollData: 0
};
}

componentDidMount = () => {
this.addHandleTimer();
};

componentWillUnmount = () => {
this.removeHandleTimer();
};
//timers
addHandleTimer = () => {

let {data, scrollData} = this.state;
if (!data || !data.length) return null;
if(data.length < 5 ) return false;
this.timer = setInterval(() => {
if (scrollData > -52) {
scrollData--;
this.setState({
scrollData: scrollData
})
} else {
scrollData = 0;
let firstData = data[0];
data.shift();
data.push(firstData);

let firstData1 = data[0];
data.shift();
data.push(firstData1);
}
}, 70);
}

removeHandleTimer = () => {
this.timer && clearInterval(this.timer);
}

handleScroll = e => {
const ev = e || window.event;
e.preventDefault();
const conDom = this.refs["content"];
const speed = 6; // scroll speed
if (ev.deltaY > 0) {
conDom.scrollTop += speed;
} else {
conDom.scrollTop -= speed;
}
};

render() {
const {data, scrollData} = this.state;
return (
<div
ref="content"
onMouseEnter={this.removeHandleTimer}
onMouseLeave={this.addHandleTimer}
onWheel={this.handleScroll}
>
<ul style={{marginTop: scrollData + "px"}}>
{
data.length > 0 && data.map((item, index) => {
return (
this.props.type === 'superior' ?
<li key={item.id}>
<p title={item.name}>{item.name}</p>
<p title={item.card}>{item.card}</p>
<p title={item.show}>{item.show}</p>
<p title={item.feedback}>{item.feedback}</p>
<p title={item.state}>{item.state}</p>
</li>
: <li key={item.id}>
<p title={item.name}>{item.name}</p>
<p title={item.card}>{item.card}</p>
<p title={item.time}>{item.time}</p>
<p title={item.origin}>{item.origin}</p>
<p title={item.destination}>{item.destination}</p>
</li>
)
}
)
}
</ul>
</div>
);
}
}

InformationOnDuty.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import "./InformationOnDuty.scss"
import { hidden } from "ansi-colors";

class InformationOnDuty extends Component {
constructor(props) {
super(props);
this.state = {
dutyList: [],
scrollData: 0,
inital: true
};
this.getOnDutyListData = this.getOnDutyListData.bind(this);
this.animationInterval = this.animationInterval.bind(this);
this.removeAnimationInterval = this.removeAnimationInterval.bind(this);
this.getOnDutyListData();
}

getOnDutyListData() {
axios({
method: 'GET',
url: `/OnDutyList`,
}).then(res => {
this.setState({
dutyList: res.data.result
}, () => this.animationInterval())
})
}

animationInterval() {
let { scrollData, dutyList } = this.state;
if (!dutyList.length) return;
this.state.animationInterval = setInterval(() => {
this.setState({
scrollData: scrollData--
});

let firstItem = dutyList[1];

if (scrollData < 0 && scrollData > -2) {
if (this.state.inital) {
dutyList.push(dutyList[0]);
this.setState({
inital: false
})
}
}

// dutyList.push(firstItem);
if (scrollData % 128 === 0) {
// debugger
dutyList.shift();
dutyList.push(firstItem);
scrollData = 0;
// debugger
}
// if (scrollData > -113) {
// let firstItem = dutyList[0];
// // dutyList.shift();
// // dutyList.push(firstItem);
// this.setState({
// scrollData: scrollData--
// })
// } else {
// scrollData = 0;
// let firstItem = dutyList[0];
// dutyList.shift();
// dutyList.push(firstItem);
// }
}, 60)

}

removeAnimationInterval() {
this.state.animationInterval && clearInterval(this.state.animationInterval);
}

componentDidMount() {
}

componentWillReceiveProps() {
debugger

}

componenWillUnmount() {
this.removeAnimationInterval();
}

render() {
let { dutyList, scrollData } = this.state;
return (
<div className="InformationOnDuty">
<div className="title">
<div className="tit1"><span>值班信息</span></div>
<div className="tit2"><span>查看值班表</span></div>
</div>
<div
className="contetn"
ref="content"
onMouseEnter={this.removeAnimationInterval}
onMouseLeave={this.animationInterval}
style={{overflow: "hidden"}}
// onWheel={}
>
<div style={{ marginTop: scrollData + "px" }}>
{
dutyList.map((item, index) => {
return (
<div className="card" key={`${item.title}:${item.name}${index}`}>
<div className="row1">
<span>{item.title}&nbsp;</span><b>&nbsp;|&nbsp;</b><span>{item.name}</span>
</div>
<div className="row2">
<span className={item.title.length > 3 ? 'nbsp4' : 'nbsp3'}>电话&nbsp;</span><b>&nbsp;|&nbsp;</b><span>{item.phone}</span>
</div>
</div>
)
})
}
</div>

</div>
</div>
)
}
}

export default InformationOnDuty;

React中key的必要性与使用

  |  
 阅读次数

React中key的必要性与使用

关键词:diff算法 唯一标识

当React作diff时,只要子元素有key属性,便会去原v-dom树中相应位置(当前横向比较的层级)寻找是否有同key元素,比较它们是否完全相同若是则复用该元素,免去不必要的操作。

key必须是字符串类型,它的取值可以用数据对象的某个唯一属性,或是对数据进行hash来生成key。

强烈 不推荐 用数组index来作为key。如果数据更新仅仅是数组重新排序或在其中间位置插入新元素,那么视图元素都将重新渲染。来看下例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
<ul>{list.map((v,idx)=><li key={idx}>{v}</li>)}</ul>
// ['a','b','c']=>
<ul>
<li key="0">a</li>
<li key="1">b</li>
<li key="2">c</li>
</ul>
// 数组重排 -> ['c','a','b'] =>
<ul>
<li key="0">c</li>
<li key="1">a</li>
<li key="2">b</li>
</ul>

React发现key为0,1,2的元素的text都变了,将会修改三者的html,而不是移动它们。

渲染同类型元素不带key只会产生性能问题; 如果渲染的是不同类型的状态性组件,组件将会被替换,状态丢失。

1
2
3
4
{this.state.type ? 
(<div><Son_1 /><Son_2 /></div>)
: (<div><Son_2 /><Son_1 /></div>)
}

如上述代码,每次按下按钮,原 Son_1与Son_2 组件的实例都将被销毁并创建新的Son_1与Son_2实例不能继承原来的状态;而它们实际上只是调换了位置。给它们加上key可避免问题:

1
2
3
4
{this.state.type ? 
(<div><Son_1 key="1"/><Son_2 key="2"/></div>)
: (<div><Son_2 key="2"/><Son_1 key="1"/></div>)
}

结论:

  1. 使用唯一id作为key比使用 index 作为 key 性能更好
  2. 移动节点比修改节点的html 性能更好

分析补充:\
使用 index 作为 key 某些情况下影响性能。\
举个例子:
一个数组[a,b],渲染成两个不同类型的节点,key用index。然后数组变成[b,a],\
再次渲染时,react先判断变化前后同key的虚拟节点是否相等,发现节点类型就不同,接下来的操作就是新建两个dom节点去替换原来的节点。这种情况下key等于没用。而如果用唯一id,react会知道同key节点没有变化,只是换了位置,只要打个移动节点的patch到dom上,而不是新建替换。
另外修改节点可能修改量无限大啊