2018-08-04

移动端触摸事件

最近在写一个博客后台的单页面应用,在文章列表这一个页面每一条文章都会拥有三个操作,“预览”, “编辑”, “删除”,在PC端,他们看上去是这个样子的:

而在手机端展示的时候,就会显得很“挤”:

mobile.png

如果右侧的按钮改为左划呼出,那样的话,按钮区域就不会占用文字显示的空间,看上去也较为美观,先上成果图:

想要达到这样的效果,就要了解一下HTML DOM 事件对象中的touch事件。

我们先创建一个<div></div>,然后将event对象传进touchstart()这个函数,ontouchstart是一个DOM事件,当按下手指时,执行后面的函数:

<div ontouchstart="touchstart(event)" id="demo">
    touch me!
</div>

<script>
    function touchstart(e) {
        console.log(e.touches[0]);   
        //touches是当前位于屏幕上的所有手指的一个列表
    }
</script>

打印出来的结果为一个对象:

属性
clientX 141
clientY 60
force 1
identifier 0
pageX 141
pageY 60
radiusX 11.5
radiusY 11.5
rotationAngle: 0
screenX 548
screenY 198
target div#demo

其中一些属性的意义如下:

  • clientX:触摸目标在视口中的x坐标
  • clientY:触摸目标在视口中的y坐标
  • identifier:标识触摸的唯一ID
  • pageX:触摸目标在页面中的x坐标
  • pageY:触摸目标在页面中的y坐标
  • screenX:触摸目标在屏幕中的x坐标
  • screenY:触摸目标在屏幕中的y坐标
  • target:触摸的DOM节点目标

那么我们最需要的就是触摸目标在视口中的x坐标与y坐标,另外一些关于触摸的事件有:

  • 移动手指时,触发ontouchmove
  • 移走手指时,触发ontouchend

那么我们便可以监听这三个DOM事件,做一些有趣的操作~

做之前我们先加一点样式:

#demo {
    position: absolute;
    width: 256px;
    height: 256px;
    color: #7d8d9b;
    background-color: #eef3f8;
    border-radius: 3px;
    text-align: center;
    line-height: 256px;
}

监听三个事件,并且添加一条内联样式,便于我们在js中获取。

<div ontouchstart="touchstart(event)" 
     ontouchmove="touchmove(event)" 
     ontouchend="touchend(event)" 
     id="demo"
     style="top: 15px; left: 15px"> //内联样式
    touch me!
</div>

接着我们通过js来实现用手指拖动这个<div></div>的效果。在开始之前,我们需要定义一些变量记录手指按下瞬间,触摸点在视口的位置,以及拖动的过程中,触摸点在视口的位置,还要记录下拖动前元素的left以及top属性的值,以便在拖动元素时,计算出两个方向的偏移量。

var $demo = document.getElementById('demo');
var start_x,
    start_y,
    start_left,
    start_top,
    current_x,
    current_Y;

touchstart()函数中,只需记录下相应触摸点的信息:

function touchstart(e) {
    start_x = e.touches[0].clientX;
    start_y = e.touches[0].clientY;
    start_left = parseFloat($demo.style.left.replace('px', ''));
    start_top = parseFloat($demo.style.top.replace('px', ''));
}

在移动过程中我们获取移动过程中的触摸位置,并计算出偏移量,然后重新设置元素的left以及top属性的值,及触摸前的lefttop + 移动过程中的偏移量,也就是start_left/top + change_x/y

function touchmove(e) {
    current_x = e.touches[0].clientX;
    current_y = e.touches[0].clientY;
    var change_x = current_x - start_x;
    var change_y = current_y - start_y;
    $demo.style.left = start_left + change_x + 'px';
    $demo.style.top = start_top + change_y + 'px';
}

function touchend(e) {
    $demo.innerHTML = '别摸我!';
}

成果图:

简单的通过控制DOM元素的left以及top样式就可以实现这个有趣的效果!那么回到开头的命题,有了这些基础我们怎样完成左划呼出菜单的设想呢?上面这个demo已经实现了元素的拖动,我们只需要x方向的拖动,并且在手指离开的瞬间需要对元素移动距离进行判断,符合一定条件就可以呼出控制区域。首先先写好我们的html模板:

css样式:

* {
    box-sizing: border-box;
}

#slide-wrap {
    border-top: 1px solid rgba(0, 0, 0, 0.07);
    border-bottom: 1px solid rgba(0, 0, 0, 0.07);
    overflow: hidden;
}

#slide-item {
    position: relative; /* 重要 */
    display: flex;
    width: 100%;
}

.info {
    flex: 0 0 100%;
    font-size: 30px;
    font-weight: 300;
    padding: 30px 15px;
}

.btns {
    display: flex;
    flex: 0 0 211px;
}

.separation {
    width: 1px;
    height: 100%;
    background-color: #e1e7ec;
}

.del {
    color: #ffe2e2;
    background-color: #e66565;
}

button {
    flex: 1;
    border: none;
    color: #636569;
    background-color: #eef3f8;
    outline: none;
}

html模板:

<div id="slide-wrap">
    <div id="slide-item">
        <div class="info">我是孤独的根号三</div>
        <div class="btns">
            <button>预览</button>
            <span class="separation"></span>
            <button>编辑</button>
            <button class="del">删除</button>
        </div>
    </div>
</div>

关于.info样式的设定,有必要补充一点说明,flex属性是flex-growflex-shrinkflex-basis 属性的简写属性,而我们设置的flex-shrink为0,也就是元素不会收缩他的宽度,会按照flex-basis中设置的宽度来显示,所以三个按钮就会被“顶”到右侧。

.info {
    flex: 0 0 100%; /* flex-grow|flex-shrink|flex-basis */
    font-size: 30px;
    font-weight: 300;
    padding: 30px 15px;
}

而外层元素#slide-wrapoverflow设为了hidden,所以按钮部分就会被修剪掉。

#slide-wrap {
    border-top: 1px solid rgba(0, 0, 0, 0.07);
    border-bottom: 1px solid rgba(0, 0, 0, 0.07);
    overflow: hidden;
}

如果我们将.info中的flex-shrink设置为1,也就是空间不足的时候会自动收缩,我们就能够看到三个按钮被显示出来了。

当然我们不需要这样的显示方式,如果我们要以滑动的方式显示按钮,就需要改变#slide-itemleft值,当我们将其设置为-211px时:

<div id="slide-item" style="left: -211px">
......
</div>

右侧按钮被显示了,那我们只需要让js代替我们完成这个过程!

监听触摸事件:

<div class="info"
     ontouchstart="touchstart(event)" 
     ontouchmove="touchmove(event)" 
     ontouchend="touchend(event)">我是孤独的根号三</div>

操作样式:

参照上一个demo的例子,将y方向移动的部分删去:

var $ele = document.getElementById('slide-item');
var start_x,
    start_left,
    current_x;

function touchstart(e) {
    start_x = e.touches[0].clientX;
    start_left = parseFloat($ele.style.left.replace('px', ''));
}

function touchmove(e) {
    current_x = e.touches[0].clientX;
    var change_x = current_x - start_x;
    $ele.style.left = start_left + change_x + 'px';
}

但是当手指离开时,元素并没有回到它该去的地方,所以在touchend()函数中我们要加以判断:

function touchend() {
    if (change_x < -70) {
        $ele.style.left = '-211px';
    } else {
        $ele.style.left = '0px';
    }
}

动画效果太僵硬了,添加一点过度,最终的js代码为:

var $ele = document.getElementById('slide-item');
var start_x,
    start_left,
    current_x,
    change_x;

function touchstart(e) {
    $ele.style.transition = 'left 0ms';
    start_x = e.touches[0].clientX;
    start_left = parseFloat($ele.style.left.replace('px', ''));
}

function touchmove(e) {
    current_x = e.touches[0].clientX;
    change_x = current_x - start_x;
    $ele.style.left = start_left + change_x + 'px';
}

function touchend(e) {
    $ele.style.transition = 'left 300ms';
    if (change_x < -70) {
        $ele.style.left = '-211px';
    } else {
        $ele.style.left = '0px';
    }
}

最终的效果为:

效果还不错哦(●'◡'●),当然还有许多细节可以优化,大家也可以给我指出欠妥的地方~


CodePen效果:在线demo(如果是PC端,在浏览器调试工具中模拟手机触屏才会有效果)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容

  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,454评论 1 45
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,744评论 1 92
  • 一组 李同学 Vuex的基本使用? demo https://codepen.io/mzrx/pen/ZjRBrz...
    达魔学院阅读 514评论 0 1
  • 吉安娜和阿尔萨斯的爱情让我们唏嘘了数十年,伊利丹和影歌的爱恨纠缠持续了一万年,萨格拉斯的野心弥漫在艾泽拉斯的亘古时...
    65游戏阅读 242评论 0 0
  • 都说毕业遥遥无期,可转眼大家都已各奔东西,山南水北,想要再一次聚起才是无期。 当学校毕业念歌响起时...
    青春不浪漫阅读 310评论 0 2