zoukankan      html  css  js  c++  java
  • 手写一个移动端带惯性的轮播图vue组件

    利用vue的插槽(solt)的方法实现

    1. 带有惯性
    2. 可以适应手机端屏幕
    3. 可以灵活的修改样式
    4. 可以手动的拖拽
    5. 拉力
    调用方式:
    1. 引入组件
      export default defineComponent({
          components: {
              slide
          },
    2. 在template模板插入代码
      <slide
                  ref="slide"
                  :count="swiperData.length"
                  :springrange=".2"
              >
                  <slot slot="swiper-item" v-for="(item, index) in swiperData">
                      <div class="emoji-item">
                      </div>
                  </slot>
              </slide>
    3. 在style编swiper-item的样式。

    组件代码:
    <template>
        <div class="swiper-container">
            <div 
                class="swiper-touch" 
                ref="touch" 
                @touchstart="touchStart" 
                @touchmove="touchMove" 
                @touchend="touchEnd">
                <div
                    class="swiper-wrapper"
                    :style="{
                        transform: 'translate3d(' + slideEffect + 'px, 0px, 0px)',
                        transitionDuration: tdurationTime + 'ms',
                         count + '00%'
                    }"
                >
                    <slot name="swiper-item"></slot>
                </div>
            </div>
            <ul class="swiper-dots">
                    <li 
                        v-for="num in count" 
                        :key="num" 
                        :class="currantIndex === num?'swiper-dots-active':''"
                        @click="handleChangeCurrantIndex(num)">
                    </li>
            </ul>
        </div>
    </template>
    
    
    <script lang="ts">
    import {
        VueCompositionApiLib as VC,
        toRefs,
        defineComponent,
        reactive,
        onMounted
    } from 'common';
    
    interface Props {
        count: number;
        springrange: number;
        tduration: number;
        isShowIndexBtn: boolean;
    }
    export default defineComponent({
        props: {
            count: {
                type: Number,
                default: 0
            },
            isShowIndexBtn: {
                type: Boolean,
                default: true
            },
            springrange: {
                type: Number,
                default: 0.2
            },
            tduration: {
                type: Number,
                default: 300
            }
        },
        setup(props: Props, context: VC.SetupContext) {
            const state = reactive({
                startX: 0, // 开始触摸的位置
                moveX: 0, // 滑动时的位置
                endX: 0, // 结束触摸的位置
                disX: 0, // 移动距离
                springWidth: 0, // 回弹的范围屏幕的百分比0.2即左右五分之一
                slideEffect: 0, // 当前位移像素
                btnWidth: 0, // 页面宽度
                currantIndex: 1, // 当前slide
                tdurationTime: 300, // 过渡时间
                lastEndSpead: 0, // 上次的位置
                status: 'a' // c不可以切换上一张,a可以上下切换,b不能切换下一张
            });
    
            onMounted(() => {
                state.btnWidth = context.refs.touch.offsetWidth;
                state.springWidth = props.springrange * state.btnWidth;
                state.tdurationTime = props.tduration;
            });
    
            const touchStart = (e: any) => {
                e = e || event;
                if (e.touches.length === 1) {
                    state.startX = e.touches[0].clientX; // 记录开始位置
                }
            };
            const touchMove = (e: any) => {
                e = e || event;
                state.tdurationTime = 0;
                state.moveX = e.touches[0].clientX;
                state.disX = state.moveX - state.startX;
                // 在边界的时候做一些拉力的判断            
                if(state.disX > 0 && state.currantIndex === 1){
                    state.slideEffect = state.disX / 3 + state.lastEndSpead;
                }
                else if(state.disX < 0 && state.currantIndex === props.count)
                    state.slideEffect = state.disX / 3 + state.lastEndSpead;
                else {
                    state.slideEffect = state.disX + state.lastEndSpead;
                }
                // disX不考虑回弹时小于0下一张大于0上一张
            };
    
            const next = () => {
                state.slideEffect = -state.btnWidth + state.lastEndSpead;
                state.currantIndex += 1;
            };
            const last = () => {
                state.slideEffect = state.btnWidth + state.lastEndSpead;
                state.currantIndex -= 1;
            };
            const forbidChange = () => {
                state.slideEffect = state.lastEndSpead;
            };
            const actions: any = {
                'a': next,
                'b': last,
                'c': forbidChange
            };
    
            const touchEnd = (e: any) => {
                e = e || event;
                state.tdurationTime = props.tduration;
                state.endX = e.changedTouches[0].clientX;
                if (Math.abs(state.disX) < state.springWidth && state.currantIndex) {
                    state.status = 'c';
                }
                else if (state.disX < 0 && state.currantIndex === props.count) {
                    state.status = 'c';
                }
                else if (state.disX > 0 && state.currantIndex === 1) {
                    state.status = 'c';
                }
                else if (state.disX > 0) {
                    state.status = 'b';
                }
                else if (state.disX < 0) {
                    state.status = 'a';
                }
                state.disX = 0;
                actions[state.status]();
                state.lastEndSpead = state.slideEffect;
            };
    
            const handleChangeCurrantIndex = (num: number) => {
                state.currantIndex = num;
                state.slideEffect = -state.btnWidth * (num - 1);
                state.lastEndSpead = state.slideEffect;
            };
            return {
                ...toRefs(state),
                touchStart,
                touchMove,
                touchEnd,
                handleChangeCurrantIndex
            };
        }
    });
    </script>
    
    
    
    <style lang="less" scoped>
    .swiper-container {
        overflow: hidden;
        z-index: 1;
         100%;
        background: #F5F5F5;
        .swiper-dots {
            display: block;
            position: absolute;
            right: 0;
             100%;
            bottom: .25rem;
            height: .06rem;
            list-style: none;
            text-align: center;
            & > li {
                 .07rem;
                height: .07rem;
                margin: 0 .04rem;
                background: #ccc;
                display: inline-block;
                vertical-align: top;
                border-radius: 50%;
                opacity: .45;
            }
            .swiper-dots-active {
                opacity: 1;
            }
        }
    }
    
    .swiper-touch {
         100%;
        position: relative;
        .swiper-wrapper {
            display: flex;
            position: relative;
            transition-property: transform, height, -webkit-transform, -moz-transform, -o-transform;
        }
    }
    </style>

    总结:

    1. 相比于其他组件库的组件可能用起来不够简单
    2. 但是也有自己的优点即灵活度比较高,可以根据自己的想法扩展成自己的组件
    3. 可以随意的填写自己的样式,做成自己想要的样子
  • 相关阅读:
    数据中的悖论
    Exchange server2007自动发现服务(Auto discover service)原理及调试
    Exchange 2007 安装完后需要注意的几件事情
    有感
    Exchange Server 2007 LCR小试
    【转】Vmware ESX 3.0出现“error connecting: can not connect to host x.x.x.x: a connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed……的解决方法
    学习Exchange的几个站点
    在Exchange server 2007中启用pop3和IMAP4协议访问
    Exchange server 2007环境下,outlook2007同步脱机地址簿时出现“0x80190194”错误的分析与解决方法
    安装isa2006后加入域提示“RPC服务器不可用”
  • 原文地址:https://www.cnblogs.com/tipsydr/p/13784787.html
Copyright © 2011-2022 走看看