请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

请问老师这里NextTick怎么无效

https://img1.sycdn.imooc.com//szimg/5f7e8def0930783a10800742.jpg

老师我想请问下,这里动画更新渲染的话。为什么这里的300ms的setTimeout不可以使用nextTick代替,我使用nextTick代替并没有效果。我理解是当currentRowData改变后同时复原高度等待DOM重新渲染后,在将顶部移动的item高度清0达到动画的效果,然后再次重新渲染重复这个步骤。请问老师这样为什么不可以,nextTick不也是等待页面Dom渲染完毕之后的操作,为什么使用nextTick就看不到动画展示的效果了。

正在回答 回答被采纳积分+3

插入代码

3回答

扬_灵 2020-10-10 11:15:36

同学你好,我测试了你的代码,使用了nextTick首次渲染的时候页面第一个元素并没有过度动画。是因为首次在nexttick打印该元素的时候获取的第一个元素的高度就已经为0了,https://img1.sycdn.imooc.com//szimg/5f812745097d01d525921138.jpg



0 回复 有任何疑惑可以回复我~
  • 提问者 hy_wang #1
    老师我不是很明白第一nexttick打印元素的时候高度就是0了,我设置高度为0之前调用的nextTick,不应该是页面渲染之后await nextTick之后才高度才变为0吗。这个时候为什么不会产生动画。
    回复 有任何疑惑可以回复我~ 2020-10-10 21:41:04
  • 提问者 hy_wang #2
    老师能解释一下为什么nextTick高度直接为0吗,我不是很清楚为什么会这样
    回复 有任何疑惑可以回复我~ 2020-10-10 21:41:45
  • 扬_灵 回复 提问者 hy_wang #3
    exttick是会获取更新后的 DOM 所以这里的await nextTick()是从第一条数据变化之后才开始的,这样才会从第二条数据开始显示。这里使用await nextTick没有什么意义的。
    回复 有任何疑惑可以回复我~ 2020-10-12 10:31:24
提问者 hy_wang 2020-10-09 20:11:33
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
<template>
<div
class="base-scroll-list"
ref="container"
>
{{actualConfig.moveItems}}actualConfig
<div
class="base-scroll-header"
:style="{'background-color':actualConfig.headerBg,'height':`${+actualConfig.headerHeight}px`,
fontSize:`${actualConfig.headerFontSize}px`,
color:actualConfig.headerColor}"
>
<div
class="header-item text"
v-for="(headerItem,index) in headerData"
:key="index"
:align="actualConfig.headerAlign"
:style="{width:`${headerWidth[index]}px`,...headerStyle[index]}"
v-html="headerItem"
/>
</div>
<div
class="base-scroll-rows-wapper"
:style="{height:`${wholeHeight - actualConfig.headerHeight}px`}"
>
<!-- 注意这里div的key 要求每次数据更新之后就重新渲染 -->
<!-- 如果使用rowIndex的话diff算法就会复用之前div 第一个高度就会从0变成72 -->
<!-- 使用不同的key直接渲染之前的div 那么就会进行transition变化 -->
<!-- 而使用不同的key值时 就会直接重新渲染不同的div 所以从0-72高度的变化过程就不会进行transition -->
<div
class="base-scroll-rows"
v-for="(rowData,rowIndex) in currentRowData"
:key="rowData.index"
:style="{backgroundColor:rowBgColor(rowData.index),
fontSize:`${actualConfig.rowFontSize}px`,
height:`${rowsHeight[rowIndex]}px`,
lineHeight:`${rowsHeight[rowIndex]}px`,
color:actualConfig.rowColor}"
>
<!-- 注意style绑定的是对象,所以这里使用了对象的扩展运算符合并 -->
<div
class="base-scroll-list-column"
v-for="(colData,colIndex) in rowData.value"
:style="{width:`${headerWidth[colIndex]}px`,...rowStyle[colIndex]}"
:key="colIndex"
:align="actualConfig.rowsAlign"
>
{{colData}}
</div>
</div>
</div>
 
</div>
</template>
<script>
import { ref, onMounted, nextTick, watch } from "vue";
import cloneDeep from "lodash/cloneDeep";
import assign from "lodash/assign";
import { useScreen } from "../../hooks/useScreen";
const mockData = [
["张三""22", 10000],
["李四""24", 15000],
["王浩羽""26", 24000],
["王浩羽1""26", 24000],
["王浩羽2""26", 24000],
["王浩羽3""26", 24000],
["王浩羽4""26", 24000],
["ddd""26", 24000],
["xxx""26", 24000],
["aaa""26", 24000],
];
const defaultConfig = {
headerData: ["姓名""年龄""月薪"],
headerStyle: [{ color: "red", width: "200px" }],
headerBg: "rgb(90,90,90)",
headerHeight: 48,
headerIndex: true,
headerIndexContext: "#",
headerAlign: "center",
headerFontSize: 32,
headerColor: "#fff",
headerIndexContextStyle: {
color: "green",
width: "20px",
},
data: mockData,
rowNumber: 10, // 每页显示行数
// 序号列内容的样式
rowIndexStyle: {
color: "red",
},
// 表体样式
rowStyle: [{ color: "green" }],
// 奇偶行背景颜色
rowBg: ["rgb(40,40,40)""rgb(55,55,55)"],
rowFontSize: 28,
rowColor: "#000",
rowsAlign: "center",
moveItems: 1,
duration: 2,
};
export default {
name: "BaseScrollList",
props: {
config: {
type: Object,
default: () => {},
},
},
setup(props, ctx) {
const container = ref(null);
const actualConfig = ref({});
 
const headerData = ref([]);
const headerStyle = ref([]);
const headerWidth = ref([]);
 
const rowStyle = ref([]);
const rowsData = ref([]); // 总的
const currentRowData = ref([]); // 当前展示
const currentIndex = ref(0);
const rowItemHeight = ref(0); // 每行行高
const rowsHeight = ref([]); // 数组 行高
const wholeHeight = ref(0);
 
// 处理props Header逻辑
const handleConfigHeader = (config) => {
const _headerData = cloneDeep(config.headerData);
const _headerStyle = cloneDeep(config.headerStyle);
const _rowStyle = cloneDeep(config.rowStyle);
if (!_headerData?.length) return;
if (config.headerIndex) {
_headerData.unshift(config.headerIndexContext);
_headerStyle.unshift(config.headerIndexContextStyle);
_rowStyle.unshift(config.rowIndexStyle);
}
headerData.value = _headerData;
headerStyle.value = _headerStyle;
rowStyle.value = _rowStyle;
const { width, height } = useScreen(container.value);
computedHeaderArea(width, height);
};
 
// 处理props Data逻辑
const handleConfigData = (config) => {
rowsData.value = config.data || [];
if (config.headerIndex) {
// 处理序号列
rowsData.value.forEach((item, index) => {
item.unshift(index + 1);
});
}
// 计算高度
computedHeight(config);
};
 
// 奇偶列背景颜色
const rowBgColor = (index) => {
const bgColor =
index % 2 === 0
? actualConfig.value.rowBg[0]
: actualConfig.value.rowBg[1];
return bgColor;
};
 
// 计算表体高度
const computedHeight = (config) => {
const { width, height } = useScreen(container.value);
const { headerHeight, rowNumber } = config;
const unUsedHeight = height.value - headerHeight;
// rowHeight.value = Math.floor(unUsedHeight / rowNumber);
// 可删除变量 rowHeight
const rowHeight = Math.floor(unUsedHeight / rowNumber);
rowItemHeight.value = rowHeight;
rowsHeight.value = new Array(rowsData.value.length).fill(rowHeight);
};
 
// 计算表头宽度/高度
const computedHeaderArea = (width, height) => {
let useWidth = 0;
let useCount = 0;
const length = headerData.value.length;
const _headerStyle = headerStyle.value;
// 查找是否存在自定义width
_headerStyle.forEach((style) => {
// 存在 按照自定义width判断
if (style.width) {
// 计算自定义宽度总数和总个数
useWidth = +style.width.replace("px""");
useCount++;
}
});
// 动态计算列宽时 使用剩余的宽度除以剩余列数
const _widthCount = Math.floor(
(width.value - useWidth) / (length - useCount)
);
const arrayWidth = new Array(length).fill(_widthCount);
// 计算结果后再次循环 替换arrayWidth
_headerStyle.forEach((style, index) => {
// 存在 按照自定义width判断
if (style.width) {
// 替换对应宽度
arrayWidth[index] = +style.width.replace("px""");
}
});
headerWidth.value = arrayWidth;
};
// 初始化整体数据 需要做额外兼容处理
const initRowData = (rowNumber, data) => {
const length = data.length;
if (length > rowNumber) {
return data.map((i, idx) => {
return {
value: i,
index: idx,
};
});
}
if (length * 2 > rowNumber) {
const newData = [...cloneDeep(data), ...cloneDeep(data)];
return newData.map((i, idx) => {
return {
value: i,
index: idx,
};
});
}
};
// 经典基础算法 滚动算法
const startAnmiation = async () => {
const { rowNumber, moveItems, duration } = actualConfig.value;
 
const _rowData = initRowData(rowNumber, rowsData.value);
const length = _rowData.length;
 
if (length < rowNumber) {
currentRowData.value = _rowData;
else {
// 开启动画
const data = _rowData.slice(currentIndex.value);
data.push(..._rowData.slice(0, currentIndex.value));
currentRowData.value = data;
// 第一次渲染出来之后 我先让先头的个数 高度为0 消失 然后在进行重新渲染(这个时候用户无法察觉变化)
// 视觉上就是动画元素往上移动了
rowsHeight.value = new Array(length).fill(rowItemHeight.value);
// 先重新渲染 0.3s 后 进行动画展示
// 这个Promise 更新页面
// await new Promise((res) => setTimeout(res, 300));
// 使用nextTick的话 第一次页面渲染之后 直接就将height变为了0
await nextTick(); // 为什么这里不可以?
// 页面已经更新了 开始进行动画
rowsHeight.value.splice(0, moveItems, ...new Array(moveItems).fill(0));
// 动画 0.3s后结束 等待1s后 更改currentIndex的值 其实也就是延迟渲染 不然就会立马渲染
await new Promise((res) => setTimeout(res, duration * 1000));
currentIndex.value += moveItems;
const lasted = currentIndex.value - length;
if (lasted >= 0) {
currentIndex.value = lasted;
}
await startAnmiation();
}
};
 
const update = () => {
const _config = assign(defaultConfig, props.config);
// 拿到总高度
const { height } = useScreen(container.value);
wholeHeight.value = height.value;
handleConfigHeader(_config);
console.log(rowsHeight.value, " rowsHeight.value ");
 
handleConfigData(_config);
actualConfig.value = _config;
// 开启动画
startAnmiation();
};
 
watch(
() => props.config,
() => {
console.log("更新");
update();
},
{
deep: true,
}
);
 
onMounted(() => {
update();
});
 
return {
container,
headerData,
headerStyle,
actualConfig,
headerWidth,
rowsData,
currentRowData,
rowsHeight,
rowStyle,
rowBgColor,
wholeHeight,
};
},
};
</script>
<style lang="scss" scoped>
.base-scroll-list {
height: 100%;
width: 100%;
}
.base-scroll-header {
display: flex;
align-items: center;
.text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.header-item {
padding: 0 10px;
box-sizing: border-box;
}
}
.base-scroll-rows-wapper {
overflow: hidden;
.base-scroll-rows {
overflow: hidden;
transition: all 0.3s linear;
display: flex;
.base-scroll-list-column {
padding: 0 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
}
}
}
</style>


0 回复 有任何疑惑可以回复我~
扬_灵 2020-10-09 17:33:03

同学你好,首先await new Promise(resolve => setTimeout(resolve, waitTime))这段代码只是在设置所有行高度和设置隐藏行的高度为0的时候增加的时间间隔,使用await nextTick()是可以的的,而且我们在css样式中设置了 transition: all 0.3s linear;而且动画运动的关键代码是await new Promise(resolve => setTimeout(resolve, duration - waitTime)),可以把你的代码上传一下吗,我在本地帮你定位一下问题。


0 回复 有任何疑惑可以回复我~
  • 提问者 hy_wang #1
    老师我上传了我的代码,使用了nextTick首次渲染的时候页面第一个元素并没有过度动画。
    回复 有任何疑惑可以回复我~ 2020-10-09 20:12:04
  • 提问者 hy_wang #2
    请问老师关于你说的await new Promise(resolve => setTimeout(resolve, waitTime))这段代码设置高度为0的时间间隔我不是很明白,代码执行到await new Promise(resolve => setTimeout(resolve, waitTime))的时候,只有这个Promise在waitTime后才会执行rowsHeight.value.splice(0, moveItems, ...new Array(moveItems).fill(0));将高度变为0.这300ms的时间js相当于是空闲挂起状态吧。
    回复 有任何疑惑可以回复我~ 2020-10-09 20:14:31
  • 扬_灵 回复 提问者 hy_wang #3
    是的 await new Promise(resolve => setTimeout(resolve, waitTime))为了等动画结束后,再执行后面的逻辑
    回复 有任何疑惑可以回复我~ 2020-10-10 09:07:11
问题已解决,确定采纳
还有疑问,暂不采纳
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号