element-ui Carousel 走马灯源码分析整理笔记(十一)
程序员文章站
2022-08-10 13:45:39
Carousel 走马灯源码分析整理笔记,这篇写的不详细,后面有空补充 main.vue item.vue ......
carousel 走马灯源码分析整理笔记,这篇写的不详细,后面有空补充
main.vue
<template> <!--走马灯的最外层包裹div--> <div class="el-carousel" :class="{ 'el-carousel--card': type === 'card' }" @mouseenter.stop="handlemouseenter" @mouseleave.stop="handlemouseleave"> <div class="el-carousel__container" :style="{ height: height }"> <!--左边的切换箭头--> <transition name="carousel-arrow-left"> <button type="button" v-if="arrow !== 'never'" v-show="(arrow === 'always' || hover) && (loop || activeindex > 0)" @mouseenter="handlebuttonenter('left')" @mouseleave="handlebuttonleave" @click.stop="throttledarrowclick(activeindex - 1)" class="el-carousel__arrow el-carousel__arrow--left"> <i class="el-icon-arrow-left"></i> </button> </transition> <!--右边的切换箭头--> <transition name="carousel-arrow-right"> <button type="button" v-if="arrow !== 'never'" v-show="(arrow === 'always' || hover) && (loop || activeindex < items.length - 1)" @mouseenter="handlebuttonenter('right')" @mouseleave="handlebuttonleave" @click.stop="throttledarrowclick(activeindex + 1)" class="el-carousel__arrow el-carousel__arrow--right"> <i class="el-icon-arrow-right"></i> </button> </transition> <!--幻灯片内容显示区域--> <slot></slot> </div> <!--底部的指示器列表,点击或hover时切换幻灯片--> <ul class="el-carousel__indicators" v-if="indicatorposition !== 'none'" :class="{ 'el-carousel__indicators--labels': haslabel, 'el-carousel__indicators--outside': indicatorposition === 'outside' || type === 'card' }"> <li v-for="(item, index) in items" class="el-carousel__indicator" :class="{ 'is-active': index === activeindex }" @mouseenter="throttledindicatorhover(index)" @click.stop="handleindicatorclick(index)"> <button class="el-carousel__button"><span v-if="haslabel">{{ item.label }}</span></button> </li> </ul> </div> </template> <script> //throttle节流函数 import throttle from 'throttle-debounce/throttle'; import { addresizelistener, removeresizelistener } from 'element-ui/src/utils/resize-event'; export default { name: 'elcarousel', props: { initialindex: { //初始状态激活的幻灯片的索引,从 0 开始 type: number, default: 0 }, height: string, //走马灯的高度 trigger: { //指示器的触发方式 type: string, default: 'hover' }, autoplay: { //是否自动切换 type: boolean, default: true }, interval: { //自动切换的时间间隔,单位为毫秒 type: number, default: 3000 }, indicatorposition: string, //指示器的位置 indicator: { type: boolean, default: true }, arrow: { //切换箭头的显示时机 always/hover/never type: string, default: 'hover' }, type: string, //走马灯的类型,card loop: { //是否循环显示 type: boolean, default: true } }, data() { return { items: [], //幻灯片数组 activeindex: -1, //标识当前幻灯片索引 containerwidth: 0, timer: null, hover: false //记录当前鼠标的移入状态 }; }, computed: { haslabel() { return this.items.some(item => item.label.tostring().length > 0); } }, watch: { items(val) { if (val.length > 0) this.setactiveitem(this.initialindex); }, activeindex(val, oldval) { this.resetitemposition(oldval); this.$emit('change', val, oldval); }, autoplay(val) { val ? this.starttimer() : this.pausetimer(); }, loop() { this.setactiveitem(this.activeindex); } }, methods: { // 当鼠标移入 handlemouseenter() { // 当鼠标移入时,清空幻灯片播放的定时器,暂停自动切换。 this.hover = true; this.pausetimer(); }, // 当鼠标移出 handlemouseleave() { // 当鼠标移出,设置幻灯片自动播放定时器 this.hover = false; this.starttimer(); }, iteminstage(item, index) { const length = this.items.length; // 满足当前为最后一个幻灯片;当前幻灯片在场景内;第一个幻灯片激活状态; // 或者 满足 当前幻灯片在场景内;当前幻灯片后面有至少一个项目;当前幻灯片后面一个项目处于激活状态 if (index === length - 1 && item.instage && this.items[0].active || (item.instage && this.items[index + 1] && this.items[index + 1].active)) { return 'left'; } else if (index === 0 && item.instage && this.items[length - 1].active || (item.instage && this.items[index - 1] && this.items[index - 1].active)) { return 'right'; } return false; }, // 当鼠标移入左边的切换幻灯片的按钮 handlebuttonenter(arrow) { this.items.foreach((item, index) => { if (arrow === this.iteminstage(item, index)) { item.hover = true; } }); }, handlebuttonleave() { this.items.foreach(item => { item.hover = false; }); }, // 将所有的幻灯片放入items数组中 updateitems() { this.items = this.$children.filter(child => child.$options.name === 'elcarouselitem'); }, // 重置幻灯片位置 resetitemposition(oldindex) { this.items.foreach((item, index) => { item.translateitem(index, this.activeindex, oldindex); }); }, //改变当前的幻灯片 playslides() { if (this.activeindex < this.items.length - 1) { this.activeindex++; } else if (this.loop) { this.activeindex = 0; } }, pausetimer() { // 清空定时器 clearinterval(this.timer); }, starttimer() { // 如果自动切换的时间间隔小于等于0时,或者用户未设置自动播放时,直接返回,幻灯片不自动播放 if (this.interval <= 0 || !this.autoplay) return; this.timer = setinterval(this.playslides, this.interval); }, //设置当前页 setactiveitem(index) { // 如果index是字符串,则是用户设置了幻灯片的name if (typeof index === 'string') { // 找到对应name的幻灯片 const filtereditems = this.items.filter(item => item.name === index); // 如果找到的items长度大于0,取第一个的索引作为我们要使用的索引 if (filtereditems.length > 0) { index = this.items.indexof(filtereditems[0]); } } // 索引转成数字 index = number(index); // 如果索引不是数字,或者不是整数 if (isnan(index) || index !== math.floor(index)) { // 如果不是生产环境下,就报warn process.env.node_env !== 'production' && console.warn('[element warn][carousel]index must be an integer.'); return; } // 获取幻灯片数组的长度 let length = this.items.length; const oldindex = this.activeindex; // 如果索引小于0,判断是否设置循环播放,如果设置了,设置当前页为最后一页;也就是在向前切换到第一张,继续向前切换显示最后一张,然后显示倒数第二张 if (index < 0) { this.activeindex = this.loop ? length - 1 : 0; } else if (index >= length) { //如果索引大于数组长度,判断是否设置循环播放,如果设置了,设置当前页为第一页;也就是在向后切换到最后一张时,继续向后切换显示第一张,然后显示第二张 this.activeindex = this.loop ? 0 : length - 1; } else { //否则,当前页设置为索引页 this.activeindex = index; } if (oldindex === this.activeindex) { this.resetitemposition(oldindex); } }, prev() { this.setactiveitem(this.activeindex - 1); }, next() { this.setactiveitem(this.activeindex + 1); }, handleindicatorclick(index) { this.activeindex = index; }, handleindicatorhover(index) { if (this.trigger === 'hover' && index !== this.activeindex) { this.activeindex = index; } } }, created() { // throttle节流函数,点击频率控制,返回函数连续调用时 http://npm.taobao.org/package/throttle-debounce // 第二个参数notrailing,当其设置为true时,保证函数每隔delay时间只能执行一次,如果设置为false或者没有指定,则会在最后一次函数调用后的delay时间后重置计时器。 this.throttledarrowclick = throttle(300, true, index => { this.setactiveitem(index); }); this.throttledindicatorhover = throttle(300, index => { this.handleindicatorhover(index); }); }, mounted() { this.updateitems(); this.$nexttick(() => { addresizelistener(this.$el, this.resetitemposition); if (this.initialindex < this.items.length && this.initialindex >= 0) { this.activeindex = this.initialindex; } this.starttimer(); }); }, beforedestroy() { if (this.$el) removeresizelistener(this.$el, this.resetitemposition); } }; </script>
item.vue
<template> <!--单个的幻灯片html结构--> <div v-show="ready" class="el-carousel__item" :class="{ 'is-active': active, 'el-carousel__item--card': $parent.type === 'card', 'is-in-stage': instage, 'is-hover': hover, 'is-animating': animating }" @click="handleitemclick" :style="{ mstransform: `translatex(${ translate }px) scale(${ scale })`, webkittransform: `translatex(${ translate }px) scale(${ scale })`, transform: `translatex(${ translate }px) scale(${ scale })` }"> <div v-if="$parent.type === 'card'" v-show="!active" class="el-carousel__mask"></div> <!--幻灯片的自定义内容以插槽的方式显示在此区域--> <slot></slot> </div> </template> <script> const card_scale = 0.83; export default { name: 'elcarouselitem', props: { name: string, //幻灯片的名字,可用作 setactiveitem 的参数 label: { //该幻灯片所对应指示器的文本 type: [string, number], default: '' } }, data() { return { hover: false, translate: 0, //偏移量设置 scale: 1, active: false, ready: false, instage: false, animating: false }; }, methods: { processindex(index, activeindex, length) { if (activeindex === 0 && index === length - 1) { //当前是activeindex是第一张,index是最后一张,返回-1,相差-1,表示二者相邻且在左侧 return -1; } else if (activeindex === length - 1 && index === 0) { //当前页activeindex是最后一张,index是第一张,返回length,相差1,表示二者相邻且在右侧 return length; } else if (index < activeindex - 1 && activeindex - index >= length / 2) { // 如果,index在activeindex前一页的前面,并且之间的间隔在一半页数即以上,则返回页数长度+1,这样它们会被置于最右侧 return length + 1; } else if (index > activeindex + 1 && index - activeindex >= length / 2) { // 如果,index在activeindex后一页的后面,并且之间的间隔在一般页数即以上,则返回-2,这样它们会被置于最左侧 return -2; } return index; }, calculatetranslate(index, activeindex, parentwidth) { if (this.instage) { return parentwidth * ((2 - card_scale) * (index - activeindex) + 1) / 4; } else if (index < activeindex) { return -(1 + card_scale) * parentwidth / 4; } else { return (3 + card_scale) * parentwidth / 4; } }, // 这是用来移动幻灯片。 translateitem(index, activeindex, oldindex) { // 获取父组件的宽度 const parentwidth = this.$parent.$el.offsetwidth; // 获取幻灯片数组的长度 const length = this.$parent.items.length; // 如果不是card模式 if (this.$parent.type !== 'card' && oldindex !== undefined) { this.animating = index === activeindex || index === oldindex; } if (index !== activeindex && length > 2 && this.$parent.loop) { // 对当前索引进行处理 index = this.processindex(index, activeindex, length); } if (this.$parent.type === 'card') { this.instage = math.round(math.abs(index - activeindex)) <= 1; this.active = index === activeindex; this.translate = this.calculatetranslate(index, activeindex, parentwidth); this.scale = this.active ? 1 : card_scale; } else { this.active = index === activeindex; // 设置幻灯片的偏移量 this.translate = parentwidth * (index - activeindex); } this.ready = true; }, handleitemclick() { const parent = this.$parent; if (parent && parent.type === 'card') { const index = parent.items.indexof(this); parent.setactiveitem(index); } } }, created() { this.$parent && this.$parent.updateitems(); }, destroyed() { this.$parent && this.$parent.updateitems(); } }; </script>
下一篇: 又惊又怒超经典小笑话
推荐阅读
-
element-ui Upload 上传组件源码分析整理笔记(十四)
-
element-ui Tag、Dialog组件源码分析整理笔记(五)
-
element-ui button组件 radio组件源码分析整理笔记(一)
-
element-ui Carousel 走马灯源码分析整理笔记(十一)
-
element-ui Upload 上传组件源码分析整理笔记(十四)
-
element-ui input组件源码分析整理笔记(六)
-
element-ui Steps步骤条组件源码分析整理笔记(九)
-
element-ui switch组件源码分析整理笔记(二)
-
element-ui inputNumber、Card 、Breadcrumb组件源码分析整理笔记(三)
-
element-ui Rate组件源码分析整理笔记(十三)