惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

zodream梦想开源/个人编程日记

angular 21 升级使用 signals 方案笔记-zodream梦想开源/个人编程日记 文件解析笔记-zodream梦想开源/个人编程日记 密码本开发笔记之读写与保存-zodream梦想开源/个人编程日记 基于 SkiaSharp 的轮廓获取-zodream梦想开源/个人编程日记 SkiaSharp 把 pixel byte[] 转成 SKBitmap-zodream梦想开源/个人编程日记 nas 使用 Docker 安装 gogs-zodream梦想开源/个人编程日记 复制 android 手机中的文件到电脑-zodream梦想开源/个人编程日记 最新|个人日记-zodream梦想开源/个人编程日记 升级 SiteServer CMS 并迁移到 Linux 服务器-zodream梦想开源/个人编程日记 最新|个人日记-zodream梦想开源/个人编程日记 最新|个人日记-zodream梦想开源/个人编程日记 最新|个人日记-zodream梦想开源/个人编程日记 周报:寻找优质的周刊-zodream梦想开源/个人编程日记 开发日志:对Markdown的代码块新增引用来源支持-zodream梦想开源/个人编程日记 周报:怎么写技术类的教程文章-zodream梦想开源/个人编程日记 css display:flex 布局尺寸超出问题-zodream梦想开源/个人编程日记 周报:SEO优化的思考-zodream梦想开源/个人编程日记 Edge 浏览器不适用 Edge Image Viewer 打开图片 -zodream梦想开源/个人编程日记 SEO 学习笔记(一) 内容来源-zodream梦想开源/个人编程日记 PHP 实现双因素身份认证(2FA)-zodream梦想开源/个人编程日记 winui3 自定义标题栏-zodream梦想开源/个人编程日记 WPF MVVM 获取List 多选数据-zodream梦想开源/个人编程日记 php 接入 WebAuthn 登录-zodream梦想开源/个人编程日记 Burp Suite 抓包-zodream梦想开源/个人编程日记 lnmp php集成环境安装包使用-zodream梦想开源/个人编程日记 js 进行在线编辑器开发-zodream梦想开源/个人编程日记 使用 indexnow 注意事项-zodream梦想开源/个人编程日记 Godot 使用字体图标 例如: Iconfont、FontAwesome-zodream梦想开源/个人编程日记 angular 15 对指定页面进行访问限制-zodream梦想开源/个人编程日记 CSS 使用 column-count 实现瀑布流出现内容分割的解决办法-zodream梦想开源/个人编程日记 angular 15 实现按下确认键,焦点移动到下一个表单或提交表单-zodream梦想开源/个人编程日记 input 确认按键事件在手机端不生效-zodream梦想开源/个人编程日记 C# 使用socket 进行通讯-zodream梦想开源/个人编程日记 Maui开发中Windows应用开启管理员权限-zodream梦想开源/个人编程日记 Maui 中自定义控件-zodream梦想开源/个人编程日记 TencentOS Server 3.1 安装 Nginx 1.23、PHP 8.2、MariaDB 10.11-zodream梦想开源/个人编程日记 angular 14 使用 ng-template 实现tree 结构显示-zodream梦想开源/个人编程日记 angular 14 替换 ComponentFactoryResolver 实现动态创建组件-zodream梦想开源/个人编程日记 c# 动态安装和卸载dll-zodream梦想开源/个人编程日记 慎用 CompositionTarget.Rendering-zodream梦想开源/个人编程日记 c# 重写 c++ 程序笔记:数据初始化-zodream梦想开源/个人编程日记 源码编译 aseprite-zodream梦想开源/个人编程日记 记录一下字符串分隔split各语言之间的不同-zodream梦想开源/个人编程日记 c# Gzip解码无头内容-zodream梦想开源/个人编程日记 Windows 10 查看内存占用-zodream梦想开源/个人编程日记 UWP 使用 win2d:加阴影-zodream梦想开源/个人编程日记 清除 PowerShell 历史记录-zodream梦想开源/个人编程日记 c# 调用 c++ 的dll-zodream梦想开源/个人编程日记 c# 重写 c++ 程序笔记:遍历-zodream梦想开源/个人编程日记 Net Core 与 UWP 共用类开发-zodream梦想开源/个人编程日记 hashcat(二)找回rar解压密码-zodream梦想开源/个人编程日记 Godot 学习笔记(一)-zodream梦想开源/个人编程日记 升级vue3记录-zodream梦想开源/个人编程日记 angular 12 显示数学公式-zodream梦想开源/个人编程日记 js 监听按键事件-zodream梦想开源/个人编程日记 angular 12 ng-deep 使用注意事项-zodream梦想开源/个人编程日记 angular 16 动态生成组件-zodream梦想开源/个人编程日记 angular 12 动画执行完成事件-zodream梦想开源/个人编程日记 angular 12 全局搜索组件-zodream梦想开源/个人编程日记 angular 12 中单例 Service 的使用-zodream梦想开源/个人编程日记 js 实现一个正则替换-zodream梦想开源/个人编程日记 uwp win2d 使用-zodream梦想开源/个人编程日记 UWP Custom Control自定义控件开发-zodream梦想开源/个人编程日记 UWP 读取应用内资源-zodream梦想开源/个人编程日记 gin 使用笔记(二)出错点-zodream梦想开源/个人编程日记 gin 使用笔记(一)基础-zodream梦想开源/个人编程日记 angular 关于自定义组件事件传递-zodream梦想开源/个人编程日记 angular 11 怎么获取 Content-Disposition-zodream梦想开源/个人编程日记 apache 使用gzip 压缩 js、css-zodream梦想开源/个人编程日记 angular 11 返回上一页保留页面数据的思考-zodream梦想开源/个人编程日记 一个简单的HTML音视频播放器-zodream梦想开源/个人编程日记 Net Core 实现一个简单的分页功能-zodream梦想开源/个人编程日记 关于内容中的 @用户 加 话题 的一些想法-zodream梦想开源/个人编程日记 Github Host 更改-zodream梦想开源/个人编程日记 OBS-Studio 等录屏软件录制显示器内容的黑屏的解决方法-zodream梦想开源/个人编程日记 angular 11 FormBuilder 中 FormGroup 和 FormArray 使用-zodream梦想开源/个人编程日记 angular 11 ngrx/effects 使用理解-zodream梦想开源/个人编程日记 angular 11 ngrx/store 使用理解-zodream梦想开源/个人编程日记 angular 10 直接获取表单值-zodream梦想开源/个人编程日记 angular 10 使用 tinymce 编辑器-zodream梦想开源/个人编程日记 htaccess 搭配 angular 10 放在二级目录-zodream梦想开源/个人编程日记 微信小程序跨页面传值-zodream梦想开源/个人编程日记 js 对 FileList 进行文件过滤上传-zodream梦想开源/个人编程日记 angular自定义表单组件支持 formControlName-zodream梦想开源/个人编程日记 基于不同形式的json响应处理-zodream梦想开源/个人编程日记 flutter CupertinoPicker 使用不显示-zodream梦想开源/个人编程日记 CC协议-zodream梦想开源/个人编程日记 flutter margin 负值实现-zodream梦想开源/个人编程日记 win10添加删除开机自启项-zodream梦想开源/个人编程日记 Wallpager Engine 删除记录-zodream梦想开源/个人编程日记 angular10教程之http 拦截器-zodream梦想开源/个人编程日记 dpl 文件-zodream梦想开源/个人编程日记 微信小程序开发记录(一)真机无法进入页面-zodream梦想开源/个人编程日记 flutter 跳转页面操作上一页-zodream梦想开源/个人编程日记 Regex Generator 使用指南-zodream梦想开源/个人编程日记 go init函数-zodream梦想开源/个人编程日记 angular 9 升级 angular 10-zodream梦想开源/个人编程日记 kotlin AndroidManifest 注意事项-zodream梦想开源/个人编程日记 对于zodream 框架的优化的思考-zodream梦想开源/个人编程日记 flutter 页面滚动条-zodream梦想开源/个人编程日记
vue 实现左滑删除-zodream梦想开源/个人编程日记
2019-03-14 · via zodream梦想开源/个人编程日记

更新

支持 :index 为数值或字符串

演示效果

源码

实现原理

  1. 需要记录初始位置,滑动方向,是否是滑动(点击不触发touchmove事件)

oldLeft: 0,       // 记录初始位置 > 0 显示左边控件 < 0 显示右边控件  0 原始状态
left: 0,         // 控制滑动位置
startX: 0,       // 记录滑动的初始位置
isTouch: false,  //判断是否是滑动


touchStart(e: TouchEvent) {
    this.oldLeft = this.left;
    this.isTouch = false;
    this.startX = e.targetTouches[0].clientX;
},

12345678910111213

  1. 滑动中不能超过可显示的隐藏区域宽度

touchMove(e: TouchEvent) {
    this.isTouch = true;
    // 获取滑动距离
    const diff = e.targetTouches[0].clientX - this.startX;
    if (this.oldLeft == 0) {
        if (diff < 0) {
            // 左滑显示右边控件
            this.left = Math.max(diff, -this.getRightWidth());
            return;
        }
        // 右滑显示左边控件
        this.left = Math.min(diff, this.getLeftWidth());
        return;
    }
    if (this.oldLeft > 0) {
        if (diff > 0) {
            // 已显示左边控件,不能继续右滑
            return;
        }
        // 左滑隐藏左边控件
        this.left =  Math.max(this.oldLeft + diff, 0);
        return;
    }
    if (diff < 0) {
        // 已显示右边控件,不能继续左滑
        return;
    }
    // 右滑隐藏右边控件
    this.left = Math.min(this.oldLeft + diff, 0);
}

1234567891011121314151617181920212223242526272829303132

  1. 滑动结束,判断总滑动距离,不超过1/3自动复原

touchEnd(e: TouchEvent) {
    if (!this.isTouch) {
        // 点击,并复原
        this.animation(this.left, 0);
        this.$emit('click');
        return;
    }
    if (this.left == 0) {
        return;
    }
    if (this.left > 0) {
        const width = this.getLeftWidth();
        this.animation(this.left, this.left * 3 > width ? width : 0);
        return;
    }
    const width = - this.getRightWidth();
    this.animation(this.left, this.left * 3 < width ? width : 0);
}

1234567891011121314151617181920

  1. 点击事件需要复原

关于操作一个隐藏其他实现设想

  1. 通过父控件记录所有子控件引用,并标记子空间顺序,在父控件调用子元素复原方法或子控件内部调用

  2. 子控件在初始化的时刻,生成一个自增唯一标识,然后根据这个挂载到父控件或全局属性上

完整代码


<template>
    <div class="swipe-row" :style="{left: left + 'px'}">
        <div class="actions-left" ref="left">
            <slot name="left"></slot>
        </div>
        <div :class="['swipe-content', name]"  
            @touchstart='touchStart'
            @touchmove='touchMove'
            @touchend='touchEnd'>
            <slot></slot>
        </div>
        <div class="actions-right" ref="right">
            <slot name="right">
                <i class="fa fa-trash" @click="tapRemove"></i>
            </slot>
        </div>
    </div>
</template>
<script lang="ts">
import { Vue, Component, Prop, Emit } from 'vue-property-decorator';

@Component
export default class SwipeRow extends Vue {
    @Prop([String, Array]) readonly name!: string| string[];
    @Prop([Number, String]) readonly index!: number|string;
    oldLeft: number = 0;
    left = 0;
    startX = 0;
    isTouch = false;
    leftBox: HTMLDivElement | null = null;
    rightBox: HTMLDivElement | null = null;

    mounted() {
        this.leftBox = this.$refs.left as HTMLDivElement;
        this.rightBox = this.$refs.right as HTMLDivElement;
    }

    getLeftWidth(): number {
        if (!this.leftBox) {
            return 0;
        }
        return this.leftBox.clientWidth || this.leftBox.offsetWidth;
    }

    getRightWidth(): number {
        if (!this.rightBox) {
            return 0;
        }
        return this.rightBox.clientWidth || this.rightBox.offsetWidth;
    }

    tapRemove(item: any) {
        this.$emit('remove', item);
    }

    touchStart(e: TouchEvent) {
        this.resetOther();
        this.oldLeft = this.left;
        this.isTouch = false;
        this.startX = e.targetTouches[0].clientX;
    }

    touchMove(e: TouchEvent) {
        this.isTouch = true;
        const diff = e.targetTouches[0].clientX - this.startX;
        if (this.oldLeft == 0) {
            if (diff < 0) {
                this.left = Math.max(diff, -this.getRightWidth());
                return;
            }
            this.left = Math.min(diff, this.getLeftWidth());
            return;
        }
        if (this.oldLeft > 0) {
            if (diff > 0) {
                return;
            }
            this.left =  Math.max(this.oldLeft + diff, 0);
            return;
        }
        if (diff < 0) {
            return;
        }
        this.left = Math.min(this.oldLeft + diff, 0);
    }

    touchEnd(e: TouchEvent) {
        if (!this.isTouch) {
            this.animation(this.left, 0);
            this.$emit('click');
            return;
        }
        //const diff = e.changedTouches[0].clientX - this.startX;
        if (this.left == 0) {
            return;
        }
        if (this.left > 0) {
            const width = this.getLeftWidth();
            this.animation(this.left, this.left * 3 > width ? width : 0);
            return;
        }
        const width = - this.getRightWidth();
        this.animation(this.left, this.left * 3 < width ? width : 0);
    }
    // 补间动画
    animation(
        start: number, end: number, endHandle?: Function) {
        const diff = start > end ? -1 : 1;
        let step = 1;
        let handle = setInterval(() => {
            start += (step ++) * diff;
            if ((diff > 0 && start >= end) || (diff < 0 && start <= end)) {
                clearInterval(handle);
                this.left = end;
                endHandle && endHandle();
                return;
            }
            this.left = start;
        }, 16);
    }

    // 暴露出来给外部调用
    public reset() {
        if (this.left === 0) {
            return;
        }
        this.animation(this.left, 0);
    }

    // 复原其他
    resetOther() {
        if (typeof this.index == 'undefined') {
            return;
        }
        const items: SwipeRow[] = this.$parent.$refs.swiperow as SwipeRow[];
        if (!items || items.length < 1) {
            return;
        }
        for (let i = 0; i < items.length; i++) {
            if (items[i].index == this.index) {
                continue;
            }
            items[i].reset();
        }
    }
}
</script>

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149

使用

<div class="swipe-box">
    <SwipeRow v-for="(item, index) in items" :key="index" @remove="tapRemove(item)" :index="index" ref="swiperow">
        <div>
            这是内容
        </div>
    </SwipeRow>
</div>

12345678

样式


.swipe-box {
    overflow: hidden;
    .swipe-row {
        width: 100%;
        position: relative;
        height: 5rem; 
        padding: 0;
        margin: 0;
        transition: left .5s;
        .swipe-content {
            width: 100%;
            display: block;
            height: 5rem;
        }
        .actions-left,
        .actions-right {
            position: absolute;
            height: 5rem; 
            min-height: 5rem;
            max-height: 5rem;
            text-align: center;
            font-size: 1.375rem;
            top: 0;
            white-space: nowrap;
            .fa {
                font-size: 1.875rem;
                padding: 1.5625rem 0.9375rem;
            }
            a {
                color: #fff;
                text-decoration: none;
            }
        }
        .actions-left {
            color: #fff;
            background-color: rgb(0, 187, 72);
            right: 100%;
        }
        .actions-right {
            color: #fff;
            background-color: #BB0000;
            left: 100%;
        }
    }
}

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647

转载请保留原文链接: https://zodream.cn/blog/id/45.html