请稍等 ...
×

采纳答案成功!

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

请问老师关闭动画的逻辑

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

老师这里的Promise之前添加这个isAnimationStart意义是什么我不太清楚。我理解正常来讲如果config更新触发watch,那么watch中的代码是这样的

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

watch中的代码stopAnmiation和isAnimationStart的赋值是同步逻辑,在watch触发到执行这段代码,对于上方Promise中的代码并不会执行(JS单线程性质)。所以我认为是无意义的中止动画执行,因为并不存在先执行了stopAnimation这个函数,然后正在执行update函数的逻辑中js会穿插执行Promise中的异步代码。一定是update方法执行完毕之后才会执行其他等待的异步代码。请问老师是我理解错误了吗,如果哪里有不对希望老师可以指出。我认为是config更新后触发watch然后actualConfig是ref的变量会自动更新,从而动画会自动变成更新后的值进行执行。(停止动画的逻辑永远不会被js执行到)。劳烦老师指正,会存在update函数执行到stopAnimation更新值为false而不继续执行update函数之后的逻辑先执行Promise的逻辑吗?

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

插入代码

2回答

提问者 hy_wang 2020-10-09 20:46:31
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
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
<template>
<div
class="base-scroll-list"
ref="container"
>
<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);
let stopAnimation = false;
 
// 处理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 () => {
if (stopAnimation) {
nextTick(() => {
update();
});
return;
}
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));
if (stopAnimation) {
nextTick(() => {
update();
});
return;
}
 
// 使用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));
if (stopAnimation) {
nextTick(() => {
update();
});
return;
}
 
currentIndex.value += moveItems;
const lasted = currentIndex.value - length;
if (lasted >= 0) {
currentIndex.value = lasted;
}
await startAnmiation();
}
};
 
const update = async () => {
// 关闭动画
const _config = assign(defaultConfig, props.config);
// 拿到总高度
const { height } = useScreen(container.value);
wholeHeight.value = height.value;
handleConfigHeader(_config);
handleConfigData(_config);
actualConfig.value = _config;
// 开启动画
stopAnimation = false;
startAnmiation();
};
 
watch(
() => props.config,
() => {
stopAnimation = true;
},
{
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:35:57

同学你好,我今天测试了一下我们课程中关于关闭动画的逻辑,是有些问题的,在这里你可以通过监听isAnimationStart的值来控制动画的开启和关闭,你可以看一下这是我今天测试的使用的代码是使用按钮来控制的

1
2
3
4
5
6
7
8
9
const handleChange = () => {
if (isAnimationStart.value) {
isAnimationStart.value = false
stopAnimation()
else {
isAnimationStart.value = true
startAnimation()
}
}


0 回复 有任何疑惑可以回复我~
  • 提问者 hy_wang #1
    请问下老师可以详细说下吗,比如默认动画是1个一个切换的,我设置一个定时器想再2s后动画变成3个3个切换。那么应该如何停止之前的动画?
    回复 有任何疑惑可以回复我~ 2020-10-09 20:24:58
  • 提问者 hy_wang #2
    请问老师关于停止动画还有更加优雅的方式吗,我改成了这个样子应该是可以的
    回复 有任何疑惑可以回复我~ 2020-10-09 20:46:18
  • 扬_灵 回复 提问者 hy_wang #3
    同学你好,动画每次切换的个数在代码中我们是通过moveNum来控制的,你可以在定时器中先停止动画修改moveNum的变量后再次执行动画。
    回复 有任何疑惑可以回复我~ 2020-10-10 09:05:54
问题已解决,确定采纳
还有疑问,暂不采纳
微信客服

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

帮助反馈 APP下载

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

公众号

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