一个滑动选中的小功能

85 阅读1分钟

思路

考虑到是移动端的滑动选中功能,所以刚开始的思路是直接使用移动端自带的选中功能。但是和产品沟通需求后,很明显不是他想要的效果,是实现一个类似微信图文识别选中文本的功能。

初始思路是:

  1. 使用 touch 的事件监听滑动
  2. 通过当前位置获取对应元素,给对应元素添加上样式
  3. 需要获取文本时,遍历元素来获取

但是在实现的时候还有一些细节需要考虑,比如如何取消选中。

实现

<template>  
    <div class="copy-div">  
  
        <!-- selectstart return false 阻止默认的选中事件-->  
        <div class="copy-text" @selectstart="()=>{return false}">  
            <span v-for="(item, index) in content" class="text-span" :key="index" :id="index" 
            @touchstart="highlight"  @touchmove="highlight"
            @touchend="highlight">{{  item  }}</span>  
        </div>  
    </div>  
</template>  
  
<script>  
// 用于记录当前元素的更改时间  
const updateObj = {};  
export default {  
    props: {},  
    data() {  
        return {  
            copyText: '',  
            content: 'hello 欢迎来到我的世界!!!!'  
        };  
    },  
    computed: {},  
    created() {  
    },  
    mounted() {  
    },  
    methods: {  
        // 标识高亮  
        highlight(e) {  
            let currentTime = new Date().getTime();  
            // 获取当前位置元素  
            let element = document.elementFromPoint(e.changedTouches[0].pageX, e.changedTouches[0].pageY);  
            if (element) {  
                let classList = element.classList || [];  
                let id = element.id;  
                let updTime = updateObj[id] || 0;  
                let className = '';  
                // 要确保选中的是目标元素  
                // touch 事件可能多次触发,如果修改时间距离当前事件100ms之后才允许再次修改  
                if (classList.contains('text-span') && currentTime - updTime > 800) {  
                    // 取消选中  
                    if (classList.contains('highlight-wrap')) {  
                        className = 'text-span';  
                    } else {  
                    // 选中  
                        className = 'text-span highlight-wrap';  
                    }  
                element.setAttribute('class', className);  
                }  
                // 更新当前元素的修改事件  
                updateObj[id] = currentTime;  
            }  
            this.$nextTick(() => {  
                this.getSelectText();  
            });  
        },  
        // 全选/取消全选  
        // state true 取消全选,false全选  
        selectAll(state) {  
            let dom = document.getElementsByClassName('copy-text')[0];  
            let elements = dom.children;  
            let currentTime = new Date().getTime();  
            if (elements) {  
                elements.forEach(el => {  
                    el.setAttribute('class', state ? 'text-span' : 'text-span highlight-wrap');  
                    updateObj[el.id] = currentTime;  
                });  
            }  
            this.getSelectText();  
        },  
        // 获取选中的内容  
        getSelectText() {  
            let dom = document.getElementsByClassName('copy-text')[0];  
            let elements = dom.children;  
            this.copyText = '';  
            if (elements) {  
                elements.forEach(el => {  
                    if (el.classList.contains('highlight-wrap')) {  
                        this.copyText += el.innerHTML;  
                    }  
                });  
            }  
        },  
    },  
};  
</script>  
  
<style lang="less" scoped>  
  
.copy-div {  
    // 阻止默认选中事件  
    -webkit-user-select: none;  
    -moz-user-select: none;  
    -ms-user-select: none;  
    user-select: none;  
  
    .copy-text {  
        font-size: 14px;  
        font-weight: 400;  
        color: rgba(31, 34, 38, 0.8);  
        line-height: 26px;  
    }  
}  
  
// 选中样式  
.highlight-wrap {  
    background: #B3D7FF;  
}  
</style>

遗留问题

虽然基本的实现了完整的功能,但是还有一些问题:

  1. 滑动过快时,可能可能有一些元素应该选中,但实际上并未选中
  2. 频繁遍历元素

如果大家有什么好的方案欢迎讨论。