Vue实现内部组件轮播切换效果 1. 实现切换

npm install -g vue-cli
vue init webpack slide-demo # 运行后router等都选择no

<script>
// import HelloWorld from './components/HelloWorld'

export default {
name: 'App',
components: {
}
}
</script>

[{index: 1, type: 1, content: ''}, {index: 2, type: 1, content: ''},
{index: 3, type: 2, content: ''}, {index: 4, type: 3, content: ''}]

data() {
return {
currentIndex: 0
};
},
created() {
// 请求question数据
this.questions = [
{index: 1, type: 1, question: ''}, /*...*/];
},

<template>
<div id="app">
<component :is="'task-' + questions[currentIndex].type"
</component>
</div>
</div>
</template>

<template>
<div id="app">
<component :is="'task-' + questions[currentIndex].type"
:question="questions[currentIndex]"></component>
<button class="next-question" @click="nextQuestion">下一题</button>
</div>
</div>
</template>

methods: {
nextQuestion() {
this.currentIndex = (this.currentIndex + 1)
% this.questions.length;
}
},

<template>
<section>
<h2>{{question.index}}. 选择题</h2>
<div>{{content}}</div>
</section>
</template>
<script>
export default {
props: ["question"]
}
</script> 2. 添加轮播切换效果

<template>
<div id="app">
<component :is="'task-' + questions[currentIndex].type"
></component>
<component :is="'task-' + questions[currentIndex + 1].type"
</div>
</div>
</template>

<style>
display: none;
transform: translateX(100%);
/* 添加一个动画，当改变transform的值时就会触发这个动画 */
transition: transform 0.5s ease;
}
</style>

<template>
<div id="app">
<component :is="'task-' + questions[evenIndex].type"
:class="{'next-task': nextIndex === evenIndex}"
<component :is="'task-' + questions[oddIndex].type"
:class="{'next-task': nextIndex === oddIndex}
</div>
</div>
</template>

<script>

export default {
name: 'App',
data() {
return {
currentIndex: 0, // 当前显示的index
nextIndex: 1,    // 表示下一张index，值为currentIndex + 1
evenIndex: 0,    // 偶数的index，值为currentIndex或者是currentIndex + 1
oddIndex: 1      // 奇数的index，值同上
};
},
}

methods: {
nextQuestion() {
this.currentIndex = (this.currentIndex + 1)
% this.questions.length;
this._slideToNext();
},
// 切到下一步的动画效果
_slideToNext() {

}
}

nextQuestion函数可能还有其它一些处理，在它里面调一下_slideToNext函数，这个函数的实现如下：

_slideToNext() {
// 当前slide的类型（currentIndex已经加1了，这里要反一下）
let currentType = this.currentIndex % 2 ? "even" : "odd",
// 下一个slide的类型
nextType =  this.currentIndex % 2 ? "odd": "even";
// 获取下一个slide的dom元素
let \$nextSlide = this.\$refs[`\${nextType}Task`].\$el;
\$nextSlide.style.display = "block";
// 把下一个slide的translate值置为0，原本是translateX(100%)
\$nextSlide.style.transform = "translateX(0)";
// 等动画结束后更新数据
setTimeout(() => {
this.nextIndex = (this.currentIndex + 1)
% this.questions.length;
// 原本的next是当前显示的slide
this[`\${nextType}Index`] = this.currentIndex;
// 而原本的current slide要显示下下张的内容了
this[`\${currentType}Index`] = this.nextIndex;
}, 500);
}

\$nextSlide.style.display = "block";
// 这里使用setimeout，因为\$nextTick有时候没有动画，非必现
setTimeout(() => {
\$nextSlide.style.transform = "translateX(0)";
// ...
}, 0);

display: none;
transform: translateX(100%);
transition: transform 0.5s ease;
z-index: 2;
}

3. 解决点击过快的问题

methods: {
nextQuestion() {
this.currentIndex = (this.currentIndex + 1)
% this.questions.length;
// 把currentIndex插到队首
this.slideQueue.unshift(this.currentIndex);
// 如果当前没有滑动，则执行滑动
!this.sliding && this._slideToNext();
},
}

_slideToNext() {
// 取出下一个要处理的元素
let currentIndex = this.slideQueue.pop();
// 下一个slide的类型
let nextType =  currentIndex % 2 ? "odd" : "even";
let \$nextSlide = this.\$refs[`\${nextType}Task`].\$el;
\$nextSlide.style.display = "block";
setTimeout(() => {
\$nextSlide.style.transform = "translateX(0)";
this.sliding = true;
setTimeout(() => {
this._updateData(currentIndex);
// 如果当前还有未处理的元素，
// 则继续处理即继续滑动
if (this.slideQueue.length) {
// 要等到两个component的DOM更新了
this.\$nextTick(this._slideToNext);
} else {
this.sliding = false;
}
}, 500);
}, 0);
}, _slideToNext(fast = false) {
let currentIndex = this.slideQueue.pop();
// 下一个slide的类型
let nextType =  currentIndex % 2 ? "odd" : "even";
// 获取下一个slide的dom元素
let \$nextSlide = this.\$refs[`\${nextType}Task`].\$el;
\$nextSlide.style.display = "block";
this.sliding = true;
// 使用原生animate函数
\$nextSlide.animate([
// 关键帧
{transform: "translateX(100%)"},
{transform: "translateX(0)"}
], {
duration: fast ? 200 : 500,
iteration: 1,
easing: "ease"
// 返回一个Animate对象，它有一个onfinish的回调
}).onfinish = () => {
// 等动画结束后更新数据
this._updateData(currentIndex);
if (this.slideQueue.length) {
this.\$nextTick(() => {
this._slideToNext(true);
});
} else {
this.sliding = false;
}
};
}, 【号外】《高效前端》已上市，京东、亚马逊、淘宝等均有售
{"ft_lang_label":"__label__zh","ft_lang_prob":0.8061708,"math_prob":0.7929333,"size":9222,"snap":"2019-43-2019-47","text_gpt3_token_len":4924,"char_repetition_ratio":0.15469733,"word_repetition_ratio":0.1857639,"special_character_ratio":0.2327044,"punctuation_ratio":0.23204903,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.96625775,"pos_list":[0,1,2,3,4,5,6,7,8,9,10],"im_url_duplicate_count":[null,null,null,null,null,null,null,null,null,null,null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2019-10-15T04:33:46Z\",\"WARC-Record-ID\":\"<urn:uuid:978ab30c-0294-42d6-8678-ecb7a26d2409>\",\"Content-Length\":\"49254\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:cfab6466-6d6e-43ce-bc5b-bc443ad1775e>\",\"WARC-Concurrent-To\":\"<urn:uuid:e0838027-fd2d-4217-b484-c0423f3944ca>\",\"WARC-IP-Address\":\"39.106.182.51\",\"WARC-Target-URI\":\"https://www.qdskill.com/frames/vue/9174.html\",\"WARC-Payload-Digest\":\"sha1:B2QIGSCIL6KGJRS2KRM2A5YZRJ5WZOJZ\",\"WARC-Block-Digest\":\"sha1:6BN4ZCSRHIPH5QO4EMQ3Y2U76AS5TTPE\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2019/CC-MAIN-2019-43/CC-MAIN-2019-43_segments_1570986655864.19_warc_CC-MAIN-20191015032537-20191015060037-00039.warc.gz\"}"}