手势魅力-设置一个触摸菜单
虽互不曾谋面,但希望能和你成为笔尖下的朋友
以读书,技术,生活为主,偶尔撒点鸡汤
不作,不敷衍,意在真诚吐露,用心分享
点击右上方,可关注本刊
(后台回复该标题,可下载源码,阅读文字就好,口头表达能力确实着急)
序言
本篇为一移动端博文,个人觉得这篇外文还可以,就翻译了一下,最终实现的一个效果是:用手势创建一个本地菜单(点击一菜单按钮,实现设置一个触摸侧滑,滑动滑出效果,如下文中的gif图所示),主要涉及的知识点有移动端三大触摸事件(touchstart,touchmove,touchend),触摸属性,以及实现侧边栏动画,在处理移动端点击,拖动,滑动时,是不得要考虑用户的触摸手势,判断手指在页面上到底是点击还是滑动的,利用原生js的方法封装点击,移动,抬起功能函数,尽管移动(手机)端与pc端有很多相似之处,但还是有很多要注意的地方的,如果你想获得该Demo的源码,复制该标题后台回复[手势魅力-设置一个触摸菜单]就可以了的,初次翻译,如果有误导的地方,欢迎路过的老师,多提意见和指正,如果你想阅读英文原文,扫文末下方二维码或者跳转到指定链接就可以了的
正文从这里开始~
最终代码实现效果图所示:
前戏
触摸和手势驱动设备的兴起,极大地改变了我们思考交互的方式。手势不仅仅是娱乐性的,它们非常有用,也很熟悉
移动触摸手势已成为每个应用程序的重要组成部分,大多数用户甚至没有意识到的一部分。谁不喜欢(流畅)的互动应用程序?
然而至今,憎恨可能伴随的混乱和数学是非常容易的。我知道,令人震惊的是,尤其是当你不是第一次码代码的人,或者你只是在那里维护它的时候
有时候,这可能是一个吃力不讨好的工作。那种让你用一只手盯着屏幕,另一只手放在你的额头上,另一只手放在鼠标上滚动的时间 有 - 我敢说呢? - 如丝般流畅的手势触摸手势和动画可能是一个挑战,并随着时间的推移变得越来越突出。但这是另一天的战斗。或另一篇文章。或两者
今天,我们要告诉你如何用手势创建一个本地菜单
让羊驼走上舞台! 所以,在我转向实际的代码之前,在那里有一些我想要经历的事情,所以请耐心等待
HTML结构
<!-- layout开始 -->
<div class="layout">
<!-- 头部开始 -->
<div class="header">
<div class="header-top">
<!-- 菜单按钮 -->
<div data-link="" class="app-menu-burger OSFillParent" href="#" id="b2-Menu">
<!-- 三条横岗线,这里其实完全用一小图片或者icon字体图标代替 -->
<div data-container="" class="app-menu-line"></div>
<div data-container="" class="app-menu-line OSAutoMarginTop"></div>
<div data-container="" class="app-menu-line OSAutoMarginTop"></div>
</div>
</div>
</div>
<!-- 头部结束 -->
<!-- 中间部分开始 -->
<div data-container="" class="center-content">
<!-- 中间图像小图片 -->
<div class="center-content-header ph" id="b3-Top"></div>
<div class="center-content-container ph" id="b3-Center">
<!--中间图片 -->
<div data-container="">
<div data-container="" style="text-align: center;"><img data-image="" src="https://s11.postimg.org/409mhl043/icon.png">
</div>
<div data-container="" class="OSAutoMarginTop" style="text-align: center;">
<h1>Welcome to itclanCoder!<br></h1>
<h3>手势魅力:设置一个触摸侧滑菜单<br>阅读原文即可</h3>
</div>
</div>
</div>
<div class="center-content-bottom ph" id="b3-Bottom"></div>
</div>
<!-- 中间部分结束 -->
<!-- 左侧带单栏开始 -->
<div class="menu">
<!-- 黑色遮罩 -->
<div class="menu-background" style="cursor: pointer;"></div>
<div class="app-menu-container">
<div class="app-menu">
<!-- 侧栏顶部开始 -->
<div class="top">
<!-- 侧栏顶部图片 -->
<div class="top-overlay">
<img src="https://s11.postimg.org/409mhl043/icon.png" width=80>
</div>
</div>
<!-- 侧栏顶部结束 -->
<!-- 侧栏列表内容开始 -->
<div class="bottom">
<!-- 欢迎关注微信itclanCoder公众号,个人微信号:suibichuanji -->
<div class="list-item">Welcome itclanCoder</div>
<div class="list-item">Fancy Time</div>
<div class="list-item">Tea Time</div>
<div class="list-item">Adventure Time</div>
<div class="list-item">Puzzle Time</div>
<div class="list-item">Sports Time</div>
<div class="list-item">Star Wars Time</div>
<div class="list-item">Internet Time</div>
<div class="list-item">Sushi Time</div>
<div class="list-item">weChatPublicId:itclanCoder</div>
<div class="list-item">personId:suibichuanji</div>
</div>
<!-- 侧栏列表内容 -->
</div>
</div>
</div>
<!-- 左侧菜单栏结束 -->
</div>
<!-- layout 结束 -->
由于css有点多,这里就不贴代码了的,本文着重在于l理解js,但这并不代表css就不重要,只是这里权重没那么大,相信对于css还是较为容易看懂的,如果你想获得该Demo的源码,复制该标题后台回复[手势魅力-设置一个触摸菜单]就可以了的,我对js,css中的代码也做了一些简要的注释,其实看到命名,j在结合文章内容,就应该很容易理解各个变量是什么意思了
所有你需要了解的JavaScript触摸事件
我将使用JavaScript事件来检测我的移动触摸手势。在这种情况下在那里是:
touchstart
:当你触摸DOM元素时触发touchmove
:当你沿着DOM元素拖动手指时触发touchend
:当你从DOM元素中移除手指时触发
在这些事件中,我将使用触摸属性(在那里还有两个属性,但这是我现在关心的)。触摸属性列出当前在屏幕上的所有手指:
PageX
:返回手指放置在DOM中的x坐标。从左边开始计算,如果适用,则考虑水平滚动PageY
:返回手指放置在DOM中的y坐标。它是从顶部边缘测量的,并考虑垂直滚动(如果适用)
而你也需要知道关于requestAnimationFrame
requestAnimationFrame
函数告诉浏览器你要执行一个动画。它要求浏览器调用指定的函数,在下一次重绘之前更新动画。这有什么好处呢
浏览器将尝试匹配显示刷新,以允许流畅的动画
非活动选项卡中的动画将停止(在CPU上花费的更少)
它不会耗尽你的电池寿命
拖动,点击和滑动:额外的东西要考虑移动触摸手势
这些事件需要能够检测和区分拖拽,点击和移动,并相应地做不同的事情。所以,当你玩手机触摸手势,想想:
限制:你想要什么元素停止?您希望它在每次拖动时移动多远?
这个手势的方向:你想只能水平移动,或者还是垂直移动?也许是两个?
拖动完成后你想要发生什么?它会回到开始还是结束,取决于它在哪里结束?它是否考虑到速度?
详情:我们是否正在用这个手势记住速度?你想在菜单后面加一个遮罩,当你打开它时会变得越来越暗吗?
在我的情况下,我只希望手势的方向是水平的,因为我希望滚动功能正常。我有限制,并且我希望它回到开始或结束。这取决于用户拖动了多少以及手指在屏幕上的速度
你不知道你想知道的关于 - 是超级重要的部分
我知道你想要了解移动触摸手势的有趣部分,但是我必须先介绍这一点,因为它会影响到你的代码。是的,现在是讨论变量的时候了。这好消息是,我也要解释为什么要设置它们的价值。这些功能将使代码看起来更清洁
全局变量和设置默认值
啊,是如此的好玩!看看所需要的变量数量;正是大多数人倾向于跳过的东西。(不要,你会后悔的)
var trackableElement; // 可追踪元素
var menu = document.querySelector(".menu");// querSelector是h5新增查找元素方法,返回匹配指定 CSS 选择器元素的第一个子元素,侧边栏菜单元素
var appMenu = document.querySelector(".app-menu-container");// 左侧边栏app-menu-container应用程序菜单容器
var overlay = document.querySelector(".menu-background"); // 左侧菜单栏黑色背景遮罩,覆盖
var burger = document.querySelector(".app-menu-burger"); // 三条横岗菜单按钮
var touchingElement = false; //触摸元素,开关
var startTime; // 开始时间
var startX = 0,
startY = 0;
var currentX = 0,
currentY = 0;
var isOpen = false;
var isMoving = false;
var menuWidth = 0;
var lastX = 0;
var lastY = 0;
var moveX = 0; // 屏幕上的哪个位置是当前的菜单where in the screen is the menu currently
var dragDirection = ""; // 拖拽方向
var maxOpacity = 0.5;
// 最大透明度,如果你想改变这个,不要忘记改变CSS类中不透明的值if you want to change this, don’t forget to change the opacity value
// of the ‘.menu--visible .menu-background’ CSS class
var init = function(element, start, move, end) {
trackableElement = element;
startTime = new Date().getTime(); // 开始时间的触摸start time of the touch
addEventListeners(); // 元素触发,函数事件的调用
}
var addEventListeners = function() {
trackableElement.addEventListener("touchstart", onTouchStart, false);
trackableElement.addEventListener("touchmove", onTouchMove, false);
trackableElement.addEventListener("touchend", onTouchEnd, false);
overlay.addEventListener("click", closeMenuOverlay, false);
// 我希望能够点击覆盖,并立即关闭菜单(在实际的菜单和它后面的页面之间的空间I want to be able to click the overlay and immediately close the menu
// 点击三缸线按钮,并立即打开菜单 open the menu(in the space between the actual menu and the page behind it)
}
非常简单,真的。按照这个顺序,代码不那么混乱,不那么可怕,而且更容易消化
函数中的函数
这些函数被 EventListener
调用,即使它们不是做实际的动画或者使菜单工作所必需的计算
// onTouchStart手指按下功能函数
function onTouchStart(evt) {
startTime = new Date().getTime(); // 开始时间
startX = evt.touches[0].pageX; // 手指点下距离x轴的坐标
startY = evt.touches[0].pageY; // 手指点下距离y轴的坐标
touchingElement = true;
touchStart(startX, startY); // touchStart功能函数调用,并设置两个实际参数startX,startY
}
// 手指移动函数
function onTouchMove(evt) {
if (!touchingElement)
return;
currentX = evt.touches[0].pageX;
currentY = evt.touches[0].pageY;
const translateX = currentX - startX; // distance moved in the x axis
const translateY = currentY - startY; // distance moved in the y axis
touchMove(evt, currentX, currentY, translateX, translateY); // touchStart功能函数调用,并设置两个实际参数startX,startY
}
// 手指抬起功能函数
function onTouchEnd(evt) {
if (!touchingElement)
return;
touchingElement = false;
const translateX = currentX - startX; // 距离在x轴上移动distance moved in the x axis
const translateY = currentY - startY; // 距离在y轴上移动distance moved in the y axis
const timeTaken = (new Date().getTime() - startTime); // 时间戳
}
所有这些变量都用于动画所涉及的数学运算。为了可读性,在函数中没有太多的代码行,我把它们全部分成了小的一行
这个手机触摸手势最后有趣的一部分
现在我对触摸事件,变量和函数的解释已经不存在了,现在是我关注如何创建动画的时候了。这正是菜单移动以及所有数学和算法背后的原因 动画开始
// 兼容性写法,手指抬起
function touchStart(startX, startY) {
var menuOpen = document.querySelector(".menu.menu--visible");
if (menuOpen !== null) {
isOpen = true;
} else {
isOpen = false;
}
menu.classList.add("no-transition");
appMenu.classList.add("no-transition");
isMoving = true;
menuWidth = document.querySelector(".app-menu").offsetWidth;
lastX = startX;
lastY = startY;
if (isOpen) {
moveX = 0;
} else {
moveX = -menuWidth;
}
dragDirection = "";
menu.classList.add("menu--background-visible");
// why is this being added? ‘.menu--background-visible .menu-background’ makes the overlay
// ‘active’, displaying it on the DOM for those sweet opacity changes.
}
每次触摸屏幕时,这些代码都会运行。此功能将用作重置为默认值,具体取决于你上次提起手指后菜单发生了什么
动画中间
function touchMove(evt, currentX, currentY, translateX, translateY) {
if (!dragDirection) {
if (Math.abs(translateX) >= Math.abs(translateY)) {
dragDirection = "horizontal";
} else {
dragDirection = "vertical";
}
requestAnimationFrame(updateUi);
// this is what actually does the animation
}
// ...
}
你想知道的第一件事是手势的方向
在菜单中,垂直滚动真的不是什么可以关心的东西。意思是,在与手势相关的代码方面,行为本身应该是默认滚动。因此,确定当什么时候这是需要的
if (dragDirection === "vertical") {
lastX = currentX;
lastY = currentY;
} else {
evt.preventDefault();
// ...
}
没有 preventDefault
,这是会发生什么事情:
这绝对不是你想要用你的手机触摸手势发生的事情,所以考虑一下:当你打开/关闭菜单时,你是否有兴趣阅读滚动隐藏的内容?如果你的拖拽方向是水平的,你就不能滚动
我们需要一些边界在这里! (设置限制)
if (moveX + (currentX - lastX) < 0 && moveX + (currentX - lastX) > -menuWidth) {
moveX = moveX + (currentX - lastX);
// ...
}
所以,记得我说我有限制吗?在这个例子中,菜单隐藏在屏幕的左边。所以,如果菜单是关闭的,变量 moveX
开始为 -menuWidth
- 我希望它被拖动到右边,直到完全显示
moveX + (currentX - lastX)
你可以称之为移动间隔。这就是告诉脚本菜单在窗口中的确切位置。我使用 moveX
是因为我做了实际的动画。转到 updateUI
函数 - ` requestAnimationFrame
调用的函数 - 这就是你所拥有的
function updateUi() {
if (isMoving) {
var element = document.querySelector(".app-menu-container");
element.style.transform = "translateX(" + moveX + "px)";
element.style.webkitTransform = "translateX(" + moveX + "px)";
requestAnimationFrame(updateUi);
}
}
我希望动画无缝平滑。为此,脚本可以检测到并用于 translateX
的时间间隔越小越好。目标不是看到使用 translateX
引起的跳转
现在已经完成了,下一步就是计算叠加层的淡入效果
重叠计算
目标是:
当moveX = -menuWidth时,不透明度= 0
当movX = 0,不透明度= 0.5
然而,这些计算并不那么线性。问题始终是打破这些情况下通常使用的三路规则的零
overlay.classList.add("no-transition");
var percentageBeforeDif = (Math.abs(moveX) * 100) / menuWidth;
var percentage = 100 - percentageBeforeDif;
在这里,我确保 menuWidth
对应于100%,当前位置(moveX)对应于百分比。在这个计算中我追求的百分比是
var newOpacity = (((maxOpacity) * percentage) / 100);
这个计算是需要的,因为不透明度只有在0到0.5之间(如在变量中定义的)之后才有效。如果0.5不透明度与100%相关,则百分比将是期望的不透明度
动画结束
function touchEnd(currentX, currentY, translateX, translateY, timeTaken) {
isMoving = false;
var velocity = 0.3;
//...
}
首先要记住的是,有人可以简单地点击,事件认为这是一个摸索和touchend。如果这是一个点击,菜单上没有任何事情发生
if (translateX === 0 && translateY === 0) {
if (isOpen) {
appMenu.classList.remove("no-transition");
menu.classList.remove("no-transition");
} else {
menu.classList.remove("menu--background-visible");
menu.classList.remove("no-transition");
}
}
拖动结束后会发生什么?
当菜单打开时,它可以关闭或保持打开状态 - 与动画一起 - 返回之前的位置
如果它关闭了,那么它可以打开或者保持关闭状态,也可以在动画返回之前
if ((translateX < (-menuWidth) / 2) || (Math.abs(translateX) / timeTaken > velocity)) {
// if menu is open, this represents the close condition
if (translateX > menuWidth / 2 || (Math.abs(translateX) / timeTaken > velocity)) {
// if menu is closed, this represents the open condition
那么什么被认为足以打开菜单?五个像素移动?那么这个菜单可以根据距离打开或关闭。也就是说,如果将其拖过宽度的中间,并且拖动的速度大于定义的速度(也就是若手指拖动侧边栏超过该菜单栏本身宽度的一半位置的话,或者拖动的速度大于刚开始定义的速度,则该侧边栏就关闭或者打开的,若不是,则恢复初始前一个位置的)
就这样,你有一个工作的触摸式菜单!
总结
对本文进行总结一下,首先这个效果在我们平日的手机应用里,非常的常见,实现这一效果,主要利用的是移动端三大事件touchstart,touchmove,touchend,以及它们的触摸属性,也就是手指在屏幕DOM中的实际位置,这时,需要考虑手指是水平滑动还是垂直,甚至有时候还得考虑手倾斜的滑动,还要区分是一根手指滑动,还是多根手指滑动,侧边菜单栏动画的实现,以及要注意阻止默认事件,重叠计算等等一些细节
看似简单的效果,整个过程实现起来,还是不容易的,当然很多时候,在平时中,想当然的会用一些框架,移动端库来代替原生当中一些繁琐的写法的,原生js固然耐人耗脑,其实甭管咋实现,只要能实现就好,最后在重复一遍,若想获得本篇Demo源码,后台复制该标题回复[手势魅力-设置一个触摸菜单]就可以了的,本人对移动端也只知甚少,文若有误导的地方,请各路大佬多多指正
以下是本篇提点概要
HTML结构
所有你需要了解的JavaScript触摸事件(touchstart,touchmove,touchend),以及两个触摸属性pageX,pageY
需要知道关于requestAnimationFrame
拖动,点击和滑动:额外的东西要考虑移动触摸手势(手势方向,水平,垂直,还有手指根数)
你不知道你想知道的关于 - 是超级重要的部分
全局变量和设置默认值(一些初始化值变量的设置)
函数中的函数(手指按下,移动,抬起功能函数的封装调用)
这个手机触摸手势最后有趣的一部分(创建动画)
动画中间(手指移动,拖动菜单过程)
我们需要一些边界在这里!(设置限制),也就是侧边栏菜单滑动的位置
重叠计算(透明度变化,也就是用小数来计算,百分比值)
动画结束(菜单栏打开和关闭状态,菜单栏的位置)
以下是本文陌生词汇(仅供参考)
1. Gestures Glamour: 手势魅力 2. dramatically:显着,极大的
3. interaction:交互,相互作用 4.detect:检测
5. scenario:脚本 6. Triggered:触发
7. coordinate:坐标 8. perform:执行,演出,表演
9. repaint:重绘 10. smooth:流畅的,滑动
11. inactive:迟钝,非活动 12. battery:电池
13. distinguish:区分 14. straightforward:直截了当
15. cluttered:凌乱 16. digest:消化
17. calculations:计算 18. readability:可读性
19. involved:参与,用于 20. separated:分离
21. algorithms:算法 22. identifying:识别,确定
23. seamless:无缝 24. corresponds:对应
25. percentage:百分比 26. velocity:速度
扫码可阅读英文原文或打开底下链接
(https://medium.com/outsystems-experts/gestures-glamour-setting-up-a-touch-menu-6d9b94039997)
私人微信号:suibichuanji
注意添加时:要记得备注下,地区-昵称-身份呀,例如:上海-王尼玛-机器学习,让我知道于茫茫人海中你是那个谁哈,扔出去的漂流瓶都能找到幸福的归宿,缘分这种事,谁说的定呢,一定要永远相信美好的事情,即将要发生,我们不都是从陌生到熟悉的过程嘛,非诚勿扰,哈哈哈哈哈哈...
手势魅力-设置一个触摸菜单相关推荐
- canvas实现H5手势密码设置以及手势解锁
前言 这段时间遇到了一个需求,'我的' 页面有一个快捷登录,用户可以设置手势密码.指纹.以及面部,这篇文章主要是说说手势密码,以前也没有接触过,真是人都麻了,上网查到了一些案例,但是终归和自己需求有所 ...
- iphone xr xs_如何在iPhone XR上设置触觉触摸
iphone xr xs The iPhone XR does not have 3D Touch, instead relying on something called Haptic Touch. ...
- Mac使用技巧总结-如何独立设置Mac触摸板方向和鼠标滚轮方向?
Mac使用技巧总结 如何独立设置Mac触摸板方向和鼠标滚轮方向? 苹果Macbook的使用者都知道,Mac自带的触控板非常好用,不仅支持多手势操控,而且手感极佳,使用流畅. 但是如果对鼠标的焦距有高有 ...
- vue 递归组件多级_Vue 递归组件构建一个树形菜单
原标题:Vue 递归组件构建一个树形菜单 Vue.js 中的递归组件是一个可以调用自己的组件例如: Vue.component('recursive-component', { template: ` ...
- Altium Designer,设置一个器件或一个区域不做电气规则检查
Altium Designer,设置一个器件或一个区域不做电气规则检查.方法如下 一.菜单说明 Design→Rooms→Place Rectangular Room:放置四方形的区域Design→R ...
- Win7系统便签怎样设置一个闹钟提醒
Win7系统便签怎么设置一个闹钟提醒呢?在便签上设置提醒事项,还可以确定任务完成时间,让每一项任务都在规定时间内完成,杜绝拖延快速完成工作.想要在便签上设置提醒,首先要有一款会提醒的便签,还要了解在该 ...
- 手机怎样设置一个高考倒计时便签,可以天天看见那种
现在距离2022年高考还有不到100天的时间,为了加强对高考的重要,一些老师或学生想要在手机上设置一个高考倒计时提醒事项,可以天天看见的那种.那手机怎么设置一个高考倒计时便签呢?其实,在手机上使用云便 ...
- IDEA相关配置(特别完整)看完此篇就将所有的IDEA的相关配置都配置好了、设置鼠标滚轮修改字体大小、设置鼠标悬浮提示、设置主题、设置窗体及菜单的字体及字体大小、设置编辑区主题、通过插件更换主题
文章目录 1.创建模块(Module) 2.常用配置 2.1Appearance & Behavior 2.1.1设置主题 2.1.2设置窗体及菜单的字体及字体大小 (可忽略) 2.1.3补充 ...
- 如何在 Debian 11 上设置一个静态 IP 地址
当你在电脑上安装一个新的操作系统时,DHCP服务器会给你分配一个动态IP地址.然而,在各种情况下,你可能需要在你的机器上设置一个静态IP地址,例如,当你正在托管一个网络服务器,或者任何服务需要一个IP ...
最新文章
- 深入理解TCP/IP协议族
- ASP.NET的Page.IsPostBack 属性详细说明(转)
- linux华为路由器模拟器,华为路由器模拟器与实验内容.doc
- vba mysql连接字符串_分享一个VBA连接mysql数据库的方法
- 盘点中兴通讯强悍的战斗力
- 【MyBatis框架】Mybatis开发dao方法第一部分
- 工作笔记-新系统安装deb文件失败
- IIS服务在启动默认网站(停止)时显示发生意外错误0x8ffe2740,
- python中hub_PyHubWeekly | 第一期:Github上那些值得推荐的Python小工具
- niginx之虚拟主机和域名解析
- windows api学习笔记-键盘钩子
- Android项目实战(二十):浅谈ListView悬浮头部展现效果
- 从公司买火车票到代理模式和适配器模式
- 计算机应用基础上机实验报告怎么写,excel实验报告模板
- python中if in是什么意思_python中的in是什么意思
- 2018.8.18梦中的凶杀案
- https://github.com/lin-xin/vue-manage-system
- 小红书数据监测,品牌方必看的笔记投放技巧!
- 30个Vim常用命令
- Google Earth Engine(GEE)——快速建立一个10km的格网